Monorepo for Aesthetic.Computer aesthetic.computer
at main 111 lines 2.7 kB view raw
1/** 2 * Max/MSP JavaScript for reading audio from buffer~ and streaming to jweb~ 3 * 4 * Uses Max's Buffer object to read samples at full sample rate. 5 * Runs on a timer to periodically grab chunks and send them. 6 * 7 * Arguments: 8 * buffer name (e.g., "ac-audio-L") 9 * 10 * Inlets: 11 * 0: bang to read next chunk, or "start"/"stop" messages 12 * 13 * Outlets: 14 * 0: executejavascript message with sample arrays 15 */ 16 17// Configuration 18var CHUNK_SIZE = 512; // Samples per chunk (~11.6ms at 44.1kHz) 19var BUFFER_SIZE = 4096; // Total buffer size (must match buffer~ size) 20 21// Buffer references (set via arguments or messages) 22var bufferNameL = "ac-audio-L"; 23var bufferNameR = "ac-audio-R"; 24var bufL = null; 25var bufR = null; 26 27// State 28var readPos = 0; 29var running = false; 30var task = null; 31 32// Declare inlets/outlets 33inlets = 1; 34outlets = 1; 35 36function loadbang() { 37 // Initialize buffer references after a short delay 38 task = new Task(initBuffers, this); 39 task.schedule(100); 40} 41 42function initBuffers() { 43 bufL = new Buffer(bufferNameL); 44 bufR = new Buffer(bufferNameR); 45 46 if (bufL.framecount() > 0) { 47 BUFFER_SIZE = bufL.framecount(); 48 post("Buffer initialized: " + BUFFER_SIZE + " frames\n"); 49 } 50} 51 52function start() { 53 if (!bufL || !bufR) { 54 initBuffers(); 55 } 56 running = true; 57 readPos = 0; 58 post("Sample streaming started\n"); 59} 60 61function stop() { 62 running = false; 63 post("Sample streaming stopped\n"); 64} 65 66function bang() { 67 if (!running) return; 68 if (!bufL || !bufR) { 69 initBuffers(); 70 if (!bufL || !bufR) return; 71 } 72 73 readChunk(); 74} 75 76function readChunk() { 77 var leftSamples = []; 78 var rightSamples = []; 79 80 // Read CHUNK_SIZE samples from each buffer 81 for (var i = 0; i < CHUNK_SIZE; i++) { 82 var pos = (readPos + i) % BUFFER_SIZE; 83 leftSamples.push(bufL.peek(1, pos, 1)); // channel 1, frame pos, 1 sample 84 rightSamples.push(bufR.peek(1, pos, 1)); 85 } 86 87 // Advance read position 88 readPos = (readPos + CHUNK_SIZE) % BUFFER_SIZE; 89 90 // Format as JSON arrays 91 var leftStr = "[" + leftSamples.join(",") + "]"; 92 var rightStr = "[" + rightSamples.join(",") + "]"; 93 94 // Output executejavascript command 95 outlet(0, "executejavascript", "window.acSampleBatch&&window.acSampleBatch(" + leftStr + "," + rightStr + ")"); 96} 97 98// Set chunk size 99function chunksize(n) { 100 if (n > 0 && n <= 4096) { 101 CHUNK_SIZE = n; 102 post("Chunk size set to " + n + "\n"); 103 } 104} 105 106// Set buffer names 107function buffers(nameL, nameR) { 108 bufferNameL = nameL; 109 bufferNameR = nameR; 110 initBuffers(); 111}