The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: piotrus on March 01, 2007, 03:09:37 PM

Title: Programming sound effects
Post by: piotrus on March 01, 2007, 03:09:37 PM
I`d like to write a library which adds an echo effect (and some others) to a .wav file. I`ve read some stuff about .wav file format but still have no idea how to process a sample to get this effect...  :(
Any ideas?
Title: Re: Programming sound effects
Post by: ragdog on March 01, 2007, 03:12:53 PM
hi

I use bass libary from www.un4seen.com
or fmod from www.fmod.org

ragdog
Title: Re: Programming sound effects
Post by: Tedd on March 02, 2007, 12:33:02 PM
A quick solution would be to copy the last 'n' milliseconds of the wav, and append them to the end. That should give a basic 'echo' effect.
Once you've got the hang of that you can start to make it more sophiticated, such as adding repeats, fading the volume, overlapping, etc.
As for decoding the wav file, start by working out how to calculate the offset of the sample for a particular time offset - which will require you to extract the useful information from the header, and then use that for the calculation. Once you've done that, find the start and end points to copy for the echo is pretty easy.
(There are a few different wav formats, but just stick with pcm for now.)
Title: Re: Programming sound effects
Post by: gabor on March 13, 2007, 08:14:10 AM
Hi!

Standard wav files have a 44 byte header describing the body part, the samples in this case: the number of channels, the sample rate, the bits per sample (or bytes per sample) and the length of the body part.
So, the first step should be to read this 44 bytes, setup your work buffer according to it. You can either process the whole sample or reading only a portion of it into the buffer.
The delay effect is simple like Tedd posted. The delayer is a module with memory, since it has to store and add earlier sample values. Like having a 1 second delay has to remember the sample value of 1 second earlier. You have to store the unprocessed samples or you can simply read it from the file (having a buffer for it is necessary for real -time sample synth only). The delay given in miliseconds you have to convert into bytes: delay*sampleRate*bytesPerSample/1000 (the division by 1000 is necessary only if the delay is given in miliseconds). Example: for a 44100Hz, 16 bit, stereo sample the position difference between the actual sample and the delay sample at the 1.2 sec delay in bytes is 1.2*44100*4=211680. You can read the file using 2 pointers, the first pointer reads the actual sample, the second that has a delay (211680 bytes in the example) reads the delayed sample. You would amplify this value or it won't sound like an echo. For multiple echo you have to use more file pointers, or in the memory buffer version more pointers on the buffer. Important to use 0 (silence) for the first n samples, where the echo position would point out of the sample (in the example actual position-211680 would be less than 0).
Finally I would note that it can allocate lot of memory though, I would choose the memory buffer version since accessing the disk is slow, and here for every sample the disk has to be accessed numberOfEchos+1 times. On the other hand for a 10 second delay with CD quality sound you have to allocate 10*44100*4=ca. 1.68MB (this is endurable  :bg)

Greets, Gábor

I hope I gave some hints.
Title: Re: Programming sound effects
Post by: piotrus on April 14, 2007, 07:26:36 PM
Quote from: Tedd on March 02, 2007, 12:33:02 PM
A quick solution would be to copy the last 'n' milliseconds of the wav, and append them to the end. That should give a basic 'echo' effect.
Once you've got the hang of that you can start to make it more sophiticated, such as adding repeats, fading the volume, overlapping, etc.


I copied the end samples of the wav, and put them on the end. How can I fade a volume to make an echo sound more realistic?
Title: Re: Programming sound effects
Post by: Tedd on April 14, 2007, 09:03:07 PM
Aah, well done! Now for the fun part :U

Okay, so think about what happens when the volume fades - it becomes quieter!
So, what you'll need to do is reduce the volume level (amplitude) of each sample in the echo part so that's it quieter. Now, the amplitude of each sample is its distance from zero (not just the value itself because they can be negative) so you'll need to reduce that by some amount (let's just say half for now; once you know what to do you can change it.)
Of course, if you just reduce the amplitude of each sample in the echo part by a constant amount, then all you'll get is the sound, and then a quiet repeat -- but this is a first step, get it working before you make it more complex :wink
So then to make it more complex (and hopefully sound better) you'll want to do the actual fade. So the first few samples will be at 99% of their original value, then some 98%, 97, 96, 95, ......, eventually down to silence at zero; make sure you scale the values more accurately though (calculate how fast you want to fade and then apply the gradient to the samples.) Doing this will give you a linear fade, you might also try a logarithmic fade (a curve, instead of a straight line), which might sound more realisitic.

And then, when you put it all together, having multiple repeats (each becomes shorter than the last - echoo..choo..c'oo.o), and the volume fade, you should end up with a fairly nice echo effect :cheekygreen:
Title: Re: Programming sound effects
Post by: piotrus on April 17, 2007, 10:26:15 AM
I divided each sample by 2 and appended them to the end of the file. The "echo buffer" was as long as the whole file, because I experimented with short files.The effect was something like: HELLO TED.. hello ted... :) I changed the "echo buffer" to a constant value instead of the leght of the file. I wanted to get an effect like: "HELLO TED.. ted.. " :) But what I got was: "HELLO TED.. grrhhrrhr" :) The only change in code was a lenght of the buffer and .if which checks if esi>BufferSize. What could be wrong? I checked in debugger, and data in EchoBuffer seem to be OK... :|

mov esi, 5A44   ;the lenght of the echo buffer
     
.while (edi !=00000000h)  ;in edi there`s number of samples (bytes) to proccess

;read sample
   invoke ReadFile,hDebugFile,pBuffer,1, addr SizeReadWrite, NULL      ;pBuffer - pointer to a temporary buffer for a read byte
   mov eax, pBuffer
   mov bl, BYTE PTR [eax]
   xor eax, eax
                xor edx, edx
   mov al, bl
;divide the sample by 2
   div WORD PTR Divider       ;Divider = 2
   mov ecx, pEchoBuffer     
;save the sample in the echo buffer
   mov BYTE PTR [ecx+esi],al
   dec edi   
                inc esi

    .if (esi > EchoBufferSize)
      xor esi, esi
   .endif
.endw
      
mov esi, 5A44h

;append the buffer to the end of the file   
.while (esi != 0)
   invoke WriteFile,hDebugFile, pEchoBuffer, 1, addr SizeReadWrite,NULL
   inc pEchoBuffer
   dec esi   
.endw

Title: Re: Programming sound effects
Post by: Tedd on April 17, 2007, 01:44:14 PM
A few things :bdg

* I hope you're skipping the wav header before doing that :P
* You seem to be treating the samples as bytes - are they really 8-bit?
* You're not sign-extending, and you're using DIV, which means you're messing up the negative samples -- use IDIV, not DIV; and add sign-extension as necessary (if the top bit is 1, then all the bits of the high byte should also be 1 -- see CBW, CDQ, and MOVSX)
* You're copying the echo from the start of the file? (Which would give "HELLO PIO hell-", and not "HELLO PIO-pio")

Play some more :bg Attach full source if you need more help :wink
Title: Re: Programming sound effects
Post by: piotrus on May 04, 2007, 04:58:48 PM
I`ve put movsx and idiv and the effect was still very poor  :'( At least this time I could hear the actual echo :)
Do I really need to care about the sign of the sample? When I analyzed my .wav file in a GoldWave program, it showed that it`s "Wave PCM unsigned 8-bit" ...
What is the diference between signed and unsigned wave files?

Here is my troublemaker:
(When I deleted a line with idiv the effect was OK (but the echo wasn`t quieter :/ ) )

.while (edi != 00000000h)
;read a sample
invoke ReadFile,hDebugFile,pBuffer,1, addr SizeReadWrite, NULL
mov eax, pBuffer

;save the sample in the echo buffer
movsx bx, BYTE PTR [eax]
xor eax, eax
mov ax, bx
xor edx, edx
idiv WORD PTR Divider
mov ecx, pEchoBuffer
mov BYTE PTR [ecx+esi],al

dec edi
inc esi
.if (esi == 2D22h)
mov esi, 0h
.endif

.endw

Title: Re: Programming sound effects
Post by: piotrus on May 05, 2007, 09:40:35 AM
Is it possible that very poor quality of the echo sound is coused by the arithmetic inaccuracy. When I use idiv I divide an integer by integer, and as a result I use only quotient stored in AL??

Title: Re: Programming sound effects
Post by: Tedd on May 09, 2007, 02:49:10 PM
Okay, sorry, the 'standard' is a little messed up..
For wavs with 8-bit samples, they are unsigned -- 0 is the lowest, 255 is the highest, and 128 is 'silent'; whereas wavs with more bits are signed (2's-complement.)

Which means, unfortunately, that simply dividing by 2 (signed or unsigned) isn't quite right - it's worse!
If we scale from -1 (0) to +1 (255)..
Half of +1 (255) is 0.5 (191; but 255/2 = 127!!!) -- very wrong!!
And the problem continues with 'negative' values: half of -1 (0) is -0.5 (64; but 0/2 = 0!!!) :dazzled:

So this all needs special handling - and it's a bit more than a 5 minute patch job ::)



And yes, 8-bit sound is pretty poor, but simply halving the volume shouldn't make it too much worse - the rounding error is only ever going to be less than one, so it shouldn't degrade the quality too much; sampling rate has more of an effect.
Title: Re: Programming sound effects
Post by: sebart7 on May 09, 2007, 04:34:35 PM
Lets say sample is 8bit where 127 is silent. You should substract from it 080h, process it (mix with other sample ?, fade ? anything....) and then add 080h back. This way You do work on data where silent is 0 and data range is -127 to +127

Here is an example how You can do this

        mov al,210      ; input sample byte (anything from 0h to 0ffh where 127 is silent)
       
        and eax,0ffh
        sub eax,080h
        xor edx,edx
        mov ebx,2
        div ebx
        add eax,080h
       
        ; here You have in AL Your sampleByte Divided by 2

Title: Re: Programming sound effects
Post by: Tedd on May 09, 2007, 06:26:12 PM
Aaah, nice work! (How did I miss that ::) :lol)
Although it doesn't work exactly with the negative values - it leaves the topmost bit set - but then you store only 'al' so it doesn't matter.

Anyway, that gives you..

.while (edi != 00000000h)
    ;read a sample
    invoke ReadFile,hDebugFile,pBuffer,1, addr SizeReadWrite, NULL
    mov ecx, pBuffer

    ;save the sample in the echo buffer
    xor eax,eax
    mov al,[ecx]        ;get the sample
    sub eax,80h
    xor edx,edx
    idiv Divider        ;(Divider should be a dword)
    add eax,80h

    mov ecx, pEchoBuffer
    mov BYTE PTR [ecx+esi],al

    dec edi
    inc esi
    .if (esi == 2D22h)
        mov esi, 0h
    .endif
.endw


(Not too sure about what some of the code is meant to be doing, but as long as it makes sense with the rest of your program :wink)
Title: Re: Programming sound effects
Post by: piotrus on May 12, 2007, 04:02:39 PM
It works :8) Thanks a lot!  :bg
Now I`m strugling with some improvements. My program adds an echo only at the end of the file. When a sound is short, the effect is quite nice. But with longer sounds I`d like to add an echo to the background of the whole sound. I tried to add echo samples to actual samples. The algorithm looks like that:
1. read a sample
2. add an echo sample
3. save new sample to file
Before changing the sample I dicrease its volume and save it in the echo buffer so I could add it to the other sample.
Is this idea correct?
The only effect I got was some noise in a background, but maybe it`s due to an error in a code.




Title: Re: Programming sound effects
Post by: piotrus on May 13, 2007, 03:20:40 PM
After several hours of debuging I`ve found an error  :bg Program works :bg
Thanks everybody, and special thanks for Ted  :bg
I owe you a beer :)
Title: Re: Programming sound effects
Post by: Tedd on May 14, 2007, 10:29:51 AM
Nice work :U
Title: Re: Programming sound effects
Post by: sebart7 on May 15, 2007, 08:16:18 PM
Sound effects is a wide topic but ill try to make it as short as possible.
Large amount of Sound effects including echo, ambient efect, flanger, horus, ect,
is created by multiple phase shifting of base sample data.
Theoreticaly it can be done by just one universal simple routine to where You pass fiew
input parameters and receive certain effect as output.

Heres example picture :
(http://images21.fotosik.pl/294/314144fa6dfdc39a.jpg)

Red is a base sample. Green is shifted base sample mixed together with it self (in example multiple times)
Before mixing shifted sample with base sample You may process it also somehow (fade in time ect)

Lets say example FX routine call may looks like  invoke DoSoundFX1,delayTime,valFade,valRepeat

Where :
delayTime is amount of shift.
valFade is for example how fast shifted sample will fade to silence (process it before mix with base sample)
valRepeat is how many times to repeat (picture shows 2x repeat)
Of course in time and researches You may add more parameters and make it much more complex.

Here fun starts, By providing diferent parameters You may get realy cool effects.
1000,0,1 will give FX similar to just ading sample to end of buffer like You did before
250,100,5 will give realy nice advanced echo effect.
20,40,300 may give corridor like echo efect. Ect ect....

Also You may consider to add more advanced parameters like sinShift. (look at small sinus betwin samples at picture above)
Idea is to add sinus values that change in time to delayTime. This way each shift will be a little unregular.
If You use parameter sinShift > 0 (enabled) You should use small value for delayTime that together will
create nice, drifting alike, flanger effect.

Mixing 2 samples is like
(byteSample1 + byteSample2) / 2 , dont forget to sub 080h it and add 080h before and after.
it can be:

; AL is sample1Byte
; BL is sample1Byte

    and EAX,0ffh   ; leave only AL data (but wee need all EAX)
    sub EAX,080h  ; make sample1 as  -127 +127 range
    and EBX,0ffh   ; leave only BL data (but wee need all EBX)
    sub EBX,080h  ; make sample2 as  -127 +127 range
    add EAX,EBX   ; mix both sampleBytes
    shr  EAX,1       ; shifting bits right is alike divide by 2
    add EAX,080h  ; make it back 0 to 255 range

; here You have In AL mixed byteSample