Merging wave files (Wave Audio Package)

Please post bug reports, feature requests, or any question regarding the DELPHI AREA projects here.

Merging wave files (Wave Audio Package)

Postby Kambiz » September 7th, 2003, 6:12 pm

Here is the implementation of a function to make a wave audio stream by merging several wave audio streams. The function expects all input waves have identical format, otherwise it fails and returns False.

Code: Select all
uses
  WaveUtils, MMSystem;

function MergeWaves(DstStream: TStream; SrcStreams: array of TStream): Boolean;

  function AppendWave(mmIO: HMMIO; Stream: TStream;
    ExpectedFormat: PWaveFormatEx): Boolean;
  var
    Buffer: array[0..1023] of Char;
    DataSize, DataOffset: Cardinal;
    ReadCount: Cardinal;
    WaveFormat: PWaveFormatEx;
  begin
    Result := False;
    GetStreamWaveAudioInfo(Stream, WaveFormat, DataSize, DataOffset);
    try
      if (ExpectedFormat^.cbSize = WaveFormat^.cbSize) and
         CompareMem(ExpectedFormat, WaveFormat, ExpectedFormat^.cbSize) then
      begin
        Stream.Seek(DataOffset, soFromBeginning);
        while DataSize <> 0 do
        begin
          if DataSize > SizeOf(Buffer) then
            ReadCount := SizeOf(Buffer)
          else
            ReadCount := DataSize;
          Stream.ReadBuffer(Buffer, ReadCount);
          mmioWrite(mmIO, @Buffer[0], ReadCount);
          Dec(DataSize, ReadCount);
        end;
        Result := True;
      end;
    finally
      FreeMem(WaveFormat);
    end;
  end;

var
  mmIO: HMMIO;
  ckRIFF, ckData: TMMCKInfo;
  WaveFormat: PWaveFormatEx;
  Dummy: Cardinal;
  I: Integer;
begin
 Result := False;
 if Length(SrcStreams) > 0 then
 begin
   GetStreamWaveAudioInfo(SrcStreams[0], WaveFormat, Dummy, Dummy);
   try
     mmIO := CreateStreamWaveAudio(DstStream, WaveFormat, ckRIFF, ckData);
     try
       for I := 0 to Length(SrcStreams) - 1 do
       begin
         if not AppendWave(mmIO, SrcStreams[I], WaveFormat) then
           Break; // Error: Wave formats are not identical
       end;
       Result := True;
     finally
       mmioAscend(mmIO, @ckData, 0);
       mmioAscend(mmIO, @ckRIFF, 0);
       mmioClose(mmIO, 0);
     end;
   finally
     FreeMem(WaveFormat);
   end;
 end;
end;


Here is an example of usage:

Code: Select all
MergeWaves(AudioPlayer3.Wave.Stream,
  [AudioPlayer1.Wave.Stream, AudioPlayer2.Wave.Stream]);


Cheers,
Kambiz
Last edited by Kambiz on April 6th, 2007, 3:13 am, edited 3 times in total.
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Cody » April 5th, 2007, 6:16 pm

Hi Kambiz,

I am trying to merge serveral .wav-files into one .wav-file. I am trying to use your merge function for this. But i have some problems with this again :roll:

1. Could it be that "CreateStreamWaveAudioInfo" must be "CreateStreamWaveAudio" ? Otherwise the compiler gives an error.

2. I tried to use your function like this:

Code: Select all
procedure TForm1.MixButtonClick(Sender: TObject);
var WaveLength : Integer;
begin

  AudioPlayer1.Wave.LoadFromFile(Edit2.Text);
  AudioPlayer2.Wave.LoadFromFile(Edit1.Text);
  MergeWaves(AudioPlayer3.Wave, [AudioPlayer1.Wave, AudioPlayer2.Wave]);
  AudioPlayer3.Wave.SaveToFile(Edit3.Text);

end;



But the compiler complains about "[Error] Mischer1.pas(124): E2010 Incompatible types: 'TStream' and 'TWaveStreamAdapter'"

Can you help me?

Thank you!
Cody
Active Member
Active Member
 
Posts: 5
Joined: April 5th, 2007, 1:45 pm

Postby Kambiz » April 6th, 2007, 3:17 am

1. The code was belonging to earlier versions of the wave audio package. I adapted it to the new version.

2. The Wave property is no more a stream object. Instead use Wave.Stream property to pass streams to MergeWaves function.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Cody » April 6th, 2007, 12:10 pm

Hi Kambiz!

Thank you for your help. It's working now! :D

I want to try now to really "merge" serveral 16 Bit Stereo .wav-files together and not append them. That means that they all should start at the beginning and the file size of the destination file will have the length of the longest source file.

Is it possible to do this with the Waves package too? Have you got an idea how to do it?? :roll:

Thanks again!

Cody
Cody
Active Member
Active Member
 
Posts: 5
Joined: April 5th, 2007, 1:45 pm

Postby Johnny_Bit » April 6th, 2007, 1:55 pm

Simple, yet most accurate, you won't hear much though: get average from samples in current position, put it in output, move to next position.
Johnny_Bit
VIP Member
VIP Member
 
Posts: 455
Joined: June 15th, 2003, 9:56 am

Postby Cody » April 6th, 2007, 2:07 pm

Hi Johnny,

thanks for your answer. What do you mean by "you wont hear much"? will the mixed waves be too silent because the average from the samples is used?
Can you give me an example of code because I'm new to delphi and especially to working with waves.. :roll:

Thanks alot!

Cody
Cody
Active Member
Active Member
 
Posts: 5
Joined: April 5th, 2007, 1:45 pm

Postby Kambiz » March 22nd, 2008, 2:59 pm

Recently someone asked me how to merge some wave files with a silence between them. Because of improvements in the recent updates of the Wave Audio package, The new code would be much simpler and optmizer than the old code.

Code: Select all
uses
  WaveStorage;

procedure MergeWaveFiles(const TargetFile: String;
  const SourceFiles: array of String; SilenceGap: DWORD);
var
  Target: TWaveFile;
  Source: TWaveFile;
  I: Integer;
begin
  Target := TWaveFile.Create(TargetFile, fmCreate);
  try
    for I := Low(SourceFiles) to High(SourceFiles) do
    begin
      Source := TWaveFile.Create(SourceFiles[I], fmOpenRead or fmShareDenyWrite);
      try
        if I = Low(SourceFiles) then
          Target.Assign(Source)
        else
        begin
          Target.InsertSilence(INFINITE, SilenceGap);
          Target.Insert(INFINITE, Source);
        end;
      finally
        Source.Free;
      end;
    end;
  finally
    Target.Free;
  end;
end;



Here is example of the usage:

Code: Select all
MergeWaveFiles('target.wav', ['source1.wav', 'source2.wav', 'source3.wav'], 1 * MSecsPerSec);
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby TenBaz » August 12th, 2008, 5:27 pm

Unless I'm doing something drastically wrong (which is entirely possible), the above (2nd) function works great... until you compile the program.

When the resulting exe file is run, you get a 'Priviledged Instruction' error or an Access Violation error on all subsequent attempts. (In another program I tried it in, the program just closes with no errors).

It seems to work just fine when in the Delphi 5 IDE though, so I'm assuming that when in the IDE environment, some memory cleanup is being done that isn't when using the exe. That's if my understanding that a 'Priviledged Instruction' error is down to illegally accessing memory.

I'm calling the function multiple times - with a different number of WAV files each time. But as a compiled exe it dies on the first call so I'm baffled as to why it's not working. (I'm far from an expert programmer).

The function is being called as follows:

Code: Select all
case TracksToMerge of
  2: MergeWaveFiles(OutputName,[FN1,FN2],Spacing);
  3: MergeWaveFiles(OutputName,[FN1,FN2,FN3],Spacing);
  4: MergeWaveFiles(OutputName,[FN1,FN2,FN3,FN4],Spacing);
  5: MergeWaveFiles(OutputName,[FN1,FN2,FN3,FN4,FN5],Spacing);
  6: MergeWaveFiles(OutputName,[FN1,FN2,FN3,FN4,FN5,FN6],Spacing);
  7: MergeWaveFiles(OutputName,[FN1,FN2,FN3,FN4,FN5,FN6,FN7],Spacing);
  8: MergeWaveFiles(OutputName,[FN1,FN2,FN3,FN4,FN5,FN6,FN7,FN8],Spacing);
end;


Where OutputName is a string (the required resulting wav filename), FN1, FN2 etc are strings containing the full path and filenames of the wavs to merge and Spacing (Integer) is the number of milliseconds silence required between each source wav).

Any pointers would be appreciated.

BC
TenBaz
Member
Member
 
Posts: 3
Joined: August 12th, 2008, 5:07 pm

Postby Kambiz » August 13th, 2008, 1:10 pm

It sounds very strange.

Maybe it is not the merging function that craches. Did you try to track the program execution?
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby TenBaz » August 16th, 2008, 11:16 am

Did you try to track the program execution?


Yes - in the IDE, the program just works, it's only the end compiled exe which does not. I've put ShowMessage() lines as the first and last lines of the MergeWaveFiles procedure and when compiled, the first one appears but the program crashes before the last one.

If I rem out all the lines in the MergeWaveFiles procedure before compiling, the exe obviously doesn't create a new WAV file, but it doesn't crash when run either.

So, I have not solved the problem, however I have managed to figure out a work-around, although it is a bit 'messy'.

What I have done is create a separate program containing just the MergeWaveFiles procedure and pass the parameters through the command line. My main program then ShellExec's it when required.

This works fine - albeit with a small delay.

Your procedure therefore works OK compiled but must somehow clash with something I have written in my own code. Unfortunately I can't see any way I can track down exactly what I have done to cause the problem.

BC
TenBaz
Member
Member
 
Posts: 3
Joined: August 12th, 2008, 5:07 pm

Postby Kambiz » August 16th, 2008, 2:06 pm

Do you use any COM object or third party memory manager in your application?
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby TenBaz » August 19th, 2008, 12:24 am

No - nothing like that at all. I do use a number of third party components, though nothing remotely memory management related.

Don't worry - calling your procedure as a stand-alone exe works fine for what I am doing.

Life's too short to waste any more time on it! :)

Thanks again...

BC
TenBaz
Member
Member
 
Posts: 3
Joined: August 12th, 2008, 5:07 pm


Return to DELPHI AREA Projects

Who is online

Users browsing this forum: No registered users and 1 guest

cron