Mixing WAV Samples
bradharper
Posts: 64
I'm working on an effects engine where I need to loop one WAV file continuously, and other files, same audio specs, are triggered to play simultaneously. I've tried a few different mixing strategies, but each results in unacceptable levels of noise, or complete distortion.
Essentially, I have two event-driven Buffer objects fetching audio, via fsrw, from a microSD card, applying a volume adjustment, and then writing the samples to memory where the Mixer object examines each sample pointer, and renders the samples after dithering and mixing if necessary.
Both the Buffer objects and the Mixer are modified versions of the efx_wavdacs code from this thread:
http://forums.parallax.com/showthread.php?t=123900&highlight=audio+player
Here is the Mixer loop (spin file also attached) where I'm attempting to mix the samples.
Anyone have any suggestions about what I may be doing wrong here? I've tried the following:
1) Summing the samples.
2) Dividing each by two, then summing.
3) Summing then dividing by two.
All three create noise and/or distortion. Essentially, if I touch the sample at all (other than the dither) it's ruined. Audio is crystal clear so long as I'm only iterating over a single track buffer.
Thoughts?
-BH
Essentially, I have two event-driven Buffer objects fetching audio, via fsrw, from a microSD card, applying a volume adjustment, and then writing the samples to memory where the Mixer object examines each sample pointer, and renders the samples after dithering and mixing if necessary.
Both the Buffer objects and the Mixer are modified versions of the efx_wavdacs code from this thread:
http://forums.parallax.com/showthread.php?t=123900&highlight=audio+player
Here is the Mixer loop (spin file also attached) where I'm attempting to mix the samples.
DAT org ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' I/O Setup ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// entry or dira,dira_ 'set dira bits mov ctra,ctra_ 'set ctr's to output duty to pins 'mov ctrb,ctrb_ 'mov frqa, quiet 'mov frqb, quiet ' ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' Main Sample Loop ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// :loop rdbyte mute_flag, mute_flag_pntr cmp mute_flag, #1 wz if_z mov frqa, quiet 'if_z mov frqb, quiet if_z jmp #:loop ' /////////// Track 1 Reader :tr1 rdlong tr1_lsample,tr1_pntr wz 'read samples 'if_z jmp #:loop 'add to_mix, #1 xor tr1_lsample,h80008000 'convert signed samples to unsigned mov tr1_rsample,tr1_lsample 'split and msb-justify them shl tr1_lsample,#16 and tr1_rsample,hFFFF0000 'add lsample_sum, tr1_lsample 'add rsample_sum, tr1_rsample ' /////////// Track 2 Reader :tr2 rdlong tr2_lsample,tr2_pntr wz 'if_z jmp #:out 'add to_mix, #1 xor tr2_lsample,h80008000 'convert signed samples to unsigned mov tr2_rsample,tr2_lsample 'split and msb-justify them shl tr2_lsample,#16 and tr2_rsample,hFFFF0000 'add lsample_sum, tr2_lsample 'add rsample_sum, tr2_rsample ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' Mix Samples ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// :mix 'add tr1_lsample,tr2_lsample 'add samples 'add rsample_sum,dither 'sar tr1_lsample, #1 ' signed divide by 2 'sar tr2_lsample, #1 ' signed divide by 2 adds tr1_lsample, tr2_lsample ' add together ''sar tr1_lsample, #1 ' signed divide by 2 'test lfsr,taps wc 'iterate noise generator 'rcl lfsr,#1 'mov dither,lfsr 'make dither from noise 'sar dither,#ditherdown 'add tr1_lsample,dither 'add dither to samples 'sar tr1_lsample, #1 'sar tr1_rsample, #1 'sar tr2_rsample, #1 'adds tr1_rsample, tr2_rsample :out mov frqa,tr1_lsample 'output samples 'mov frqb,tr1_rsample jmp #:loop 'again... quiet long $80000000 tr1_pntr long 0-0 tr2_pntr long 0-0 dira_ long 0 ctra_ long 0 ctrb_ long 0 h80008000 long $80008000 hFFFF0000 long $FFFF0000 taps long $80000062 lfsr long 1 zero long 0 lsample_sum long 0 rsample_sum long 0 mute_flag_pntr long 0 mute_flag byte 0 tr1_lsample res 1 tr1_rsample res 1 tr2_lsample res 1 tr2_rsample res 1 to_mix res 1 dither res 1
Anyone have any suggestions about what I may be doing wrong here? I've tried the following:
1) Summing the samples.
2) Dividing each by two, then summing.
3) Summing then dividing by two.
All three create noise and/or distortion. Essentially, if I touch the sample at all (other than the dither) it's ruined. Audio is crystal clear so long as I'm only iterating over a single track buffer.
Thoughts?
-BH
spin
6K
Comments
Why do you convert the samples to unsigned values? It's much easier if the samples are signed. Just separate the samples then divide first and then add.
Here is a code fragment, how I would do it:
Andy
As far as how to mix two audio files together, that one sort of stumps me too. I've thought about that one a few times in the past and didn't come up with an answer. Maybe it has something to do with simply watching the resulting sound levels and if they exceed a certain value within a group of samples(like 30ms worth or so) then you increment down the volume. If it hasn't exceeded the threshold for a group of samples then you let the volume rise unless it is at full volume already. It would sort of be like auto gain control. I would try adjusting the volume to the user's setting after this process.
I'd love to hear what you end up doing.
I now have the signals mixing fluidly, but seems I'm encountering the issue that others have faced with consistently opening and buffering two files simultaneously - at least that's my guess at present.
I'm seeing a failure rate on the secondary buffer about every third read where either the file never opens, or it opens and kills the other buffer. Oddly enough, sometimes I'll hear the failed buffer play 10-15 seconds later...
I'll keep investigating, but as far as the original topic, the samples are now mixing clearly. I'll post in the OBEX when I get everything cleaned up and presentable.
Jonathan
Yes, using two copies, uniquely named, each reading from their own cog.
After a bit more debugging, seems that when the secondary buffer fails, it will either return -1 from fsrw.popen() or open, but derive incoherent values from the header for the channel, sample rate, bit-depth fields.
Jonathan
@Jon, 22kHz does help some, and the lower sample rate is perfectly adequate in this application.
Thanks guys.
I appreciate everyone's time.