Mixing two audio files and saving as one?

Just starting out? Need help? Post your questions and find answers here.
Godai
Enthusiast
Enthusiast
Posts: 171
Joined: Thu Oct 05, 2006 8:13 pm

Mixing two audio files and saving as one?

Post by Godai »

Did anyone try this? I want to mix two audio files into one file where both original sounds are playing "on top" of each other.
It strikes me that it should be easy if they are both wavs of same sample rate?

Thanks in advance

/Godai
eriansa
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Mar 17, 2004 12:31 am
Contact:

Post by eriansa »

Mixing is just adding. So, just read sample per sample and add them.
Godai
Enthusiast
Enthusiast
Posts: 171
Joined: Thu Oct 05, 2006 8:13 pm

Post by Godai »

I know. Adding the two and dividing by 2 gives good avarage results (at the loss of about -3db) but just wondered if somebody had the trivial wav code etc :)

/Godai
eriansa
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Mar 17, 2004 12:31 am
Contact:

Post by eriansa »

Godai wrote:I know. Adding the two and dividing by 2 gives good avarage results (at the loss of about -3db) but just wondered if somebody had the trivial wav code etc :)

/Godai

Code: Select all

If ReadFile(#File_Work,szFile)
  SQ4\WavePath=GetPathPart(szFile)
  SQ4\SampleRate=Eng\SampleRate
;Header RIFF
  sz1.s=""
  For i=0 To 3
    sz1=sz1+Chr(ReadByte(#File_Work))
  Next
  If sz1="RIFF"
    ;Length
    lLen=ReadLong(#File_Work)
    ;WAVE/fmt
    sz1=""
    For i=0 To 3
      sz1=sz1+Chr(ReadByte(#File_Work))
    Next
    If sz1="WAVE"
      sz1=""
      For i=0 To 3
        sz1=sz1+Chr(ReadByte(#File_Work))
      Next
      If sz1="fmt "
        ;ChunkSize
        lSize=ReadLong(#File_Work)
        ;CompressionCode
        ReadWord(#File_Work)
        ;Channels    
        Channels=ReadWord(#File_Work)
        ;SampleRate    
        SQ4\SampleRate=ReadLong(#File_Work)
        ;AvgBits  
        lAvgBits=ReadLong(#File_Work)
        ;BlockAlign
        wBlockAlign.w=ReadWord(#File_Work)
        ;Bits per Sample
        Bits=ReadWord(#File_Work)
        If Bits=8 Or Bits=16 Or Bits=24 Or Bits=32
          ;Extra format bytes
          For i=0 To lSize-16-1
            ReadByte(#File_Work) 
          Next
          ;Data  
          sz1.s=""
          For i=0 To 3
            sz1=sz1+Chr(ReadByte(#File_Work))
          Next
          If sz1="data"
            ;ChunkSize
            lSize=ReadLong(#File_Work)
            If lSize>0
              ChunkSize=lSize
              If Bits=8
                NrOfSamples=lSize
              ElseIf Bits=16
                NrOfSamples=Round(lSize/2,1)
              ElseIf Bits=24 And wBlockAlign%3=0
                NrOfSamples=Round(lSize/3,1)
              Else
                NrOfSamples=Round(lSize/4,1)
              EndIf
              arrMix(ChannelNr)\MixLen=(NrOfSamples/Channels)
              arrMix(ChannelNr)\MixPtr[0]=AllocateMemory(arrMix(ChannelNr)\MixLen*4)
              arrMix(ChannelNr)\MixPtr[1]=AllocateMemory(arrMix(ChannelNr)\MixLen*4) 
              ;Read 
              fSample.f=0
              WritePtr=0
              channel=0 
              While lSize 
                Select Bits
                Case 8
                  fSample=ReadByte(#File_Work)/#Gain8
                  lSize-1
                Case 16
                  fSample=ReadWord(#File_Work)/#Gain16
                  lSize-2
                Case 24
                  If wBlockAlign%3=0
                    b1.b=ReadByte(#File_Work)
                    b2.b=ReadByte(#File_Work)
                    b3.b=ReadByte(#File_Work)
                    lSample=b3<<16+b2<<8+b1
                    fSample=lSample/#Gain24
                    lSize-3
                  Else
                    fSample=ReadLong(#File_Work)/#Gain24
                    lSize-4
                  EndIf
                Case 32
                  fSample=ReadFloat(#File_Work)
                  lSize-4
                EndSelect
                PokeF(arrMix(ChannelNr)\MixPtr[channel]+WritePtr,fSample)
                If Channels=1:PokeF(arrMix(ChannelNr)\MixPtr[1]+WritePtr,fSample):EndIf
                channel+1
                channel%Channels
                If channel=0:WritePtr+4:EndIf
                ;xtra test
                If Eof(#File_Work):lSize=0:EndIf
                If WritePtr>NrOfSamples*4/Channels
                  szError.s="BitDepth="+Str(Bits)
                  szError  =szError+Chr(13)+Chr(10)+"Channels="+Str(Channels)
                  szError  =szError+Chr(13)+Chr(10)+"AvgBits="+Str(lAvgBits)
                  szError  =szError+Chr(13)+Chr(10)+"BlockAlign="+Str(wBlockAlign)
                  szError  =szError+Chr(13)+Chr(10)+"ChunkSize="+Str(ChunkSize)
                  szError  =szError+Chr(13)+Chr(10)+"NrOfSamples="+Str(NrOfSamples)
                  szError  =szError+Chr(13)+Chr(10)+"Allocated bytes="+Str(NrOfSamples*4/Channels)
                  szError  =szError+Chr(13)+Chr(10)+"Write pointer="+Str(WritePtr)
                  MessageRequester("Wave-Error",szError)
                  lSize=0
                EndIf
              Wend
            EndIf
          EndIf
        EndIf
      EndIf
    EndIf
  EndIf
  CloseFile(#File_Work)
Endif

This is an excerpt from RaXnTrax (www.raxntrax.com), my VST-Host, to read WAV files.
Afterwards you can read the samples and add them. By the way : no need to divide them by 2.

To mix stuff very fast, I recommend to use SSE. (you can add 4 samples in one operation). I did some benchmarks, SSE is 3.7 times faster than a simple For/Next loop.
To resample, I use the free r8Brain from Voxengo.

Hope this helps?
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

A very interesting stuff is trying to mix it based on BPM (beats per minute, you need to calculate it).
Ive done something about that some time ago, it was 95% accurrated, but the 5% of failures make me quit.

One day i will complete that job :)
eriansa
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Mar 17, 2004 12:31 am
Contact:

Post by eriansa »

ricardo wrote:A very interesting stuff is trying to mix it based on BPM (beats per minute, you need to calculate it).
Ive done something about that some time ago, it was 95% accurrated, but the 5% of failures make me quit.

One day i will complete that job :)
Care to share your beat-detection algo?
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Godai wrote:I know. Adding the two and dividing by 2 gives good avarage results (at the loss of about -3db) but just wondered if somebody had the trivial wav code etc :)

/Godai
Actually you can speed that up a bit by doing this:
Debug (0.9+0.5)*0.5


That is the same as doing (0.9+0.5)/2
except that the whole math is float (avoids conversion of /2 to float)
and a multiply is faster than a divide as well.

Obviously the sample points need to be in floating point first, (silence is 0.0 full volume is 1.0) but that is the proper way to deal with audio processing anyway (less quantization or aliasing), although it is possible to use only integer I'm not sure what speed gains that would get on modern systems (unless it's in asm).
PS! Doing it in float makes it easier to handle -1.0 and 1.0 as well, with integer the sign can be a pain at times). (Oh and the fact that sound is logarithmic makes float a good choice too)
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

eriansa wrote:
ricardo wrote:A very interesting stuff is trying to mix it based on BPM (beats per minute, you need to calculate it).
Ive done something about that some time ago, it was 95% accurrated, but the 5% of failures make me quit.

One day i will complete that job :)
Care to share your beat-detection algo?
Not for now. Butthe idea behind (to detect beats on dance or high rythm 4x4 music) is not that difficult. Again, this may work more or less in 4/4 simple music.

The idea is that almost every note and peak will be more or less on time, so there will be a relation betwen the delays betwen peaks and the beats per minute.

If you measure that time betwen peaks, you will notice soon that they "grouped" more or less (that "groups" are usually multiples of the others).

Lets say i found that many peaks has timming of 125 ms, then the most probably is that the next group of common timmings will be around 250 ms and so on. This is not precise, but you can group together timmings that are 10% + or 10% - on the most usually founded.

Im trying to describe on easy words (and in a foreign language) soemthing that is a little more mathematical, but the idea behind is not that complicated. The idea is that you will find curves if you make a graphic reprsenting all the peaks that you find. Those curves, on the peak points will represent the 16th, 8th, 4th, etc. note times.

Until that point my code works almost perfect for hard rythm music (music with very noticed rythm).
But when i tried to make my own "click" (metronome) sounds on every 4th note... most time its acurrate, but sometimes a little desynchronization happends nd i had not time atm to resolve this problem, taht is small if you see it as maths, but not nice if you think it like music, because the ear or sense of rythm is very good and can notice any little failure on sync.

I hope i explain myself on a foreign language this conceptual idea.

*If anybody is interested on this matter i will gladly work together trying to find the best idea to achieve this thing. Its not common that people find a solution for this, in my own experience only one software was REALLY able to detect and syncro every 4th note. MixMeister is really big words if we talk about beat syncro.
Post Reply