<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Nicolas Gry</title><link href="https://grybouilli.github.io/" rel="alternate"/><link href="https://grybouilli.github.io/feeds/all.atom.xml" rel="self"/><id>https://grybouilli.github.io/</id><updated>2026-09-07T00:00:00+02:00</updated><subtitle>PhD Student in Computer Science · Inria de Lyon</subtitle><entry><title>Image Viewer</title><link href="https://grybouilli.github.io/extra/" rel="alternate"/><published>2026-09-07T00:00:00+02:00</published><updated>2026-09-07T00:00:00+02:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2026-09-07:/extra/</id><summary type="html">

    &lt;div id="container"&gt;

        &lt;div id="left"&gt;

            &lt;div id="controls"&gt;
                Width:
                &lt;input id="width" type="number" value="47" min="1"&gt;

                Height:
                &lt;input id="height" type="number" value="32" min="1"&gt;

                &lt;label&gt;
                    &lt;input id="rgbMode" type="checkbox" checked&gt;
                    RGB
                &lt;/label&gt;
            &lt;/div&gt;

            &lt;textarea id="data"&gt;
(255,0,0)(255,125,0)(255,255,0)(30,220,30)(0,70,200)(160,60,220)
&lt;/textarea&gt;

        &lt;/div&gt;

        &lt;div id="right"&gt;
            &lt;canvas id="canvas"&gt;&lt;/canvas&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;script&gt;

        const textarea = document.getElementById("data");
        const widthBox = document.getElementById("width");
        const heightBox = document.getElementById("height");
        const rgbMode = document.getElementById("rgbMode");

        const canvas = document.getElementById("canvas");
        const ctx = canvas.getContext …&lt;/script&gt;</summary><content type="html">

    &lt;div id="container"&gt;

        &lt;div id="left"&gt;

            &lt;div id="controls"&gt;
                Width:
                &lt;input id="width" type="number" value="47" min="1"&gt;

                Height:
                &lt;input id="height" type="number" value="32" min="1"&gt;

                &lt;label&gt;
                    &lt;input id="rgbMode" type="checkbox" checked&gt;
                    RGB
                &lt;/label&gt;
            &lt;/div&gt;

            &lt;textarea id="data"&gt;
(255,0,0)(255,125,0)(255,255,0)(30,220,30)(0,70,200)(160,60,220)
&lt;/textarea&gt;

        &lt;/div&gt;

        &lt;div id="right"&gt;
            &lt;canvas id="canvas"&gt;&lt;/canvas&gt;
        &lt;/div&gt;

    &lt;/div&gt;

    &lt;script&gt;

        const textarea = document.getElementById("data");
        const widthBox = document.getElementById("width");
        const heightBox = document.getElementById("height");
        const rgbMode = document.getElementById("rgbMode");

        const canvas = document.getElementById("canvas");
        const ctx = canvas.getContext("2d");

        function clamp(v) {
            return Math.max(0, Math.min(255, v | 0));
        }

        function render() {
            const w = parseInt(widthBox.value);
            const h = parseInt(heightBox.value);

            if (!w || !h)
                return;

            let nums;

            if (rgbMode.checked) {
                // Matches "(r,g,b)", "(r, g, b)", etc.
                nums = [];
                const regex = /\(\s*(-?\d+)\s*,\s*(-?\d+)\s*,\s*(-?\d+)\s*\)/g;

                let m;
                while ((m = regex.exec(textarea.value)) !== null) {
                    nums.push(
                        clamp(Number(m[1])),
                        clamp(Number(m[2])),
                        clamp(Number(m[3]))
                    );
                }
            }
            else {
                nums = textarea.value
                    .trim()
                    .split(/\s+/)
                    .filter(Boolean)
                    .map(Number);
            }

            canvas.width = w;
            canvas.height = h;

            const img = ctx.createImageData(w, h);

            if (rgbMode.checked) {
                const pixels = Math.min(w * h, Math.floor(nums.length / 3));

                for (let i = 0; i &lt; pixels; i++) {
                    img.data[4 * i + 0] = clamp(nums[3 * i + 0]);
                    img.data[4 * i + 1] = clamp(nums[3 * i + 1]);
                    img.data[4 * i + 2] = clamp(nums[3 * i + 2]);
                    img.data[4 * i + 3] = 255;
                }
            }
            else {
                const pixels = Math.min(w * h, nums.length);

                for (let i = 0; i &lt; pixels; i++) {
                    const v = clamp(nums[i]);

                    img.data[4 * i + 0] = v;
                    img.data[4 * i + 1] = v;
                    img.data[4 * i + 2] = v;
                    img.data[4 * i + 3] = 255;
                }
            }

            ctx.putImageData(img, 0, 0);

            const scale = Math.max(
                1,
                Math.floor(
                    Math.min(
                        (window.innerWidth * 0.55) / w,
                        (window.innerHeight * 0.9) / h
                    )
                )
            );

            canvas.style.width = (w * scale) + "px";
            canvas.style.height = (h * scale) + "px";
        }

        textarea.addEventListener("input", render);
        widthBox.addEventListener("input", render);
        heightBox.addEventListener("input", render);
        rgbMode.addEventListener("change", render);
        window.addEventListener("resize", render);

        render();

    &lt;/script&gt;

</content><category term="extra"/></entry><entry><title>Image ↔ XLSX Converter</title><link href="https://grybouilli.github.io/extra/" rel="alternate"/><published>2026-09-07T00:00:00+02:00</published><updated>2026-09-07T00:00:00+02:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2026-09-07:/extra/</id><summary type="html">

    &lt;div class="container"&gt;


        &lt;h1&gt;Image ↔ XLSX Converter&lt;/h1&gt;

        &lt;!-- Conversion mode --&gt;
        &lt;div class="mode"&gt;

            &lt;label&gt;
                &lt;input type="radio" name="mode" value="toxlsx" checked&gt;
                From image to XLSX
            &lt;/label&gt;

            &lt;label&gt;
                &lt;input type="radio" name="mode" value="toimage"&gt;
                From XLSX to image
            &lt;/label&gt;

        &lt;/div&gt;

        &lt;!-- Input --&gt;
        &lt;div class="file-row"&gt;

            &lt;input type="text" id="inputPath" placeholder="Input file..." readonly&gt;

            &lt;label class="file-button"&gt;
                Browse...
                &lt;input type="file" id="inputFile"&gt;
            &lt;/label&gt;

        &lt;/div&gt;

        &lt;!-- Output --&gt;
        &lt;div class="file-row"&gt;

            &lt;input type="text" id="outputPath" placeholder="Output file (optional)"&gt;

            &lt;button id="outputButton"&gt;
                Choose output...
            &lt;/button&gt;

        &lt;/div&gt;

        &lt;!-- Image output format --&gt;
        &lt;div class="format-row" id="imageFormatRow"&gt;

            &lt;label for="imageFormat"&gt;
                Image format:
            &lt;/label&gt;

            &lt;select id="imageFormat"&gt;
                &lt;option value="png"&gt;PNG&lt;/option&gt;
                &lt;option value="jpeg"&gt;JPEG&lt;/option&gt;
            &lt;/select&gt;

        &lt;/div&gt;

        &lt;!-- Drop zone --&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            Drop an image or XLSX file here
        &lt;/div&gt;

        &lt;!-- Execute --&gt;
        &lt;button class="execute" id="execute"&gt;
            Execute
        &lt;/button&gt;

        &lt;!-- Status --&gt;
        &lt;div class="status" id="status"&gt;&lt;/div&gt;

        &lt;!-- Preview --&gt;
        &lt;div class="preview" id="preview"&gt;
            &lt;h2&gt;Preview&lt;/h2&gt;
            &lt;img id="previewImage" alt="Image preview"&gt;
        &lt;/div&gt;


    &lt;/div&gt;

    &lt;script&gt;

        const inputFile = document.getElementById("inputFile");
        const inputPath = document.getElementById("inputPath");
        const outputPath = document.getElementById("outputPath");
        const outputButton = document.getElementById("outputButton");
        const executeButton = document …&lt;/script&gt;</summary><content type="html">

    &lt;div class="container"&gt;


        &lt;h1&gt;Image ↔ XLSX Converter&lt;/h1&gt;

        &lt;!-- Conversion mode --&gt;
        &lt;div class="mode"&gt;

            &lt;label&gt;
                &lt;input type="radio" name="mode" value="toxlsx" checked&gt;
                From image to XLSX
            &lt;/label&gt;

            &lt;label&gt;
                &lt;input type="radio" name="mode" value="toimage"&gt;
                From XLSX to image
            &lt;/label&gt;

        &lt;/div&gt;

        &lt;!-- Input --&gt;
        &lt;div class="file-row"&gt;

            &lt;input type="text" id="inputPath" placeholder="Input file..." readonly&gt;

            &lt;label class="file-button"&gt;
                Browse...
                &lt;input type="file" id="inputFile"&gt;
            &lt;/label&gt;

        &lt;/div&gt;

        &lt;!-- Output --&gt;
        &lt;div class="file-row"&gt;

            &lt;input type="text" id="outputPath" placeholder="Output file (optional)"&gt;

            &lt;button id="outputButton"&gt;
                Choose output...
            &lt;/button&gt;

        &lt;/div&gt;

        &lt;!-- Image output format --&gt;
        &lt;div class="format-row" id="imageFormatRow"&gt;

            &lt;label for="imageFormat"&gt;
                Image format:
            &lt;/label&gt;

            &lt;select id="imageFormat"&gt;
                &lt;option value="png"&gt;PNG&lt;/option&gt;
                &lt;option value="jpeg"&gt;JPEG&lt;/option&gt;
            &lt;/select&gt;

        &lt;/div&gt;

        &lt;!-- Drop zone --&gt;
        &lt;div class="drop-zone" id="dropZone"&gt;
            Drop an image or XLSX file here
        &lt;/div&gt;

        &lt;!-- Execute --&gt;
        &lt;button class="execute" id="execute"&gt;
            Execute
        &lt;/button&gt;

        &lt;!-- Status --&gt;
        &lt;div class="status" id="status"&gt;&lt;/div&gt;

        &lt;!-- Preview --&gt;
        &lt;div class="preview" id="preview"&gt;
            &lt;h2&gt;Preview&lt;/h2&gt;
            &lt;img id="previewImage" alt="Image preview"&gt;
        &lt;/div&gt;


    &lt;/div&gt;

    &lt;script&gt;

        const inputFile = document.getElementById("inputFile");
        const inputPath = document.getElementById("inputPath");
        const outputPath = document.getElementById("outputPath");
        const outputButton = document.getElementById("outputButton");
        const executeButton = document.getElementById("execute");
        const dropZone = document.getElementById("dropZone");

        const imageFormatRow = document.getElementById("imageFormatRow");
        const imageFormat = document.getElementById("imageFormat");

        const status = document.getElementById("status");

        const preview = document.getElementById("preview");
        const previewImage = document.getElementById("previewImage");

        let selectedFile = null;


        /* -------------------------------------------------
         * Utilities
         * ------------------------------------------------- */

        function getMode() {
            return document.querySelector(
                'input[name="mode"]:checked'
            ).value;
        }


        function getExtension(filename) {
            const parts = filename.split(".");
            if (parts.length &lt; 2) {
                return "";
            }

            return parts[parts.length - 1].toLowerCase();
        }


        function getBaseName(filename) {
            const lastDot = filename.lastIndexOf(".");

            if (lastDot === -1) {
                return filename;
            }

            return filename.substring(0, lastDot);
        }


        function setStatus(message, type) {
            status.textContent = message;
            status.className = "status " + type;
        }


        function clearStatus() {
            status.textContent = "";
            status.className = "status";
        }


        /* -------------------------------------------------
         * Mode switching
         * ------------------------------------------------- */

        document.querySelectorAll(
            'input[name="mode"]'
        ).forEach(radio =&gt; {

            radio.addEventListener("change", () =&gt; {

                const mode = getMode();

                imageFormatRow.style.display =
                    mode === "toimage" ? "flex" : "none";

                updateDefaultOutput();

            });

        });


        /* -------------------------------------------------
         * Input file
         * ------------------------------------------------- */

        inputFile.addEventListener("change", () =&gt; {

            if (!inputFile.files.length) {
                return;
            }

            setInputFile(inputFile.files[0]);

        });


        function setInputFile(file) {

            selectedFile = file;

            inputPath.value = file.name;

            updateDefaultOutput();

            clearStatus();

            /*
             * Preview image input.
             */
            if (getMode() === "toxlsx") {

                const extension = getExtension(file.name);

                if (
                    extension === "png" ||
                    extension === "jpg" ||
                    extension === "jpeg"
                ) {
                    const url = URL.createObjectURL(file);

                    previewImage.src = url;
                    preview.style.display = "block";
                }

            }

        }


        /* -------------------------------------------------
         * Default output name
         * ------------------------------------------------- */

        function updateDefaultOutput() {

            if (!selectedFile) {
                return;
            }

            const mode = getMode();

            const base = getBaseName(selectedFile.name);

            if (mode === "toxlsx") {
                outputPath.value = base + ".xlsx";
            }
            else {
                const format = imageFormat.value;
                outputPath.value = base + "." + format;
            }

        }


        imageFormat.addEventListener(
            "change",
            updateDefaultOutput
        );


        /* -------------------------------------------------
         * Output file picker
         *
         * Browsers cannot normally choose an arbitrary
         * output filename using a standard file dialog.
         *
         * We therefore use the browser download mechanism.
         * The text field still allows the desired filename.
         * ------------------------------------------------- */

        outputButton.addEventListener("click", () =&gt; {

            outputPath.focus();
            outputPath.select();

        });


        /* -------------------------------------------------
         * Drag &amp; drop
         * ------------------------------------------------- */

        dropZone.addEventListener("dragover", event =&gt; {

            event.preventDefault();

            dropZone.classList.add("dragover");

        });


        dropZone.addEventListener("dragleave", () =&gt; {

            dropZone.classList.remove("dragover");

        });


        dropZone.addEventListener("drop", event =&gt; {

            event.preventDefault();

            dropZone.classList.remove("dragover");

            const files = event.dataTransfer.files;

            if (!files.length) {
                return;
            }

            setInputFile(files[0]);

        });


        /* -------------------------------------------------
         * Image -&gt; XLSX
         * ------------------------------------------------- */

        async function imageToXlsx(file, filename) {

            const bitmap = await createImageBitmap(file);

            const canvas = document.createElement("canvas");

            canvas.width = bitmap.width;
            canvas.height = bitmap.height;

            const ctx = canvas.getContext("2d", {
                willReadFrequently: true
            });

            ctx.drawImage(bitmap, 0, 0);

            const imageData = ctx.getImageData(
                0,
                0,
                bitmap.width,
                bitmap.height
            );

            const pixels = imageData.data;

            const workbook = XLSX.utils.book_new();

            const channels = {
                red: [],
                green: [],
                blue: []
            };

            /*
             * Construct one row per image row.
             *
             * pixels layout:
             *
             * R G B A R G B A ...
             */

            for (let y = 0; y &lt; bitmap.height; y++) {

                const redRow = [];
                const greenRow = [];
                const blueRow = [];

                for (let x = 0; x &lt; bitmap.width; x++) {

                    const index =
                        (y * bitmap.width + x) * 4;

                    redRow.push(pixels[index]);
                    greenRow.push(pixels[index + 1]);
                    blueRow.push(pixels[index + 2]);

                }

                channels.red.push(redRow);
                channels.green.push(greenRow);
                channels.blue.push(blueRow);

            }


            /*
             * Create the three worksheets.
             */

            for (const [name, data] of Object.entries(channels)) {

                const worksheet =
                    XLSX.utils.aoa_to_sheet(data);

                XLSX.utils.book_append_sheet(
                    workbook,
                    worksheet,
                    name
                );

            }


            /*
             * Download XLSX.
             */

            XLSX.writeFile(
                workbook,
                filename || "image.xlsx"
            );

        }


        /* -------------------------------------------------
         * XLSX -&gt; Image
         * ------------------------------------------------- */

        async function xlsxToImage(file, filename) {

            const arrayBuffer = await file.arrayBuffer();

            const workbook = XLSX.read(
                arrayBuffer,
                {
                    type: "array"
                }
            );

            const requiredSheets = [
                "red",
                "green",
                "blue"
            ];

            /*
             * Check sheets.
             */

            for (const sheet of requiredSheets) {

                if (!workbook.SheetNames.includes(sheet)) {

                    throw new Error(
                        `Missing worksheet "${sheet}"`
                    );

                }

            }


            /*
             * Convert worksheets into arrays.
             */

            const channels = {};

            for (const sheetName of requiredSheets) {

                const worksheet =
                    workbook.Sheets[sheetName];

                channels[sheetName] =
                    XLSX.utils.sheet_to_json(
                        worksheet,
                        {
                            header: 1,
                            defval: 0,
                            raw: true
                        }
                    );

            }


            const red = channels.red;
            const green = channels.green;
            const blue = channels.blue;


            /*
             * Determine image dimensions.
             */

            const height = red.length;

            if (height === 0) {
                throw new Error(
                    "The red worksheet is empty."
                );
            }

            const width = red[0].length;

            if (width === 0) {
                throw new Error(
                    "The red worksheet is empty."
                );
            }


            /*
             * Check dimensions.
             */

            if (
                green.length !== height ||
                blue.length !== height
            ) {
                throw new Error(
                    "Channel sheets have different dimensions."
                );
            }

            for (let y = 0; y &lt; height; y++) {

                if (
                    green[y].length !== width ||
                    blue[y].length !== width
                ) {
                    throw new Error(
                        "Channel sheets have different dimensions."
                    );
                }

            }


            /*
             * Build RGBA pixel buffer.
             */

            const pixels =
                new Uint8ClampedArray(
                    width * height * 4
                );

            for (let y = 0; y &lt; height; y++) {

                for (let x = 0; x &lt; width; x++) {

                    const index =
                        (y * width + x) * 4;

                    pixels[index] =
                        clampByte(red[y][x]);

                    pixels[index + 1] =
                        clampByte(green[y][x]);

                    pixels[index + 2] =
                        clampByte(blue[y][x]);

                    pixels[index + 3] = 255;

                }

            }


            /*
             * Put pixels into Canvas.
             */

            const canvas =
                document.createElement("canvas");

            canvas.width = width;
            canvas.height = height;

            const ctx =
                canvas.getContext("2d");

            const imageData =
                new ImageData(
                    pixels,
                    width,
                    height
                );

            ctx.putImageData(
                imageData,
                0,
                0
            );


            /*
             * Show preview.
             */

            const mime =
                imageFormat.value === "jpeg"
                    ? "image/jpeg"
                    : "image/png";

            const quality =
                imageFormat.value === "jpeg"
                    ? 0.95
                    : undefined;

            const dataUrl =
                canvas.toDataURL(
                    mime,
                    quality
                );

            previewImage.src = dataUrl;
            preview.style.display = "block";


            /*
             * Download.
             */

            const extension =
                imageFormat.value;

            const finalFilename =
                filename ||
                `image.${extension}`;

            downloadDataUrl(
                dataUrl,
                finalFilename
            );

        }


        function clampByte(value) {

            value = Number(value);

            if (!Number.isFinite(value)) {
                return 0;
            }

            return Math.max(
                0,
                Math.min(
                    255,
                    Math.round(value)
                )
            );

        }


        /* -------------------------------------------------
         * Download helper
         * ------------------------------------------------- */

        function downloadDataUrl(dataUrl, filename) {

            const link =
                document.createElement("a");

            link.href = dataUrl;
            link.download = filename;

            document.body.appendChild(link);

            link.click();

            link.remove();

        }


        /* -------------------------------------------------
         * Execute
         * ------------------------------------------------- */

        executeButton.addEventListener(
            "click",
            async () =&gt; {

                clearStatus();

                try {

                    if (!selectedFile) {

                        throw new Error(
                            "No input file selected."
                        );

                    }

                    const mode = getMode();

                    let output = outputPath.value.trim();

                    if (!output) {

                        const base =
                            getBaseName(
                                selectedFile.name
                            );

                        if (mode === "toxlsx") {
                            output = base + ".xlsx";
                        }
                        else {
                            output =
                                base +
                                "." +
                                imageFormat.value;
                        }

                    }


                    /*
                     * Validate image -&gt; XLSX.
                     */

                    if (mode === "toxlsx") {

                        const extension =
                            getExtension(
                                selectedFile.name
                            );

                        if (
                            ![
                                "jpg",
                                "jpeg",
                                "png"
                            ].includes(extension)
                        ) {

                            throw new Error(
                                "Unsupported image format. " +
                                "Supported formats are: jpg, jpeg, png"
                            );

                        }


                        if (
                            getExtension(output) !==
                            "xlsx"
                        ) {

                            throw new Error(
                                "Output path is not a spreadsheet."
                            );

                        }


                        setStatus(
                            "Converting image to XLSX...",
                            "processing"
                        );

                        await imageToXlsx(
                            selectedFile,
                            output
                        );

                    }


                    /*
                     * Validate XLSX -&gt; image.
                     */

                    else {

                        if (
                            getExtension(
                                selectedFile.name
                            ) !== "xlsx"
                        ) {

                            throw new Error(
                                "Input file is not a spreadsheet."
                            );

                        }


                        const outputExtension =
                            getExtension(output);

                        if (
                            ![
                                "png",
                                "jpg",
                                "jpeg"
                            ].includes(outputExtension)
                        ) {

                            throw new Error(
                                "Output must be an image."
                            );

                        }


                        setStatus(
                            "Converting XLSX to image...",
                            "processing"
                        );

                        await xlsxToImage(
                            selectedFile,
                            output
                        );

                    }


                    setStatus(
                        `Saved "${output}"`,
                        "success"
                    );

                }
                catch (error) {

                    console.error(error);

                    setStatus(
                        error.message,
                        "error"
                    );

                }

            }
        );


        /* -------------------------------------------------
         * Initial state
         * ------------------------------------------------- */

        imageFormatRow.style.display = "none";

    &lt;/script&gt;

</content><category term="extra"/></entry><entry><title>Introduction to Programming</title><link href="https://grybouilli.github.io/introduction-to-programming.html" rel="alternate"/><published>2026-09-07T00:00:00+02:00</published><updated>2026-09-07T00:00:00+02:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2026-09-07:/introduction-to-programming.html</id><summary type="html">&lt;h1&gt;About&lt;/h1&gt;
&lt;p&gt;"Introduction to Programming" is a class I teach at the Université Jean Monnet de Saint-Étienne, to first year Master students in the &lt;a href="https://master-digicrea.univ-st-etienne.fr/en/index.html"&gt;Digicrea Master&lt;/a&gt; and the &lt;a href="https://www.univ-st-etienne.fr/fr/formation/master-XB/master-XB/master-arts-parcours-creation-contemporaine-et-nouvelles-technologies-option-realisateur-en-informatique-musicale-option-arts-numeriques-KX4SMCQZ.html"&gt;RIM Master&lt;/a&gt;. The students primarily come from an artistic background and have supposedly no knowledge in computing, and very light background in mathematics …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;About&lt;/h1&gt;
&lt;p&gt;"Introduction to Programming" is a class I teach at the Université Jean Monnet de Saint-Étienne, to first year Master students in the &lt;a href="https://master-digicrea.univ-st-etienne.fr/en/index.html"&gt;Digicrea Master&lt;/a&gt; and the &lt;a href="https://www.univ-st-etienne.fr/fr/formation/master-XB/master-XB/master-arts-parcours-creation-contemporaine-et-nouvelles-technologies-option-realisateur-en-informatique-musicale-option-arts-numeriques-KX4SMCQZ.html"&gt;RIM Master&lt;/a&gt;. The students primarily come from an artistic background and have supposedly no knowledge in computing, and very light background in mathematics.&lt;/p&gt;
&lt;p&gt;The class aims to introduce the foundation of programming by learning the basics of C++. The class tackles notions of variables, types and scope, boolean operations and logic, conditional statements and loops, functions, as well as data structure such as matrices, arrays and vectors. A part of the class focuses on developers' tools such as compilation with clang, library linking, and build system with cmake as an example. Scripting languages are lightly covered, with Javascript as an example.&lt;/p&gt;
&lt;p&gt;The practise examples and assignment revolve around image processing and audio / music, so as to make a direct link with the rest of their curriculum.&lt;/p&gt;
&lt;h1&gt;Course content&lt;/h1&gt;
&lt;h2&gt;Lectures&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Slides&lt;/th&gt;
&lt;th&gt;Exercises&lt;/th&gt;
&lt;th&gt;Assignment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/l1/main.pdf"&gt;Lecture 1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p1/1_about_variables.pdf"&gt;Sheet 1&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p1/2_booleans.pdf"&gt;Sheet 2&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p1/3_cond_statements.pdf"&gt;Sheet 3&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p1/4_switch_enum.pdf"&gt;Sheet 4&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p1/5_loops.pdf"&gt;Sheet 5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a1/assignment.pdf"&gt;Assignment 1&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/l2/main.pdf"&gt;Lecture 2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p2/1_functions.pdf"&gt;Sheet 1&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p2/2_main_global_and_local.pdf"&gt;Sheet 2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a2/assignment.pdf"&gt;Assignment 2&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/l3/main.pdf"&gt;Lecture 3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p3/1_matrices.pdf"&gt;Sheet 1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a3/assignment.pdf"&gt;Assignment 3&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/l4/main.pdf"&gt;Lecture 4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p4/1_exercises.pdf"&gt;Sheet 1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/assignment.pdf"&gt;Assignment 4&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/l5/main.pdf"&gt;Lecture 5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p5/1_pointers.pdf"&gt;Sheet 1&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p5/2_stl_iterators.pdf"&gt;Sheet 2&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p5/3_txtfiles.pdf"&gt;Sheet 3&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p5/4_audio_files.pdf"&gt;Sheet 4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a5/assignment.pdf"&gt;Assignment 5&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/l6/main.pdf"&gt;Lecture 6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p6/1_classes.pdf"&gt;Sheet 1&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p6/2_generic_functions.pdf"&gt;Sheet 2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a6/assignment.pdf"&gt;Assignment 6&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Extras&lt;/h2&gt;
&lt;h4&gt;Exercises resources&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Practise&lt;/th&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Practise 8&lt;/td&gt;
&lt;td&gt;Problem B.1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p3/landscape.xlsx"&gt;landscape.xlsx&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practise 9&lt;/td&gt;
&lt;td&gt;Problem 1.3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p9/1.3.alice.zip"&gt;Archive&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practise 9&lt;/td&gt;
&lt;td&gt;Problem 2.2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p9/2.2.alice.zip"&gt;Archive&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practise 9&lt;/td&gt;
&lt;td&gt;Problem 3.1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p9/3.1.alice.zip"&gt;Archive&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practise 9&lt;/td&gt;
&lt;td&gt;Problem 4.1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p9/4.1.alice.zip"&gt;Archive&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practise 13&lt;/td&gt;
&lt;td&gt;Whole sheet&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/p5/practise13.zip"&gt;Archive&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;Assignment resources&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Assignment&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Assignment 4: Exercise 1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/windows/problem1.zip"&gt;Windows&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/macos/problem1.zip"&gt;MacOS&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/macos/problem1.zip"&gt;Linux&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assignment 4: Exercise 2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/windows/problem2.zip"&gt;Windows&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/macos/problem2.zip"&gt;MacOS&lt;/a&gt; &lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a4/macos/problem2.zip"&gt;Linux&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assignment 5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/a5/assignment5.zip"&gt;Resource&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;Documentation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://grybouilli.github.io/courses/intro_to_programming/windows_build_tools_installation_steps.pdf"&gt;Guide to install CLang and CMake on Windows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Programs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://grybouilli.github.io/extra/image_exporter.html"&gt;image_exporter&lt;/a&gt;: a web interface to convert image files to .xlsx files and back.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grybouilli.github.io/extra/img_visualizer.html"&gt;img_visualizer&lt;/a&gt;: a web interface to visualize images from their pixel values. B/W and RGB mode available.&lt;/li&gt;
&lt;/ul&gt;</content><category term="Courses"/></entry><entry><title>Multi-Voice Sound Synthesis with Arduino</title><link href="https://grybouilli.github.io/multi-voice-sound-synthesis-with-arduino.html" rel="alternate"/><published>2024-08-30T00:00:00+02:00</published><updated>2024-08-30T00:00:00+02:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2024-08-30:/multi-voice-sound-synthesis-with-arduino.html</id><summary type="html">&lt;p&gt;This is a personal project. The idea was to explore Direct Digital Synthesis (DDS) using the Atmega 328p's timers and interrupts. A full explanation article is avaible on &lt;a href="https://medium.com/@grybouilli/multi-voice-sound-generation-with-arduino-9ae01ed0e441"&gt;Medium&lt;/a&gt; and the source code is available on &lt;a href="https://github.com/grybouilli/multi-voice-for-Arduino"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The end result is the ability to produce multi-voice sounds on an Arduino …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is a personal project. The idea was to explore Direct Digital Synthesis (DDS) using the Atmega 328p's timers and interrupts. A full explanation article is avaible on &lt;a href="https://medium.com/@grybouilli/multi-voice-sound-generation-with-arduino-9ae01ed0e441"&gt;Medium&lt;/a&gt; and the source code is available on &lt;a href="https://github.com/grybouilli/multi-voice-for-Arduino"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The end result is the ability to produce multi-voice sounds on an Arduino pin. Each voice can have its own signal shape. The repo contains a demo with the Super Mario theme.&lt;/p&gt;</content><category term="Projects"/></entry><entry><title>Spinning Display (PoV)</title><link href="https://grybouilli.github.io/spinning-display-pov.html" rel="alternate"/><published>2023-12-31T00:00:00+01:00</published><updated>2023-12-31T00:00:00+01:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2023-12-31:/spinning-display-pov.html</id><summary type="html">&lt;h2&gt;An implementation for a Persistence of Vision effect on the Atmega 328p.&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;
This is a scholar project. Three main goals were set : to display an analog clock, to display images and to display a digital clock. More details about the expectations &lt;a href="https://gregwar.com/embedded_system/project.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;  &lt;/th&gt;
&lt;th&gt;  &lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="padding-right: 20px;"&gt;
&lt;img src="/images/pov_spinning_demo.gif" width=400px&gt;
&lt;/td&gt;
&lt;td&gt;
Our team of three managed to implement all …&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary><content type="html">&lt;h2&gt;An implementation for a Persistence of Vision effect on the Atmega 328p.&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;
This is a scholar project. Three main goals were set : to display an analog clock, to display images and to display a digital clock. More details about the expectations &lt;a href="https://gregwar.com/embedded_system/project.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;  &lt;/th&gt;
&lt;th&gt;  &lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="padding-right: 20px;"&gt;
&lt;img src="/images/pov_spinning_demo.gif" width=400px&gt;
&lt;/td&gt;
&lt;td&gt;
Our team of three managed to implement all three solutions. For image display, the images bytes are sent using bluetooth. For the digital clock, the image display solution cna be used, or, one can flash a program with hard coded digits images. 
The repo to the project can be found &lt;a href="https://github.com/grybouilli/spinning-display"&gt; here &lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</content><category term="Projects"/></entry><entry><title>VAE Synth for Ossia Score</title><link href="https://grybouilli.github.io/vae-synth-for-ossia-score.html" rel="alternate"/><published>2023-08-31T00:00:00+02:00</published><updated>2023-08-31T00:00:00+02:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2023-08-31:/vae-synth-for-ossia-score.html</id><summary type="html">&lt;h2&gt;A Pytorch based Variational Auto-Encoder for Ossia Score.&lt;/h2&gt;
&lt;h3&gt;Context&lt;/h3&gt;
&lt;p&gt;This work was conducted during my 2023 Summer Internship at Chalmers University, under the supervision of Kivanç Tatar. The goal was to generate sound using ML Pytorch models in a DAW or intermedia sequencer such as Ossia Score. Initially, I figured …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;A Pytorch based Variational Auto-Encoder for Ossia Score.&lt;/h2&gt;
&lt;h3&gt;Context&lt;/h3&gt;
&lt;p&gt;This work was conducted during my 2023 Summer Internship at Chalmers University, under the supervision of Kivanç Tatar. The goal was to generate sound using ML Pytorch models in a DAW or intermedia sequencer such as Ossia Score. Initially, I figured I´d try to run some python code inside PureData which would in turn be embedded inside Ossia Score. Stepping forward, I ended up developping an addon-on for Ossiam Score to use Pytorch VAEs to produce sound in real-time.&lt;/p&gt;
&lt;h3&gt;Conducted work&lt;/h3&gt;
&lt;p&gt;I developed an object which allows running Python code in two steps: the loading part and the running part. The heavy and loading work should be done in the loading part and the sound synthesis should be put in the running part. This solution isn't Pytorch specific, it rather allows the user to use Python code to generate sound.&lt;/p&gt;
&lt;p&gt;To make it more accessible, I created a Docker image: you can find guidelines on how to use it &lt;a href="https://github.com/grybouilli/docker-for-ossia/tree/master"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/images/vae_synth_demo.gif"&gt;&lt;/p&gt;
&lt;p&gt;The repo to the ossia-score addon can be found &lt;a href="https://github.com/grybouilli/VAE_Synth/"&gt;here&lt;/a&gt;&lt;/p&gt;</content><category term="Projects"/></entry><entry><title>Aquarium Network</title><link href="https://grybouilli.github.io/aquarium-network.html" rel="alternate"/><published>2023-05-30T00:00:00+02:00</published><updated>2023-05-30T00:00:00+02:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2023-05-30:/aquarium-network.html</id><summary type="html">&lt;h2&gt;Let's go fishing&lt;/h2&gt;
&lt;p&gt;This is a scholar project. The goal was to implement the foundations for connected virtual aquariums. The idea is that there is an aquarium, handled by the TCP/IP server, and clients can connect to the server to get views on parts of the aquarium. When using …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Let's go fishing&lt;/h2&gt;
&lt;p&gt;This is a scholar project. The goal was to implement the foundations for connected virtual aquariums. The idea is that there is an aquarium, handled by the TCP/IP server, and clients can connect to the server to get views on parts of the aquarium. When using several computers, one can put the monitors side by side and observe the fishes swim from one screen to the other ! The project is implemented using C/C++. Click on the image below to go to the repo of the code.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;a href="https://github.com/grybouilli/AquariumNetwork"&gt;
        &lt;img src="/images/demo_aquarium.gif"/&gt;
    &lt;/a&gt;
&lt;/p&gt;</content><category term="Projects"/></entry><entry><title>No published work yet...</title><link href="https://grybouilli.github.io/no-published-work-yet.html" rel="alternate"/><published>2010-12-03T10:20:00+01:00</published><updated>2010-12-03T10:20:00+01:00</updated><author><name>Nicolas Gry</name></author><id>tag:grybouilli.github.io,2010-12-03:/no-published-work-yet.html</id><content type="html">&lt;p&gt;This a placeholder so that this webpage exists. As of today, I haven't published any work yet. Hopefully, this page will fill up soon !&lt;/p&gt;</content><category term="Publications"/></entry></feed>