Monorepo for Aesthetic.Computer
aesthetic.computer
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}