Closed Lawmate closed 4 years ago
Python can only do one thing at a time. The play
function in particular only returns when the sound is (almost) finished playing. Thus a multiple calls to play
play one after another.
In your case, you can play to all channels simultaneously by passing all four channels to one play
call.
I see, thanks for the reply.
for anyone else in the same position, I did the following: Read 4 files into separate arrays. Each channel is a mono WAV file
dataBMC1, sampleratechBMC1 = sf.read(fileBMCfr)
dataBMC2, sampleratechBMC2 = sf.read(fileBMCfl)
dataBMC3, sampleratechBMC3 = sf.read(fileBMCrr)
dataBMC4, sampleratechBMC4 = sf.read(fileBMCrl)
Then I combined the 4 arrays, rotated the array through 270 degrees, then flipped it vertically: (There's probably a better way to do this, but it worked fine for me)
dataBMCFull = np.flip(np.rot90(np.array([dataBMC1,dataBMC2,dataBMC3,dataBMC4]),3),1)
My files are quite large so I did the following to speed things up by getting rid of used variables
del dataBMC1, dataBMC2, dataBMC3, dataBMC4
gc.collect()
then you can use the play function
with speakers.player(samplerate=sampleratechBMC1, channels=[0,1,2,3] ) as sp:
sp.play(dataBMCFull)
It takes a while to load up for large files but will play smoothly and without slowing down
Two tips:
You can probably replace np.flip(np.rot90(data))
with data.T
, which is a shorthand for np.transpose(data)
.
Also, you might want to loop through short blocks of data instead of one big chunk, i.e.
idx = 0
blocklen = 1024 # just an example length
with speakers.player(...) as sp:
while idx + blocklen < len(dataBMC1):
data = np.array([dataBMC1[idx:idx+blocklen], dataBMC2[idx:idx+blocklen],
dataBMC3[idx:idx+blocklen], dataBMC4[idx:idx+blocklen]]).T
sp.play(data)
A better way is to elaborate the new nSamples x nChannels array before the while loop, which avoids memory allocation during audio enqueuing
idx = 0
# horizontal stack of arrays
data = np.hstack(dataBMC1, dataBMC2,
dataBMC3, dataBMC4)
blocksize = 1024 # just an example length
with speakers.player(...) as sp:
while idx < len(dataBMC1):
sp.play(data[idx : idx + blocksize])
idx += blocksize
If you want to avoid the allocation, it would be better to preallocate one block, and copy data to the block as needed. Pre-allocating all blocks in advance can become extremely memory-intensive very quickly.
Hi
Firstly, I'm pretty new to using python, so sorry for any obvious mistakes. I am trying to play 4 files simultaneously from my Mac, each going to a different channel on an audio interface (Motu 828ES). When I run
speakers.play(datafr,sampleratefr,[0],0) speakers.play(datafl,sampleratefl,[1],0) speakers.play(datarr,sampleraterr,[2],0) speakers.play(datarl,sampleraterl,[3],0)
each file is played sequentially. They are played through the correct channel, but not at the same time. I have also tried using multiprocessing with the following code ` def func1(): speakers.play(datafr,sampleratefr,[0],0) def func2(): speakers.play(datafl,sampleratefl,[1],0) def func3(): speakers.play(datarr,sampleraterr,[2],0) def func4(): speakers.play(datarl,sampleraterl,[3],0)
for x in range(1): if name == 'main': p1 = Process(target=func1) p2 = Process(target=func2) p3 = Process(target=func3) p3 = Process(target=func4) p1.start() p2.start() p3.start() p4.start() p1.join() p2.join() p3.join() p4.join()`
but while this plays together, there is around 0.2s delay between the different channels.
I am simply playing the sound files, no recording or other processing, so I'm not too sure where the delay latency is coming from.
Thanks