spatialaudio / jackclient-python

🂻 JACK Audio Connection Kit (JACK) Client for Python :snake:
https://jackclient-python.readthedocs.io/
MIT License
131 stars 26 forks source link

"operands could not be broadcast together with shapes" error playing sound #88

Closed manucontrovento closed 3 years ago

manucontrovento commented 4 years ago

Hello everyone and thanks for this amazing project!

I'm trying to play a wave sound. I'm adding some code in an application which I didn't start from scratch, so it's quite possible I'm not understanding what I have to do!

I want to have a "metronome", so first I load the 2 metronome sounds using soundfile module:

mDataE, mSamplerateE = sf.read("/home/.../click-emphasis.wav", dtype=np.float32) mData, mSamplerate = sf.read("/home/.../click.wav", dtype=np.float32)

then in Jack callback function, I check if beat has changed and I play the sound, routing it to the dedicated metronome jack out channels. output_buffers is an array of jack client .outports, metronome_left_channel and metronome_right_channel names of the 2 dedicated channels, and also previous_beat is defined out of callback function:

     # METRONOME: when Jack transport gets into new beat, metronome sound is played
     if state == 1 and 'beat' in position: 

         current_beat = position['beat']
         if current_beat != previous_beat:

             if current_beat == 1:  # Emphasis

                 output_buffers[metronome_left_channel][:] += mDataE[:]
                 output_buffers[metronome_right_channel][:] += mDataE[:]

             else:

                 output_buffers[metronome_left_channel][:] += mData[:]
                 output_buffers[metronome_right_channel][:] += mData[:]

             previous_beat = current_beat

when I hit Play, I get this error:

output_buffers[metronome_left_channel][:] += mDataE[:] ValueError: operands could not be broadcast together with shapes (1024,) (33412,2) (1024,)

it's first time I try to play a sound, so I'm missing something for sure, but I don't urderstand what..

thanks!

Manu

HaHeho commented 4 years ago

You're being pointed at that your data arrays (time domain samples) are not compatible. You are trying add to a block of 1024 samples (according to your chosen JACK processing block size). You will have to select an appropriate block out of the read click audio files. It would be best practice to do that directly after read() in dependence to the current block size. (Remark: If the actual click is longer than the block size, you are in quite some trouble and will have to chose a more sophisticated approach to implement your playback at this point.)

If the data sizes match you should be able to the add operation as you intended. In that case you should not necessarily distinguish the two audio channels (for performance reasons). Since your audio files seem to be Stereo and you want to assign two outputs, this can be done directly. At the end, the line should like somewhat like this:

# TODO: acquire JACK client block length here
LEN = 1024;

mData, mSamplerate = sf.read("/home/.../click.wav", dtype=np.float32)
# TODO: validate match of sampling frequency with JACK client here

# truncate and transpose to match JACK client output data structure
mData = mData[:LEN,:].T.copy(); # copy creates an optimal memory alignment

# ...
output_buffers[:] += mData

Another remark: I am not sure how you planned to incorporate to modify the actual metronome tick rate. I am afraid with this simple approach this will not be possible. You would be stuck with multiples of the block size, where the smallest possible is maybe something like 128 samples depending on the system.

manucontrovento commented 4 years ago

Hi @HaHeho thanks a lot. I'll try. Another question: what do you mean with "chose a more sophisticated approach"? I'm quite new to Python and to audio programming both; which would be a more appropriate approach? thanks! bye Manu

HaHeho commented 4 years ago

For playback of audio a file will have to be segmented into appropriate chunks (block size) and delivered to the JACK output port in a timely manner. The play_file example shows how to do that in a "simple" fashion. Unfortunately, this is everything but trivial with JACK due to its focus on control to the developer and performance. Therefore, even extending this example with some play, pause or loop behavior is kind of tough ... in particular for a beginner. :/

There is probably a conceptually better approach on realizing your desired metronome behavior when utilizing the audio files. I would suggest that you start with building your application without the files first (since this is probably the most complicated part). You could realize audible clicks for example by a Dirac impulse. Theoretically this would mean to set the first sample of the block to one, although this should be done very cautiously (i.e. don't destroy your ears or equipment !!). Maybe multiplying the first 4 samples by 4 or something is a good idea to begin experimenting with. Thereby, also make sure that the output value does not exceed one in order to prevent clipping.

mgeier commented 3 years ago

@manucontrovento Any news?

If you just need a metronome for JACK, you can also try http://das.nasophon.de/klick/.

And out of curiosity: where did you get your "click" sounds? Did you generate them yourself?

manucontrovento commented 3 years ago

Hi @mgeier , since I got not much time to spend on develping at now, I abandoned the idea of a metronome. Thanks about the klick link, I already found it and I will consider to include it. I took the click sounds from a library I had somewhere, but I tried with several sounds. bye Manu

mgeier commented 3 years ago

OK, thanks for the update!

I'll close this for now, bu we can re-open later in case you want continue working on this.