0
0

Delphi unit for mixing/merging multiple wav files with FMOD, it also allows volume fades on either channel. You can easily add more channels if required.

I thought it might be useful to someone, it took me a while to figure out. :)

Usage: Just add uFMOD to uses then call it with FMODMixFiles().

e.g. FMODMixFiles(‘c:\wav1.wav’,’c:\wav2.wav’,c:\wavoutput.wav’);

[code:3cgcsa9q]{Delphi unit for mixing/merging 2 WAV files to one WAV file using FMOD.
Includes inline channel volume fading – kim@evolutioncreative.net – 2004

minor Issues:
The outputted WAV file is quieter than what went in, no idea why.
e.g. 1311 1311 1311 7711 7711 7711 7711 9311
becomes: 0111 0111 0111 6511 6511 6511 6511 8111

StreamEnd, Isplaying, GetChannelsPlaying etc, don’t seem accurate enough
to stop at exactly the last sample of the input file.
So it’s always around 1k larger than it should be. The wav header
contains the correct data length however.

p.s. put your own error handling in :-) }

unit uFMOD;

interface

uses FMOD, fmodtypes, fmoderrors, sysutils, classes;

type
{ format of WAV file header }
TWavHeader = record { parameter description }
rId: longint; { ‘RIFF’ 4 characters }
rLen: longint; { length of DATA + FORMAT chunk }
{ FORMAT CHUNK }
wId: longint; { ‘WAVE’ }
fId: longint; { ‘fmt ‘ }
fLen: longint; { length of FORMAT DATA = 16 }
{ format data }
wFormatTag: word; { $01 = PCM }
nChannels: word; { 1 = mono, 2 = stereo }
nSamplesPerSec: longint; { Sample frequency eg 44100}
nAvgBytesPerSec: longint; { = nChannels * nSamplesPerSec * (nBitsPerSample/8) }
nBlockAlign: word; { = nChannels * (nBitsPerSAmple / 8 }
wBitsPerSample: word; { 8 or 16 }
{ DATA CHUNK }
dId: longint; { ‘data’ }
wSampleLength: longint; { length of SAMPLE DATA }
{ sample data : offset 44 }
{ for 8 bit mono = s[0],s[1]... :byte}
{ for 8 bit stereo = sleft[0],sright[0],sleft[1],sright[1]... :byte}
{ for 16 bit mono = s[0],s[1]... :word}
{ for 16 bit stereo = sleft[0],sright[0],sleft[1],sright[1]... :word}
end;

procedure FMODMixFiles(FMInput1: string; FMInput2: string; FMOutput: string);
function WriteBuffer(OriginalBuffer: Pointer; NewBuffer: Pointer; ALength, Param: Integer): Pointer; stdcall;
procedure FadeVolume(FvChannel: Integer; FvStartVol: Integer; FvEndVol: Integer; FvInPoint: Integer; FvLength: Integer; FvCTime: Integer);

var
RawWrite_DSP: PFSOUNDDSPUNIT;
Stream1, Stream2, LongWav: PFSoundStream;
OutFile, HStream: TFileStream;
Header: TWavHeader;

implementation

procedure FMODMixFiles(FMInput1: string; FMInput2: string; FMOutput: string);
begin
FSOUND_SetMixer(FSOUND_MIXER_QUALITY_MMXP5);
FSOUND_SetOutput(FSOUND_OUTPUT_NOSOUND_NONREALTIME);
FSOUND_Init(44100, 32, 0);
Stream1 := FSOUND_Stream_Open(PChar(FMInput1), FSOUND_NORMAL + FSOUND_MPEGACCURATE, 0, 0);
Stream2 := FSOUND_Stream_Open(PChar(FMInput2), FSOUND_NORMAL + FSOUND_MPEGACCURATE, 0, 0);

// get the longest wav file
if FSOUND_Stream_GetLengthMs(Stream1) > FSOUND_Stream_GetLengthMs(Stream2) then begin
HStream := TFileStream.Create(FMInput1, fmOpenReadWrite + fmShareDenyNone);
LongWav := Stream1;
end
else begin
HStream := TFileStream.Create(FMInput2, fmOpenReadWrite + fmShareDenyNone);
LongWav := Stream2;
end;

//wav header – set up for 16bit/44k/stereo/PCM/WAV – edit to suit.
Header.rId := $46464952; { ‘RIFF’ }
Header.rLen := (HStream.Size – 44) + 16; { length of sample + format }
Header.wId := $45564157; { ‘WAVE’ }
Header.fId := $20746D66; { ‘fmt ‘ }
Header.fLen := 16; { length of format chunk }
Header.wFormatTag := 1; { PCM data }
Header.nChannels := 2; { mono/stereo }
Header.nSamplesPerSec := 44100; { sample rate }
Header.nAvgBytesPerSec := 176400; {channels * sample rate * (16 div 8}
Header.nBlockAlign := 4; {channels * (16 div 8}
Header.wBitsPerSample := 16; { bit resolution eg 8, 16, 32?}
Header.dId := $61746164; { ‘data’ }
Header.wSampleLength := HStream.Size – 44; { sample data size eg file size -44}

OutFile := TFileStream.Create(FMOutput, fmCreate);
OutFile.Write(Header, 44);

RawWrite_DSP := FSOUND_DSP_Create(WriteBuffer, FSOUND_DSP_DEFAULTPRIORITY_USER, 0);
FSOUND_DSP_SetActive(RawWrite_DSP, true);

FSOUND_Stream_Play(0, Stream1);
FSOUND_Stream_Play(1, Stream2);

//set the volume after playing the streams
FSOUND_SetVolume(0, 0);
FSOUND_SetVolume(1, 255);

{FMOD’s StreamEnd callback stops short,
IsPlaying, GetPosition & GetChannelsPlaying all stop late, weird.
This is the nearest I could get to the correct length
without losing anything}
while FSOUND_STREAM_GetTime(LongWav) < FSOUND_STREAM_GetLengthMs(LongWav) do begin
if Outfile.Size >= HStream.Size then break;

//fade channels in or out over y seconds
FadeVolume(0, 0, 255, 0, 5000, FSOUND_Stream_GetTime(LongWav));

FSOUND_Update;

end;

if FSOUND_DSP_GetActive(RawWrite_DSP) = true then begin;
FSOUND_DSP_SetActive(RawWrite_DSP, False);
FSOUND_DSP_Free(RawWrite_DSP);
end;

FSOUND_Close();
HStream.Free;
OutFile.Free;
end;

function WriteBuffer(OriginalBuffer: Pointer; NewBuffer: Pointer; ALength, Param: Integer): Pointer; stdcall;
begin
//use shl 2 here otherwise it’ll just make noise
Outfile.Write(NewBuffer^, ALength shl 2);
Result := nil;
end;

procedure FadeVolume(FvChannel: Integer; FvStartVol: Integer; FvEndVol: Integer; FvInPoint: Integer; FvLength: Integer; FvCTime: Integer);
var
Vol: integer;
begin
if (FvCTime >= FvInPoint) and (FvCTime <= FvInPoint + FvLength) then begin
if FvStartVol < FvEndVol then Vol := trunc(((FvEndVol – FvStartVol) / FvLength) * (FvCTime – FvInPoint) + FvStartVol)
else Vol := trunc(Abs(Abs(((FvEndVol – FvStartVol) / FvLength) * (FvCTime – FvInPoint)) – 255));
FSOUND_SetVolume(FvChannel, Vol);
end;
end;

end.
[/code:3cgcsa9q]

KimB

  • You must to post comments
0
0

“The outputted WAV file is quieter than what went in, no idea why. “

Use [url=http://www.fmod.org/docs/HTML/FSOUND_Stream_GetLength.html:3b9jf91d]FSOUND_Stream_GetLength[/url:3b9jf91d] and a global var in the dsp callback to have an accurate value.

  • You must to post comments
0
0

yeah thanks for the tip, that might sort out the length issue although I can’t see how that would resolve the volume being quieter. it’s only about 1db so I wasn’t that bothered about it.

Best

kim

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.