4 // don't use the setter methods for the vars below
5 // they're private and have no effect on the server
6 var <server, <bufnum, <>numFrames, <>numChannels, <>sampleRate;
9 *initClass { serverCaches = IdentityDictionary.new }
12 *new { arg server, numFrames, numChannels, bufnum;
13 server = server ? Server.default;
14 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
16 Error("No more buffer numbers -- free some buffers before allocating more.").throw
18 ^super.newCopyArgs(server,
21 numChannels).sampleRate_(server.sampleRate).cache;
24 *alloc { arg server, numFrames, numChannels = 1, completionMessage, bufnum;
25 server = server ? Server.default;
26 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
28 Error("No more buffer numbers -- free some buffers before allocating more.").throw
30 ^super.newCopyArgs(server,
34 .alloc(completionMessage).sampleRate_(server.sampleRate).cache;
37 *allocConsecutive { |numBufs = 1, server, numFrames, numChannels = 1, completionMessage,
40 bufBase = bufnum ?? { server.bufferAllocator.alloc(numBufs) };
42 Error("No block of % consecutive buffer numbers is available.".format(numBufs)).throw
44 ^Array.fill(numBufs, { |i|
45 newBuf = Buffer.new(server, numFrames, numChannels, i + bufBase);
46 server.sendMsg(\b_alloc, i + bufBase, numFrames, numChannels,
47 completionMessage.value(newBuf, i));
52 alloc { arg completionMessage;
53 server.listSendMsg( this.allocMsg(completionMessage) )
55 allocRead { arg argpath,startFrame = 0,numFrames = -1, completionMessage;
57 server.listSendMsg(this.allocReadMsg( argpath,startFrame,numFrames, completionMessage));
59 allocReadChannel { arg argpath,startFrame,numFrames = 0, channels = -1, completionMessage;
61 server.listSendMsg(this.allocReadChannelMsg( argpath,startFrame,numFrames, channels,
64 allocMsg { arg completionMessage;
66 ^["/b_alloc", bufnum, numFrames.asInt, numChannels, completionMessage.value(this)]
68 allocReadMsg { arg argpath,startFrame = 0,numFrames = -1, completionMessage;
71 ^["/b_allocRead",bufnum, path,startFrame,(numFrames ? -1).asInt, completionMessage.value(this)]
73 allocReadChannelMsg { arg argpath,startFrame = 0,numFrames = -1, channels, completionMessage;
76 ^["/b_allocReadChannel",bufnum, path,startFrame, (numFrames ? -1).asInt] ++ channels ++ [completionMessage.value(this)]
79 // read whole file into memory for PlayBuf etc.
80 // adds a query as a completion message
81 *read { arg server,path,startFrame = 0,numFrames = -1, action, bufnum;
82 server = server ? Server.default;
83 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
85 Error("No more buffer numbers -- free some buffers before allocating more.").throw
87 ^super.newCopyArgs(server, bufnum)
88 .doOnInfo_(action).cache
89 .allocRead(path,startFrame,numFrames,{|buf|["/b_query",buf.bufnum]});
91 read { arg argpath, fileStartFrame = 0, numFrames = -1,
92 bufStartFrame = 0, leaveOpen = false, action;
96 this.readMsg(argpath,fileStartFrame,numFrames,bufStartFrame,
97 leaveOpen,{|buf|["/b_query",buf.bufnum]} )
100 *readChannel { arg server,path,startFrame = 0,numFrames = -1, channels, action, bufnum;
101 server = server ? Server.default;
102 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
104 Error("No more buffer numbers -- free some buffers before allocating more.").throw
106 ^super.newCopyArgs(server, bufnum)
107 .doOnInfo_(action).cache
108 .allocReadChannel(path,startFrame,numFrames,channels,
109 {|buf|["/b_query",buf.bufnum]});
111 readChannel { arg argpath, fileStartFrame = 0, numFrames = -1,
112 bufStartFrame = 0, leaveOpen = false, channels, action;
116 this.readChannelMsg(argpath,fileStartFrame,numFrames,bufStartFrame,
117 leaveOpen,channels,{|buf|["/b_query",buf.bufnum]} )
120 *readNoUpdate { arg server,path,startFrame = 0,numFrames = -1, bufnum, completionMessage;
121 server = server ? Server.default;
122 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
124 Error("No more buffer numbers -- free some buffers before allocating more.").throw
126 ^super.newCopyArgs(server, bufnum)
127 .allocRead(path,startFrame,numFrames, completionMessage);
129 readNoUpdate { arg argpath, fileStartFrame = 0, numFrames = -1,
130 bufStartFrame = 0, leaveOpen = false, completionMessage;
132 this.readMsg(argpath,fileStartFrame,numFrames,bufStartFrame,
133 leaveOpen, completionMessage)
136 readMsg { arg argpath, fileStartFrame = 0, numFrames = -1,
137 bufStartFrame = 0, leaveOpen = false, completionMessage;
139 ^["/b_read", bufnum, path, fileStartFrame, (numFrames ? -1).asInt,
140 bufStartFrame, leaveOpen.binaryValue, completionMessage.value(this)]
141 // doesn't set my numChannels etc.
144 readChannelMsg { arg argpath, fileStartFrame = 0, numFrames = -1,
145 bufStartFrame = 0, leaveOpen = false, channels, completionMessage;
147 ^["/b_readChannel", bufnum, path, fileStartFrame, (numFrames ? -1).asInt,
148 bufStartFrame, leaveOpen.binaryValue] ++ channels ++ [completionMessage.value(this)]
149 // doesn't set my numChannels etc.
152 // preload a buffer for use with DiskIn
153 *cueSoundFile { arg server,path,startFrame = 0,numChannels= 2,
154 bufferSize=32768,completionMessage;
155 ^this.alloc(server,bufferSize,numChannels,{ arg buffer;
156 buffer.readMsg(path,startFrame,bufferSize,0,true,completionMessage)
160 cueSoundFile { arg path,startFrame,completionMessage;
163 this.cueSoundFileMsg(path,startFrame,completionMessage)
166 cueSoundFileMsg { arg path,startFrame = 0,completionMessage;
167 ^["/b_read", bufnum,path,startFrame,numFrames.asInt,0,1,completionMessage.value(this) ]
170 // transfer a collection of numbers to a buffer through a file
171 *loadCollection { arg server, collection, numChannels = 1, action;
172 var data, sndfile, path, bufnum, buffer;
173 server = server ? Server.default;
174 bufnum = server.bufferAllocator.alloc(1);
176 Error("No more buffer numbers -- free some buffers before allocating more.").throw
179 if(collection.isKindOf(RawArray).not) { collection = collection.as(FloatArray) };
180 sndfile = SoundFile.new;
181 sndfile.sampleRate = server.sampleRate;
182 sndfile.numChannels = numChannels;
183 path = PathName.tmp ++ sndfile.hash.asString;
184 if(sndfile.openWrite(path),
186 sndfile.writeData(collection);
188 ^super.newCopyArgs(server, bufnum)
189 .cache.doOnInfo_({ |buf|
190 if(File.delete(path), { buf.path = nil},
191 {("Could not delete data file:" + path).warn;});
193 }).allocRead(path, 0, -1,{|buf|["/b_query",buf.bufnum]});
195 }, {"Failed to write data".warn; ^nil}
197 }, {"cannot use loadCollection with a non-local Server".warn; ^nil});
200 loadCollection { arg collection, startFrame = 0, action;
201 var data, sndfile, path;
203 if(collection.isKindOf(RawArray).not,
204 {data = collection.collectAs({|item| item}, FloatArray)}, {data = collection;}
206 if ( collection.size > ((numFrames - startFrame) * numChannels),
207 { "Collection larger than available number of Frames".warn });
208 sndfile = SoundFile.new;
209 sndfile.sampleRate = server.sampleRate;
210 sndfile.numChannels = numChannels;
211 path = PathName.tmp ++ sndfile.hash.asString;
212 if(sndfile.openWrite(path),
214 sndfile.writeData(data);
216 this.read(path, bufStartFrame: startFrame, action: { |buf|
217 if(File.delete(path), { buf.path = nil },
218 {("Could not delete data file:" + path).warn;});
222 }, {"Failed to write data".warn;}
224 }, {"cannot do fromCollection with a non-local Server".warn;});
227 // send a Collection to a buffer one UDP sized packet at a time
228 *sendCollection { arg server, collection, numChannels = 1, wait = -1, action;
229 var buffer = this.new(server, ceil(collection.size / numChannels), numChannels);
233 buffer.sendCollection(collection, 0, wait, action);
238 sendCollection { arg collection, startFrame = 0, wait = -1, action;
239 var collstream, collsize, bundsize;
241 if(collection.isSequenceableCollection
242 and: { startFrame.isNumber and: { wait.isNumber } }) {
243 collstream = CollStream.new;
244 collstream.collection = collection;
245 collsize = collection.size;
247 if ( collsize > ((numFrames - startFrame) * numChannels),
248 { "Collection larger than available number of Frames".warn });
250 this.streamCollection(collstream, collsize, startFrame * numChannels, wait, action);
252 MethodError("Invalid arguments to Buffer:sendCollection", this).throw;
257 streamCollection { arg collstream, collsize, startFrame = 0, wait = -1, action;
260 // wait = -1 allows an OSC roundtrip between packets
261 // wait = 0 might not be safe in a high traffic situation
262 // maybe okay with tcp
263 pos = collstream.pos;
264 while { pos < collsize } {
265 // 1626 max size for setn under udp
266 bundsize = min(1626, collsize - pos);
267 server.listSendMsg(['/b_setn', bufnum, pos + startFrame, bundsize]
268 ++ Array.fill(bundsize, { collstream.next }));
269 pos = collstream.pos;
270 if(wait >= 0) { wait.wait } { server.sync };
278 // these next two get the data and put it in a float array which is passed to action
280 loadToFloatArray { arg index = 0, count = -1, action;
281 var msg, cond, path, file, array;
283 path = PathName.tmp ++ this.hash.asString;
284 msg = this.write(path, "aiff", "float", count, index);
286 file = SoundFile.new;
289 array = FloatArray.newClear(file.numFrames * file.numChannels);
290 file.readData(array);
293 if(File.delete(path).not) { ("Could not delete data file:" + path).warn };
296 action.value(array, this);
301 // risky without wait
302 getToFloatArray { arg index = 0, count, wait = 0.01, timeout = 3, action;
303 var refcount, array, pos, getsize, resp, done = false;
304 pos = index = index.asInteger;
305 count = (count ? (numFrames * numChannels)).asInteger;
306 array = FloatArray.newClear(count);
307 refcount = (count / 1633).roundUp;
309 //("refcount" + refcount).postln;
310 resp = OSCFunc({ arg msg;
311 if(msg[1] == bufnum, {
312 //("received" + msg).postln;
313 array = array.overWrite(FloatArray.newFrom(msg.copyToEnd(4)), msg[2] - index);
314 refcount = refcount - 1;
315 //("countDown" + refcount).postln;
316 if(refcount <= 0, {done = true; resp.clear; action.value(array, this); });
318 }, '/b_setn', server.addr);
320 while({pos < count}, {
321 // 1633 max size for getn under udp
322 getsize = min(1633, count - pos);
323 //("sending from" + pos).postln;
324 server.listSendMsg(this.getnMsg(pos, getsize));
326 if(wait >= 0) { wait.wait } { server.sync };
330 // lose the responder if the network choked
331 SystemClock.sched(timeout,
332 { done.not.if({ resp.free; "Buffer-streamToFloatArray failed!".warn;
333 "Try increasing wait time".postln;});
337 write { arg path, headerFormat = "aiff", sampleFormat = "int24", numFrames = -1,
338 startFrame = 0,leaveOpen = false, completionMessage;
339 path = path ?? { thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ headerFormat };
341 this.writeMsg(path, headerFormat, sampleFormat, numFrames, startFrame,
342 leaveOpen, completionMessage)
345 writeMsg { arg path,headerFormat="aiff",sampleFormat="int24",numFrames = -1,
346 startFrame = 0,leaveOpen = false, completionMessage;
347 // doesn't change my path
348 ^["/b_write", bufnum, path,
349 headerFormat,sampleFormat, numFrames, startFrame,
350 leaveOpen.binaryValue, completionMessage.value(this)];
351 // writeEnabled = true;
354 free { arg completionMessage;
355 server.listSendMsg( this.freeMsg(completionMessage) );
357 freeMsg { arg completionMessage;
359 server.bufferAllocator.free(bufnum);
360 ^["/b_free", bufnum, completionMessage.value(this)];
362 *freeAll { arg server;
364 server = server ? Server.default;
365 server.bufferAllocator.blocks.do({ arg block;
366 (block.address .. block.address + block.size - 1).do({ |i|
367 b = b.add( ["/b_free", i] );
369 server.bufferAllocator.free(block.address);
371 server.sendBundle(nil, *b);
372 this.clearServerCaches(server);
374 zero { arg completionMessage;
375 server.listSendMsg(this.zeroMsg(completionMessage));
377 zeroMsg { arg completionMessage;
378 ^["/b_zero", bufnum , completionMessage.value(this) ]
381 set { arg index,float ... morePairs;
382 server.listSendMsg(["/b_set",bufnum,index,float] ++ morePairs);
384 setMsg { arg index,float ... morePairs;
385 ^["/b_set",bufnum,index,float] ++ morePairs;
389 server.sendMsg(*this.setnMsg(*args));
391 setnMsgArgs{arg ... args;
394 args.pairsDo{ arg control, moreVals;
395 if(moreVals.isArray,{
396 nargs.addAll([control, moreVals.size]++ moreVals)
398 nargs.addAll([control, 1, moreVals]);
403 setnMsg { arg ... args;
404 ^["/b_setn",bufnum] ++ this.setnMsgArgs(*args);
406 get { arg index, action;
407 OSCpathResponder(server.addr,['/b_set',bufnum,index],{ arg time, r, msg;
408 action.value(msg.at(3)); r.remove }).add;
409 server.listSendMsg(["/b_get",bufnum,index]);
412 ^["/b_get",bufnum,index];
414 getn { arg index, count, action;
415 OSCpathResponder(server.addr,['/b_setn',bufnum,index],{arg time, r, msg;
416 action.value(msg.copyToEnd(4)); r.remove } ).add;
417 server.listSendMsg(["/b_getn",bufnum,index, count]);
419 getnMsg { arg index, count;
420 ^["/b_getn",bufnum,index, count];
424 fill { arg startAt,numFrames,value ... more;
425 server.listSendMsg(["/b_fill",bufnum,startAt,numFrames.asInt,value]
428 fillMsg { arg startAt,numFrames,value ... more;
429 ^["/b_fill",bufnum,startAt,numFrames.asInt,value]
433 normalize { arg newmax=1, asWavetable=false;
434 server.listSendMsg(["/b_gen",bufnum,if(asWavetable, "wnormalize", "normalize"),newmax]);
436 normalizeMsg { arg newmax=1, asWavetable=false;
437 ^["/b_gen",bufnum,if(asWavetable, "wnormalize", "normalize"),newmax];
440 gen { arg genCommand, genArgs, normalize=true,asWavetable=true,clearFirst=true;
441 server.listSendMsg(["/b_gen",bufnum,genCommand,
442 normalize.binaryValue
443 + (asWavetable.binaryValue * 2)
444 + (clearFirst.binaryValue * 4)]
447 genMsg { arg genCommand, genArgs, normalize=true,asWavetable=true,clearFirst=true;
448 ^["/b_gen",bufnum,genCommand,
449 normalize.binaryValue
450 + (asWavetable.binaryValue * 2)
451 + (clearFirst.binaryValue * 4)]
454 sine1 { arg amps,normalize=true,asWavetable=true,clearFirst=true;
455 server.listSendMsg(["/b_gen",bufnum,"sine1",
456 normalize.binaryValue
457 + (asWavetable.binaryValue * 2)
458 + (clearFirst.binaryValue * 4)]
461 sine2 { arg freqs, amps,normalize=true,asWavetable=true,clearFirst=true;
462 server.listSendMsg(["/b_gen",bufnum,"sine2",
463 normalize.binaryValue
464 + (asWavetable.binaryValue * 2)
465 + (clearFirst.binaryValue * 4)]
466 ++ [freqs, amps].lace(freqs.size * 2))
468 sine3 { arg freqs, amps, phases,normalize=true,asWavetable=true,clearFirst=true;
469 server.listSendMsg(["/b_gen",bufnum,"sine3",
470 normalize.binaryValue
471 + (asWavetable.binaryValue * 2)
472 + (clearFirst.binaryValue * 4)]
473 ++ [freqs, amps, phases].lace(freqs.size * 3))
475 cheby { arg amplitudes,normalize=true,asWavetable=true,clearFirst=true;
476 server.listSendMsg(["/b_gen",bufnum,"cheby",
477 normalize.binaryValue
478 + (asWavetable.binaryValue * 2)
479 + (clearFirst.binaryValue * 4)]
482 sine1Msg { arg amps,normalize=true,asWavetable=true,clearFirst=true;
483 ^["/b_gen",bufnum,"sine1",
484 normalize.binaryValue
485 + (asWavetable.binaryValue * 2)
486 + (clearFirst.binaryValue * 4)]
489 sine2Msg { arg freqs, amps,normalize=true,asWavetable=true,clearFirst=true;
490 ^["/b_gen",bufnum,"sine2",
491 normalize.binaryValue
492 + (asWavetable.binaryValue * 2)
493 + (clearFirst.binaryValue * 4)]
494 ++ [freqs, amps].lace(freqs.size * 2)
496 sine3Msg { arg freqs, amps, phases,normalize=true,asWavetable=true,clearFirst=true;
497 ^["/b_gen",bufnum,"sine3",
498 normalize.binaryValue
499 + (asWavetable.binaryValue * 2)
500 + (clearFirst.binaryValue * 4)]
501 ++ [freqs, amps, phases].lace(freqs.size * 3)
503 chebyMsg { arg amplitudes,normalize=true,asWavetable=true,clearFirst=true;
504 ^["/b_gen",bufnum,"cheby",
505 normalize.binaryValue
506 + (asWavetable.binaryValue * 2)
507 + (clearFirst.binaryValue * 4)]
511 copyData { arg buf, dstStartAt = 0, srcStartAt = 0, numSamples = -1;
513 this.copyMsg(buf, dstStartAt, srcStartAt, numSamples)
516 copyMsg { arg buf, dstStartAt = 0, srcStartAt = 0, numSamples = -1;
517 ^["/b_gen", buf.bufnum, "copy", dstStartAt, bufnum, srcStartAt, numSamples]
520 // close a file, write header, after DiskOut usage
521 close { arg completionMessage;
522 server.listSendMsg( this.closeMsg(completionMessage) );
524 closeMsg { arg completionMessage;
525 ^["/b_close", bufnum, completionMessage.value(this) ];
530 Post << "bufnum :" << msg[1] << Char.nl
531 << "numFrames : " << msg[2] << Char.nl
532 << "numChannels : " << msg[3] << Char.nl
533 << "sampleRate :" << msg[4] << Char.nl << Char.nl;
534 }, '/b_info', server.addr).oneShot;
535 server.sendMsg("/b_query",bufnum)
538 updateInfo { |action|
539 // add to the array here. That way, update will be accurate even if this buf
543 server.sendMsg("/b_query", bufnum);
546 // cache Buffers for easy info updating
548 Buffer.initServerCache(server);
549 serverCaches[server][bufnum] = this;
552 if(serverCaches[server].notNil,{
553 serverCaches[server].removeAt(bufnum);
555 if(serverCaches[server].size == 1) {
556 // the 1 item would be the responder
557 // if there is more than 1 item then the rest are cached buffers
558 // else we can remove.
559 // cx: tho i don't see why its important. it will just have to be added
560 // back when the next buffer is added and the responder is removed when
561 // the server reboots
562 Buffer.clearServerCaches(server);
565 *initServerCache { |server|
566 serverCaches[server] ?? {
567 serverCaches[server] = IdentityDictionary.new;
568 serverCaches[server][\responder] = OSCFunc({ |m|
569 var buffer = serverCaches[server][m[1]];
571 buffer.numFrames = m[2];
572 buffer.numChannels = m[3];
573 buffer.sampleRate = m[4];
576 }, '/b_info', server.addr).fix;
577 NotificationCenter.register(server,\newAllocators,this,{
578 this.clearServerCaches(server);
582 *clearServerCaches { |server|
583 if(serverCaches[server].notNil) {
584 serverCaches[server][\responder].free;
585 serverCaches.removeAt(server);
588 *cachedBuffersDo { |server, func|
590 serverCaches[server] !? {
591 serverCaches[server].keysValuesDo({ |key, value|
592 if(key.isNumber) { func.value(value, i); i = i + 1 };
596 *cachedBufferAt { |server, bufnum|
597 ^serverCaches[server].tryPerform(\at, bufnum)
600 // called from Server when b_info is received
602 doOnInfo.value(this);
606 printOn { arg stream;
607 stream << this.class.name << "(" <<* [bufnum,numFrames,numChannels,sampleRate,path] <<")";
610 *loadDialog { arg server,startFrame = 0,numFrames, action, bufnum;
612 server = server ? Server.default;
613 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
615 Error("No more buffer numbers -- free some buffers before allocating more.").throw
617 buffer = super.newCopyArgs(server, bufnum).cache;
618 File.openDialog("Select a file...",{ arg path;
619 buffer.doOnInfo_(action)
620 .allocRead(path,startFrame,numFrames, {["/b_query",buffer.bufnum]})
625 play { arg loop = false, mul = 1;
627 player = PlayBuf.ar(numChannels,bufnum,BufRateScale.kr(bufnum),
628 loop: loop.binaryValue);
629 loop.not.if(FreeSelfWhenDone.kr(player));
634 duration { ^numFrames / sampleRate }
636 asUGenInput { ^this.bufnum }
637 asControlInput { ^this.bufnum }