// title: Sound file chunks: Workaround for single-precision buffer indexing // author: jamshark70 // description: // One of the common questions on the list is about handling very large sound files with BufRd. BufRd indexes into the buffer using a single-precision float, which supports buffers only up to 2**24 frames (a bit more than six minutes at 44.1 kHz). // // This approach loads sequential chunks of a sound file into buffers that are within reach of BufRd. Then, the SynthDef can advance from one buffer to the next during playback. // // Each buffer holds one extra sample, to try to give BufRd's linear interpolation enough information to work with. There may or may not be an off-by-one bug here. If you find a problem, let me know. // code: ( ~loadChunks = { |server, path, startFrame = 0, numFrames = -1, action, chunkSize = 1048576| var sf, numCh, bufs, incr = 0; sf = SoundFile.openRead(path); if(sf.isOpen) { numCh = sf.numChannels; if(numFrames < 0) { numFrames = sf.numFrames - startFrame; } { numFrames = min(numFrames, sf.numFrames - startFrame); }; sf.close; bufs = Buffer.allocConsecutive((numFrames / (chunkSize - 1)).roundUp, server, chunkSize, numCh); { server.sync; bufs.do { |buf, i| buf.read(path, startFrame + incr, chunkSize, 0); server.sync; incr = incr + chunkSize - 1; // overlap 1 sample for interpolation }; action.value(bufs); }.fork(AppClock); bufs } { "% could not be opened.".format(path).warn; }; }; ) b = ~loadChunks.value(s, "your-file.aiff", action: { "done".postln }); ( SynthDef(\playchunks, { |out = 0, bufnum, numbufs = 1, rate = 1, amp = 0.1| var phaseTrigFb = LocalIn.ar(1), frames = BufFrames.kr(bufnum), phase = Phasor.ar(phaseTrigFb, rate, 1, frames + 1000, 1), phaseTrig = phase >= (frames - 1), bufOffset = PulseCount.ar(phaseTrig), sig = BufRd.ar(2, bufnum + bufOffset, phase, loop: 0); FreeSelf.kr(bufOffset >= numbufs); LocalOut.ar(phaseTrig); Out.ar(out, sig * amp); }).add; ) a = Synth(\playchunks, [bufnum: b.first, numbufs: b.size, amp: 1]); a.free; b.do(_.free);