Kategória: Broadcast

  • SuperCollider: Supernova multi threading, több szerveres megoldás

    A supernova a SuperCollider egy speciális verziója, amely párhuzamosított (multithreaded) audio-motort használ. Ez azt jelenti, hogy több CPU magot tud kihasználni a hangszintézishez, így jobb teljesítményt érhetsz el nagyobb projektek esetén.

    Alapértelmezés szerint a scsynth csak egy CPU magot használ, míg a supernova lehetővé teszi a processzor több magjának egyidejű használatát.

    Server.supernova;
    s.options.threads = 8;

    Miért hasznos a supernova?

    Jobb teljesítmény nagyobb számú synth esetén
    Több CPU mag egyidejű kihasználása
    Kevesebb késleltetés (latency)
    Szálkezelés optimalizálása nagyobb projektekhez

    A supernova kifejezetten akkor hasznos, ha sok szintetizátort futtatsz egyszerre, és nem akarod, hogy az egyetlen processzormagod legyen a szűk keresztmetszet.

    Terminál-parancs: oscsend

    oscsend localhost 57121 /setFile ss "/mnt/ramdrive/temp/002.wav" "p1"
    oscsend localhost 57121 /play s "p1"
    oscsend localhost 57121 /stop sf "p1" 7.0
    oscsend localhost 57121 /stop fs 7.0 "p1"

    Terminál-parancs: supernova (SuperCollider) indítása

     ./startsc

    Terminál-parancs: JACK supernova indítása

    ./jacksn.sh {darkice id}

    Hasznos linkek:

    CPU headroom, multi threading

  • Broadcast Core manuális indítása és tesztelése

    A fejlesztés korai szakaszában a már működő SuperCollider – JACK – Darkice szolgáltatás-együttes indítása és tesztelése. Az indítási folyamat lépéseinek sorrendjét tartani kell. Amíg a SuperCollider nem bootolt be, addig a JACK nem tud hozzá csatlakozni. A JACK fogja a SuperCollider által lejátszott audio fájlt a Darkice-nak biztosítani.

    A Darkice szerver indítása nem szükséges, mert szolgáltatásként elindul a szerverrel együtt. Az mtm nevű felhasználóhoz kapcsolt szolgáltatás a megfelelő jogosultságokkal. A konfigurációs fájl is az mtm felhasználó home könyvtárában található. Az mtm felhasználó tudja is indítani a Darkice szolgáltatást.

    service darkice stat | stop | status

    Minden parancs az mtm felhasználónévvel bejelentkezve adható ki!

    1. SuperCollider indítása

    bash:
    sclang

    2. SuperCollider szerver bootolása

    supercollider:
    s.boot;

    3. JACK szolgáltatás indítása

    Mivel az SuperCollider nem lép ki promptba az sclang-ból, ezért ezt egy másik terminálablakban kell végrehajtani.

    A JACK szolgáltatáshoz ismerni kell a SuperCollider audio out csatornájának azonosítóját (portját). Ez viszont minden Darkice indításkor megváltozhat.

    A JACK indításához írt bash script paramétereként kell megadni az SC audio out csatorna azonosítóját, de ha nem ismered, akkor csak paraméterként indítsd el a bash scriptet, és a hibaüzenetek közt kiírja azt is, hogy milyen azonosítóval érhető el az out csatona.

    A parancsot az mtm felhasználó home könyvtárában kell kiadni.

    bash:
    ./jack.sh <audio out azonosítója>

    A SuperCollider innentől kell, hogy tudjon hangot kiadni az IceCast2 szerver felé. Ha tesztelésre van szükség, akkor az sclang-ban kiadható egy parancs, mely egy 440Hz-es hangot küld, 30%-os hangerővel, sztereó csatornán keresztül a JACK felé.

    supercollider:
    { SinOsc.ar(440, 0, 0.3).dup }.play;

    Böngészőben megnyitható a következő címen a stream:

    Web:
    http://192.168.66.12:8000/mtm.mp3

    Az sclang-ban megállítható a teszthang a következő parancsokkal:

    supercollider:
    s.freeAll;

    4. A SuperCollider script betöltése sclangban.

    supercollider:
    "/home/mtm/html/mtm.project/systems/broadcast/core/scd/scplay3.1.scd".load;

    5. OCS vezérlő betöltése – Node.JS script WebSocketServer

    A script könyvtára: /home/mtm/html/mtm.project/systems/broadcast/core/ocs

    Script: wss_osc.js

    Ennek indítása a előtt meg kell győződni arról, hogy a scriptben lévő portok megfelelően vannak beállítva. Az OSC parancsokat az SuperCollider egy meghatározott porton fogadja. Hogy melyik porton az lellenőrizhető az sclang-ban kiadott paranccsal:

    supercollider:
    NetAddr.langPort
    wss_osc.js
    remoteAddress: "127.0.0.1",
    remotePort: 57122 // SC alapértelmezett OSC portja

    A remotePort értéke egyenlő kell, hogy legyen az sclang-ban kapott értékkel.

    Ha rendben van a következő paranccsal indítható a wss_osc.js

    bash:
    pm2 start wss_osc.js --name osc-controller

    OSC WSS port: 8080

    Ebben a könyvtárban az ./start_osc.sh bash script is ezt a parancsot adja ki.

    6. OSC vezérlő tesztelése

    Ubuntu Server terminálablakából a wscat segítéségvel. A wscat alkalmas a WebSocket szerverkkel kommunikálni, így JSON objektumokat küldhetünk az OSC vezérlőnek úgy, ahogyan a SuperCollid script várja. A wscat a Node.JS része, így npm használatával telepíthető, ha még nem lenne elérhető. Érdemes globális telepítést indítani, hogy bármelyik virtuális környezetben elérhető legyen.

    Telepítése:

    bash:
    npm install -g wscat

    A -g opció jelenti a globális telepítést.

    A wscat indítása:

    bash:
    wscat -c ws://127.0.0.1:8080

    Példa a JSON objektumokban küldhető parancsokra:

    wscat:
    {"command": "setFile", "filePath": "/mnt/ramdrive/temp/001.wav", "bufferName": "p1"}
    {"command": "setFile", "filePath": "/mnt/ramdrive/temp/020.wav", "bufferName": "p2"}
    {"command": "setFile", "filePath": "/mnt/ramdrive/temp/018.wav", "bufferName": "p3"}
    {"command": "play", "bufferName": "p1"}
    {"command": "play", "bufferName": "p2"}
    {"command": "play", "bufferName": "p3"}
    {"command": "stop", "fadeTime": 2.0, "bufferName": "p1"}
    {"command": "stop", "fadeTime": 7.0, "bufferName": "p2"}
    {"command": "stop", "fadeTime": 7.0, "bufferName": "p3"}

  • Broadcast Core: SuperCollider és Node.js

    A broadcast vezérlés fő összetevői: SuperCollider server az audio fájlok lejátszásra; Node.js java script a SuperCollider vezérlésére OSC (Open Sound Control) használatával. Ezek a háttérben futnak szolgáltatásként, és a megadott portokon lehet elérni a szolgáltatásokat.

    A SuperCollider indításáról itt lehet olvasni bővebben.

    SuperCollider: scplay3.1.scd

    Az scplay rendszermag már a harmadik verzióban kerül továbbfejlesztésre. Több próbálkozás közül ez a verzió hozta meg a várt működést.

    A fájl helye:
    ./mtm.project/systems/broadcast/core/scd/scplay3.x.scd

    Indítása SuperCollider serveren

    s.boot;
    "/home/mtm/html/mtm.project/systems/broadcast/core/scd/scplay3.1.scd".load;

    Ha az OSC parancsokat szeretnénk az sclang környezetében használni, akkor meg kell nyitni egy portot, melyen keresztül kommunikálni tudunk a SuperCollider serverrel

    n = NetAddr("127.0.0.1", NetAddr.langPort);

    Bufferelés

    Az eredeti gondolat az volt, hogy 2 buffer kerül létrehozásra, melybe betöltető egy audio fájl (URL alapján) elindítható a lejátszás, dinamikus fade-out használatával meg lehet állítani a lejátszást.

    Az scplay 3. verziója több szálat is képes bufferelni, és a bufferek mindegyikét külön-külön tudja kezelni (lejátszani és megállítani) a buffer nevével, melyet a fájl betöltésénél kell megadni (lásd: setfile).

    A bufferek egy Dictionary típusú változóban kerülnek tárolására.

    ~buffers = Dictionary.new;

    A SuperCollider kód Dokumentáció

    Ez a SuperCollider szkript egy egyszerű OSC-vezérelt hanglejátszó rendszert valósít meg. A kód a SuperCollider szerveren fut, ahol az OSC (Open Sound Control) üzeneetek által kapott parancsokat hajtja végre. A szkript képes hanglefájlokat beolvasni, lejátszani, megállítani és figyelni a lejtátszási pozíciót.

    1. Globális Változók

    ~buffers = Dictionary.new;
    ~players = Dictionary.new;
    • ~buffers: Egy szótár („Dictionary”), amely a betöltött hangfájlokat Buffer objektumok formájában tárolja.
    • ~players: Egy szótár, amely a futó Synth objektumokat tárolja, amelyek a hangfájlokat lejátsszák.

    2. OSC Definíciók

    2.1. Hangfájl beállítása

    OSCdef(\setFile, { |msg|
        var bufferName;
    
        ~filePath = msg[1];
        bufferName = msg[2];
        "File set to: ".post; ~filePath.postln;
        ("Buffer: " + bufferName).postln;
        ~buffers[bufferName] = Buffer.read(s, ~filePath);
    }, '/setFile');
    • OSCdef(\setFile, { |msg| ... }, '/setFile');: Egy OSC hallgató, amely a /setFile üzeneetre reagál.
    • msg[1]: A beolvasandó hangfájl elérési útja.
    • msg[2]: A buffer neve, amely alatt a fájl elérhető lesz.
    • Buffer.read(s, ~filePath): A hangfájl betöltése a szerverre, majd a ~buffers szótárban elmentése.

    2.2. Hangfájl leátszása

    OSCdef(\play, { |msg|
        var bufferName;
        bufferName = msg[1];
    
        if (~filePath.notNil) {
            ~players[bufferName] = Synth(\playerSynth, [\buf, ~buffers[bufferName]]);
            "Playing file: ".post; ~filePath.postln;
            ("Playing buffer: " + bufferName).postln;
        } {
            "No file set!".postln;
        };
    }, '/play');
    • OSCdef(\play, { |msg| ... }, '/play');: Egy OSC hallgató, amely a /play üzenetre reagál.
    • msg[1]: A buffer neve, amelyet le kell játszani.
    • Synth(\playerSynth, [\buf, ~buffers[bufferName]]): Egy Synth példányt hoz létre, amely lejátssza a bufferName nevű bufferben lévő hangot.
    • ~players[bufferName]: A futó Synth példányt tárolja a megfelelő kulcs alatt.

    2.3. Lejátszás leállítása

    OSCdef(\stop, { |msg|
        var fadeTime = msg[1].asFloat;
        var bufferName = msg[2];
    
        fadeTime = fadeTime.max(0.1);
    
        if (~players[bufferName].notNil) {
            "Stopping " + bufferName + "] with fade-out: ".post; fadeTime.postln;
            ~players[bufferName].set(\gate, 0, \fadeTime, fadeTime);
            ~players[bufferName] = nil;
        } {
            ("No active player! [" + bufferName + "]").postln;
        };
    }, '/stop');
    • fadeTime = msg[1].asFloat: Az elhalvás ideje.
    • fadeTime = fadeTime.max(0.1): Biztosítja, hogy ne legyen 0 vagy negatív.
    • ~players[bufferName].set(\gate, 0, \fadeTime, fadeTime): Az envelope gate lezárásával elindít egy fade-out-ot.
    • ~players[bufferName] = nil;: A referencia törlése.

    3. Lejátszás definíciója (SynthDef)

    SynthDef(\playerSynth, {
        |buf, fadeTime = 1.0, gate = 1|
        var sig, env, pos, rate;
    
        rate = BufRateScale.kr(buf);
        env = EnvGen.kr(Env.asr(0.01, 1, fadeTime, curve: -4), gate, doneAction: 2);
        sig = PlayBuf.ar(2, buf, rate, loop: 0);
        
        pos = Phasor.ar(0, rate, 0, BufFrames.kr(buf));
        SendReply.kr(Impulse.kr(1), '/playbackPosition', pos / BufSampleRate.kr(buf));
        
        Out.ar(0, sig * env);
    }).add;
    • BufRateScale.kr(buf): A buffer sebességi arányának kiszámítása.
    • EnvGen.kr(...): Az envelope generálása a fade-out kezelésére.
    • PlayBuf.ar(...): A buffer lejátszása.
    • Phasor.ar(...): A lejátszási pozíció kiszámítása.
    • SendReply.kr(...): A lejtátszási időközön OSC üzenetet küld.
    • Out.ar(0, sig * env): A jel kiküldése a hangkimenetre.

    4. Lejátszási Pozíció Figyelése

    OSCdef(\positionListener, { |msg|
        var time, min, sec;
        time = msg[3].asInteger;
        min = time.div(60);
        sec = time.mod(60);
        ("Playback Position: " ++ min.asString ++ ":" ++ sec.asString).postln;
    }, '/playbackPosition');

    Ez az OSC hallgató figyeli a /playbackPosition üzeneetet, és formázottan kiírja az aktuális időpozíciót.

    Node.js OSC és WebSocket szerver dokumentáció

    A fájl helye:
    ./mtm.project/systems/broadcast/core/osc/wss_osc.js

    Áttekintés

    Ez a Node.js alkalmazás egy Open Sound Control (OSC) protokollt használó szerver, amely kommunikál egy SuperCollider (SC) szerverrel. Az alkalmazás egy WebSocket szervert is futtat, amely lehetővé teszi, hogy a kliensek valós időben küldjenek parancsokat és kapjanak visszajelzéseket a lejátszási pozícióról.

    Függőségek

    Az alkalmazás két fő Node.js modult használ:

    • osc: Az OSC protokoll kezeléséhez
    • ws: WebSocket kommunikációhoz

    A szükséges csomagok telepíthetők a következő paranccsal:

    npm install osc ws

    OSC Szerver Konfiguráció

    const osc = require('osc');

    Az osc modult importáljuk, hogy az OSC protokollal tudjunk kommunikálni.

    const udpPort = new osc.UDPPort({
        localAddress: "0.0.0.0",
        localPort: 57121, // Az SC által figyelt port
        remoteAddress: "127.0.0.1",
        remotePort: 57122 // SC alapértelmezett OSC portja
    });

    Létrehozunk egy UDP alapú OSC kapcsolatot:

    • localAddress: "0.0.0.0": A szerver minden hálózati interfészen figyel.
    • localPort: 57121: A helyi OSC szerver ezen a porton hallgat.
    • remoteAddress: "127.0.0.1": A cél SC szerver azonosítója (localhost).
    • remotePort: 57122: Az SC alapértelmezett OSC portja.
    udpPort.open();

    Megnyitjuk az OSC kapcsolatot.

    udpPort.on("ready", () => {
        console.log("OSC vezérlő készen áll");
    });

    Amikor az OSC kapcsolat készen áll, egy üzenetet írunk a konzolra.

    OSC Parancsok Küldése

    Az alábbi függvények az OSC üzenetek küldését végzik:

    setFile(filePath, bufferName)

    function setFile(filePath, bufferName) {
        udpPort.send({
            address: "/setFile",
            args: [filePath, bufferName]
        });
        console.log(`Fájl beállítva: ${filePath}, buffer: ${bufferName}`);
    }

    Egy fájl beállítását végzi egy adott buffer névre.

    play(bufferName)

    function play(bufferName) {
        udpPort.send({
            address: "/play",
            args: [bufferName]
        });
        console.log(`Lejátszás indítása: ${bufferName}`);
    }

    Lejátszási parancs küldése egy adott buffer névre.

    stop(fadeTime, bufferName)

    function stop(fadeTime, bufferName) {
        udpPort.send({
            address: "/stop",
            args: [fadeTime, bufferName]
        });
        console.log(`Lejátszás leállítása: ${bufferName}, fade-out: ${fadeTime}s`);
    }

    Leállítási parancs küldése egy adott bufferre, egy fade-out idővel.

    OSC Üzenetek Fogadása

    udpPort.on("message", (oscMsg) => {
        if (oscMsg.address === "/playbackPosition") {
            let time = Math.floor(oscMsg.args[0]);
            let min = Math.floor(time / 60);
            let sec = time % 60;
            let formattedTime = `${min}:${sec.toString().padStart(2, '0')}`;
            
            console.log(`Lejátszási pozíció: ${formattedTime}`);
    
            // Küldjük az összes csatlakozott WebSocket kliensnek
            wss.clients.forEach(client => {
                if (client.readyState === WebSocket.OPEN) {
                    client.send(JSON.stringify({ type: "position", time: formattedTime }));
                }
            });
        }
    });

    Ha egy /playbackPosition üzenetet kapunk:

    • Az idő másodpercekben érkezik.
    • Átalakítjuk percekké és másodpercekké.
    • Kiírjuk a konzolra.
    • Minden WebSocket kliensnek elküldjük az időt JSON formátumban.

    WebSocket Szerver Beállítása

    const WebSocket = require('ws');

    Importáljuk a WebSocket modult.

    const wss = new WebSocket.Server({ port: 8080 });

    Létrehozunk egy WebSocket szervert a 8080-as porton.

    wss.on("connection", (ws) => {
        console.log("Új WebSocket kapcsolat");

    Amikor egy új WebSocket kliens csatlakozik, kiírjuk a konzolra.

    WebSocket Üzenetek Kezelése

        ws.on("message", (message) => {
            try {
                const data = JSON.parse(message);
                console.log("Kliens üzenet:", data);

    Ha a kliens küld egy üzenetet, JSON-ként dolgozzuk fel.

                if (data.command === "setFile") {
                    setFile(data.filePath, data.bufferName);
                } else if (data.command === "play") {
                    play(data.bufferName);
                } else if (data.command === "stop") {
                    stop(data.fadeTime, data.bufferName);
                }

    A beérkező parancsokat a megfelelő függvényekkel továbbítjuk az OSC szerver felé.

            } catch (err) {
                console.error("Hibás üzenet:", err);
            }
        });

    Ha a JSON formátum hibás, hibaüzenetet írunk ki.

    WebSocket Kapcsolat Bontása

        ws.on("close", () => {
            console.log("WebSocket kapcsolat bezárva");
        });

    Ha a kapcsolat megszakad, kiírjuk a konzolra.

    WebSocket Szerver Indítása

    console.log("WebSocket szerver elindítva a 8080-as porton.");

    Induláskor egy üzenetet írunk ki a konzolra.

    Összegzés

    Ez a Node.js alkalmazás kétirányú kommunikációt biztosít:

    • OSC-n keresztül SuperColliderhez csatlakozik, és fájlok betöltését, lejátszást, illetve leállítást vezérel.
    • WebSocket szerverként működik, és kliensekkel kommunikál, lehetővé téve számukra az SC vezérlését és a lejátszási információk fogadását.

    Az alkalmazás lehetővé teszi, hogy valós időben történjen a zenei fájlok lejátszása és vezérlése egy távoli SC szerveren keresztül.

    Node.js indítása

    pm2 start wss_osc.js --name osc-controller

    Node.js környezet kialakítása

    https://chatgpt.com/share/67acf90a-a484-8008-afc6-508c209dd448

    https://chatgpt.com/share/67acf93c-3f0c-8008-8c0c-ac79c8ab96df