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 = 0.0, 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 collstream = CollStream.new;
242 collstream.collection = collection;
243 collsize = collection.size;
245 if ( collsize > ((numFrames - startFrame) * numChannels),
246 { "Collection larger than available number of Frames".warn });
248 this.streamCollection(collstream, collsize, startFrame * numChannels, wait, action);
252 streamCollection { arg collstream, collsize, startFrame = 0, wait = -1, action;
255 // wait = -1 sends each packet when the previous has arrived
256 // wait = 0 might not be safe in a high traffic situation
257 // maybe okay with tcp
258 pos = collstream.pos;
259 while { pos < collsize } {
260 // 1626 max size for setn under udp
261 bundsize = min(1626, collsize - pos);
262 server.listSendMsg(['/b_setn', bufnum, pos + startFrame, bundsize]
263 ++ Array.fill(bundsize, { collstream.next }));
264 pos = collstream.pos;
265 if(wait >= 0) { wait.wait } { server.sync };
273 // these next two get the data and put it in a float array which is passed to action
275 loadToFloatArray { arg index = 0, count = -1, action;
276 var msg, cond, path, file, array;
278 path = PathName.tmp ++ this.hash.asString;
279 msg = this.write(path, "aiff", "float", count, index);
281 file = SoundFile.new;
284 array = FloatArray.newClear(file.numFrames * file.numChannels);
285 file.readData(array);
288 if(File.delete(path).not) { ("Could not delete data file:" + path).warn };
291 action.value(array, this);
296 // risky without wait
297 getToFloatArray { arg index = 0, count, wait = 0.01, timeout = 3, action;
298 var refcount, array, pos, getsize, resp, done = false;
299 pos = index = index.asInteger;
300 count = (count ? (numFrames * numChannels)).asInteger;
301 array = FloatArray.newClear(count);
302 refcount = (count / 1633).roundUp;
304 //("refcount" + refcount).postln;
305 resp = OSCFunc({ arg msg;
306 if(msg[1] == bufnum, {
307 //("received" + msg).postln;
308 array = array.overWrite(FloatArray.newFrom(msg.copyToEnd(4)), msg[2] - index);
309 refcount = refcount - 1;
310 //("countDown" + refcount).postln;
311 if(refcount <= 0, {done = true; resp.clear; action.value(array, this); });
313 }, '/b_setn', server.addr);
315 while({pos < count}, {
316 // 1633 max size for getn under udp
317 getsize = min(1633, count - pos);
318 //("sending from" + pos).postln;
319 server.listSendMsg(this.getnMsg(pos, getsize));
321 if(wait >= 0) { wait.wait } { server.sync };
325 // lose the responder if the network choked
326 SystemClock.sched(timeout,
327 { done.not.if({ resp.free; "Buffer-streamToFloatArray failed!".warn;
328 "Try increasing wait time".postln;});
332 write { arg path, headerFormat = "aiff", sampleFormat = "int24", numFrames = -1,
333 startFrame = 0,leaveOpen = false, completionMessage;
334 path = path ?? { thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ headerFormat };
336 this.writeMsg(path, headerFormat, sampleFormat, numFrames, startFrame,
337 leaveOpen, completionMessage)
340 writeMsg { arg path,headerFormat="aiff",sampleFormat="int24",numFrames = -1,
341 startFrame = 0,leaveOpen = false, completionMessage;
342 // doesn't change my path
343 ^["/b_write", bufnum, path,
344 headerFormat,sampleFormat, numFrames, startFrame,
345 leaveOpen.binaryValue, completionMessage.value(this)];
346 // writeEnabled = true;
349 free { arg completionMessage;
350 server.listSendMsg( this.freeMsg(completionMessage) );
352 freeMsg { arg completionMessage;
354 server.bufferAllocator.free(bufnum);
355 ^["/b_free", bufnum, completionMessage.value(this)];
357 *freeAll { arg server;
359 server = server ? Server.default;
360 server.bufferAllocator.blocks.do({ arg block;
361 (block.address .. block.address + block.size - 1).do({ |i|
362 b = b.add( ["/b_free", i] );
364 server.bufferAllocator.free(block.address);
366 server.sendBundle(nil, *b);
367 this.clearServerCaches(server);
369 zero { arg completionMessage;
370 server.listSendMsg(this.zeroMsg(completionMessage));
372 zeroMsg { arg completionMessage;
373 ^["/b_zero", bufnum , completionMessage.value(this) ]
376 set { arg index,float ... morePairs;
377 server.listSendMsg(["/b_set",bufnum,index,float] ++ morePairs);
379 setMsg { arg index,float ... morePairs;
380 ^["/b_set",bufnum,index,float] ++ morePairs;
384 server.sendMsg(*this.setnMsg(*args));
386 setnMsgArgs{arg ... args;
389 args.pairsDo{ arg control, moreVals;
390 if(moreVals.isArray,{
391 nargs.addAll([control, moreVals.size]++ moreVals)
393 nargs.addAll([control, 1, moreVals]);
398 setnMsg { arg ... args;
399 ^["/b_setn",bufnum] ++ this.setnMsgArgs(*args);
401 get { arg index, action;
402 OSCpathResponder(server.addr,['/b_set',bufnum,index],{ arg time, r, msg;
403 action.value(msg.at(3)); r.remove }).add;
404 server.listSendMsg(["/b_get",bufnum,index]);
407 ^["/b_get",bufnum,index];
409 getn { arg index, count, action;
410 OSCpathResponder(server.addr,['/b_setn',bufnum,index],{arg time, r, msg;
411 action.value(msg.copyToEnd(4)); r.remove } ).add;
412 server.listSendMsg(["/b_getn",bufnum,index, count]);
414 getnMsg { arg index, count;
415 ^["/b_getn",bufnum,index, count];
419 fill { arg startAt,numFrames,value ... more;
420 server.listSendMsg(["/b_fill",bufnum,startAt,numFrames.asInt,value]
423 fillMsg { arg startAt,numFrames,value ... more;
424 ^["/b_fill",bufnum,startAt,numFrames.asInt,value]
428 normalize { arg newmax=1, asWavetable=false;
429 server.listSendMsg(["/b_gen",bufnum,if(asWavetable, "wnormalize", "normalize"),newmax]);
431 normalizeMsg { arg newmax=1, asWavetable=false;
432 ^["/b_gen",bufnum,if(asWavetable, "wnormalize", "normalize"),newmax];
435 gen { arg genCommand, genArgs, normalize=true,asWavetable=true,clearFirst=true;
436 server.listSendMsg(["/b_gen",bufnum,genCommand,
437 normalize.binaryValue
438 + (asWavetable.binaryValue * 2)
439 + (clearFirst.binaryValue * 4)]
442 genMsg { arg genCommand, genArgs, normalize=true,asWavetable=true,clearFirst=true;
443 ^["/b_gen",bufnum,genCommand,
444 normalize.binaryValue
445 + (asWavetable.binaryValue * 2)
446 + (clearFirst.binaryValue * 4)]
449 sine1 { arg amps,normalize=true,asWavetable=true,clearFirst=true;
450 server.listSendMsg(["/b_gen",bufnum,"sine1",
451 normalize.binaryValue
452 + (asWavetable.binaryValue * 2)
453 + (clearFirst.binaryValue * 4)]
456 sine2 { arg freqs, amps,normalize=true,asWavetable=true,clearFirst=true;
457 server.listSendMsg(["/b_gen",bufnum,"sine2",
458 normalize.binaryValue
459 + (asWavetable.binaryValue * 2)
460 + (clearFirst.binaryValue * 4)]
461 ++ [freqs, amps].lace(freqs.size * 2))
463 sine3 { arg freqs, amps, phases,normalize=true,asWavetable=true,clearFirst=true;
464 server.listSendMsg(["/b_gen",bufnum,"sine3",
465 normalize.binaryValue
466 + (asWavetable.binaryValue * 2)
467 + (clearFirst.binaryValue * 4)]
468 ++ [freqs, amps, phases].lace(freqs.size * 3))
470 cheby { arg amplitudes,normalize=true,asWavetable=true,clearFirst=true;
471 server.listSendMsg(["/b_gen",bufnum,"cheby",
472 normalize.binaryValue
473 + (asWavetable.binaryValue * 2)
474 + (clearFirst.binaryValue * 4)]
477 sine1Msg { arg amps,normalize=true,asWavetable=true,clearFirst=true;
478 ^["/b_gen",bufnum,"sine1",
479 normalize.binaryValue
480 + (asWavetable.binaryValue * 2)
481 + (clearFirst.binaryValue * 4)]
484 sine2Msg { arg freqs, amps,normalize=true,asWavetable=true,clearFirst=true;
485 ^["/b_gen",bufnum,"sine2",
486 normalize.binaryValue
487 + (asWavetable.binaryValue * 2)
488 + (clearFirst.binaryValue * 4)]
489 ++ [freqs, amps].lace(freqs.size * 2)
491 sine3Msg { arg freqs, amps, phases,normalize=true,asWavetable=true,clearFirst=true;
492 ^["/b_gen",bufnum,"sine3",
493 normalize.binaryValue
494 + (asWavetable.binaryValue * 2)
495 + (clearFirst.binaryValue * 4)]
496 ++ [freqs, amps, phases].lace(freqs.size * 3)
498 chebyMsg { arg amplitudes,normalize=true,asWavetable=true,clearFirst=true;
499 ^["/b_gen",bufnum,"cheby",
500 normalize.binaryValue
501 + (asWavetable.binaryValue * 2)
502 + (clearFirst.binaryValue * 4)]
506 copy { arg buf, dstStartAt = 0, srcStartAt = 0, numSamples = -1;
508 this.deprecated(thisMethod, this.class.findRespondingMethodFor(\copyData));
509 this.copyData(buf, dstStartAt, srcStartAt, numSamples);
515 copyData { arg buf, dstStartAt = 0, srcStartAt = 0, numSamples = -1;
517 this.copyMsg(buf, dstStartAt, srcStartAt, numSamples)
520 copyMsg { arg buf, dstStartAt = 0, srcStartAt = 0, numSamples = -1;
521 ^["/b_gen", buf.bufnum, "copy", dstStartAt, bufnum, srcStartAt, numSamples]
524 // close a file, write header, after DiskOut usage
525 close { arg completionMessage;
526 server.listSendMsg( this.closeMsg(completionMessage) );
528 closeMsg { arg completionMessage;
529 ^["/b_close", bufnum, completionMessage.value(this) ];
534 Post << "bufnum :" << msg[1] << Char.nl
535 << "numFrames : " << msg[2] << Char.nl
536 << "numChannels : " << msg[3] << Char.nl
537 << "sampleRate :" << msg[4] << Char.nl << Char.nl;
538 }, '/b_info', server.addr).oneShot;
539 server.sendMsg("/b_query",bufnum)
542 updateInfo { |action|
543 // add to the array here. That way, update will be accurate even if this buf
547 server.sendMsg("/b_query", bufnum);
550 // cache Buffers for easy info updating
552 Buffer.initServerCache(server);
553 serverCaches[server][bufnum] = this;
556 if(serverCaches[server].notNil,{
557 serverCaches[server].removeAt(bufnum);
559 if(serverCaches[server].size == 1) {
560 // the 1 item would be the responder
561 // if there is more than 1 item then the rest are cached buffers
562 // else we can remove.
563 // cx: tho i don't see why its important. it will just have to be added
564 // back when the next buffer is added and the responder is removed when
565 // the server reboots
566 Buffer.clearServerCaches(server);
569 *initServerCache { |server|
570 serverCaches[server] ?? {
571 serverCaches[server] = IdentityDictionary.new;
572 serverCaches[server][\responder] = OSCFunc({ |m|
573 var buffer = serverCaches[server][m[1]];
575 buffer.numFrames = m[2];
576 buffer.numChannels = m[3];
577 buffer.sampleRate = m[4];
580 }, '/b_info', server.addr).fix;
581 NotificationCenter.register(server,\newAllocators,this,{
582 this.clearServerCaches(server);
586 *clearServerCaches { |server|
587 if(serverCaches[server].notNil) {
588 serverCaches[server][\responder].free;
589 serverCaches.removeAt(server);
592 *cachedBuffersDo { |server, func|
594 serverCaches[server] !? {
595 serverCaches[server].keysValuesDo({ |key, value|
596 if(key.isNumber) { func.value(value, i); i = i + 1 };
600 *cachedBufferAt { |server, bufnum|
601 ^serverCaches[server].tryPerform(\at, bufnum)
604 // called from Server when b_info is received
606 doOnInfo.value(this);
610 printOn { arg stream;
611 stream << this.class.name << "(" <<* [bufnum,numFrames,numChannels,sampleRate,path] <<")";
614 *loadDialog { arg server,startFrame = 0,numFrames, action, bufnum;
616 server = server ? Server.default;
617 bufnum ?? { bufnum = server.bufferAllocator.alloc(1) };
619 Error("No more buffer numbers -- free some buffers before allocating more.").throw
621 buffer = super.newCopyArgs(server, bufnum).cache;
622 File.openDialog("Select a file...",{ arg path;
623 buffer.doOnInfo_(action)
624 .allocRead(path,startFrame,numFrames, {["/b_query",buffer.bufnum]})
629 play { arg loop = false, mul = 1;
631 player = PlayBuf.ar(numChannels,bufnum,BufRateScale.kr(bufnum),
632 loop: loop.binaryValue);
633 loop.not.if(FreeSelfWhenDone.kr(player));
638 duration { ^numFrames / sampleRate }
640 asUGenInput { ^this.bufnum }
641 asControlInput { ^this.bufnum }
647 *readExample {|server, startFrame = 0, numFrames = -1, action, bufnum|
648 var file = Platform.exampleSoundFile;
649 ^this.read(server, file, startFrame, numFrames, action, bufnum)