Solved

WaveOutStream - playing wave in WinApi

Posted on 2003-12-12
24
888 Views
Last Modified: 2007-12-19
A part of my simple code:

const
  memBlockLength = 512;

type
  Tmemblock = array[0..memBlockLength] of byte;
  PmemBlock = ^TmemBlock;
  TWaveheader = packed record
    RiffID: array [1..4] of char;
    RiffLen: LongWord;
    WaveID: array [1..4] of char;
    FmtID: array [1..4] of char;
    FmtLen: LongWord;
    FormatTag: Word;
    nChannels: Word;
    nSamplesPerSec: LongWord;
    nAvgBytesPerSec: LongWord;
    nBlockAlign: Word;
    wBitsPerSample: Word;
    DataID: array [1..4] of char;
    DataLen: LongWord;
  end;

var
  OutPort:  HWAVEOUT;
  WvOutFmt: ^TWAVEFORMATEX;
  f: File;
  HeaderF: TWaveHeader;
  Header: PWaveHdr;
  OneBlock: PmemBlock;
  MMoRes: MMResult;

----------------------------------
  if OpenDialog1.Execute then
  begin
    AssignFile(F, OpenDialog1.FileName);
    Reset(F,1);
    BlockRead(F, HeaderF, 44);  
   
    New(WvOutFmt);
    with WvOutFmt^ do
    begin
      wFormatTag := WAVE_FORMAT_PCM;
      nChannels := HeaderF.nChannels;
      nSamplesPerSec := HeaderF.nSamplesPerSec;
      nAvgBytesPerSec := HeaderF.nAvgBytesPerSec;
      nBlockAlign := HeaderF.nBlockAlign;
      wBitsPerSample := HeaderF.wBitsPerSample;
      cbSize := 0;
    end;
// lbDevCaps.ItemIndex - my audiocard outs (WaveOutGetDevCaps)
    OutPort := waveOutOpen(Nil, lbDevCaps.ItemIndex, @WvOutFmt, Form1.handle, 0, WAVE_ALLOWSYNC or CALLBACK_WINDOW);
    FreeMem(WvOutFmt);
-----------------------------------------------------
And here is my problem. What should I do next do play that wave file? I tried something stupid ... like this:

  New(OneBlock);
  Header := New(PWaveHdr);
  BlockRead(F, OneBlock, SizeOf(OneBlock));
  with Header^ do
  begin
    lpdata := pointer(OneBlock);
    dwbufferlength := memblocklength;
    dwBytesRecorded := 0;
    dwUser := 0;
    dwFlags := 0;
    dwLoops := 0;
  end;
  MMoRes := waveOutPrepareHeader(OutPort, Header, SizeOf(Header));
  MMoRes := waveOutWrite(OutPort, Header, SizeOf(Header));
 
And of course it doesn't work. Could anybody give me an example of wave streaming API procedures? waveOutOpen - I'm not sure if I'm doing it right (callback??). How should I read and prepare blocks (waveOutPrepareHeader), and then send them (waveOutWrite) to audio device? Also how to use callback to get end of wave file? I need to play two different wave files (the same sample rate/freq. etc.) to different outputs of my audio card with perfect sample accurate. Could you help?

Jacek
0
Comment
Question by:JacekH
  • 7
  • 6
  • 6
  • +2
24 Comments
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 9926676
what do you mean to different outputs ? front/rear speakers ?
deAudio (commercial) does it nicely .. and much much more :)
0
 

Author Comment

by:JacekH
ID: 9926757
My audio card (RME HDSP + Mutliface) has 18 outputs - 9 stereo pairs: 8 x analog outs, 1x ADAT (8 outs) and one SPDIF (stereo). I don't want to use any other components like deAudio. I tried with BASS, FMOD. BASS cannot play waves to different outputs in perfect sync. FMOD cannot play to more one than one stereo output at all, only with multiple channel files prepared with special program. And first of all I want to learn how to do it. Typical multitrack editor plays tracks mixing them to one outputs. More advanced can play each track to individual output - that's what I need. But - I need to know to play one file properly... :-)

Jacek
0
 
LVL 8

Accepted Solution

by:
gmayo earned 134 total points
ID: 9926824
If you're not faint of heart, you could have a look at DirectSound (part of DirectX). I haven't gone into the sound part of DX much, but I'm sure you have far more control over sound than with the standard Windows APIs. Delphi translations of the headers are available at http://clootie.narod.ru/ . I think you can get them from www.delphi-jedi.org/ as well, but that site seems to be down right now, so I can't check.

Geoff M.
0
 
LVL 12

Assisted Solution

by:Lee_Nover
Lee_Nover earned 133 total points
ID: 9926827
I suggest you find ACMCompo <=1.4 version on the internet .. it was free with source (deAudio predecessor)
from there you could learn how to prepare/play audio data
sorry I can't be of more help
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 9926914
oh yes .. DirectSound :)
I've started something but never finished .. the playing part works fine
you only need to find out how to set on what output to play :)

http://leenover.homeip.net/isapi/pas2html.dll/pas2html?File=/delphi/lnDS
0
 

Author Comment

by:JacekH
ID: 9927354
Lee_Nover - I tried your codes but there some errors. And I'd like to learn hot to play it with WinAPI. So - maybe any suggestion to play one file ;-) but not with SoundPlay or MediaPlayer ;-) 'Real' wave streaming.

?

Jacek
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 9927394
there should be no errors .. there just isn't the complete implementation - no sample app
you're obviously missing some units .. my guess is DirectSound from delphi-jedi
I didn't say that it's a complete product :) .. that's the base point to help you start if you go for DirectSound
0
 

Author Comment

by:JacekH
ID: 9927694
I have DirectSound (Jedi). What I did: just new project doingo nothing - only uses included,

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,lnDSound, lnDSAudio, lnCapiAudio, lnBuffer;

All found.  But:
unit lnDSAudio;  
*** Undeclared identifier
    FDSIn: TlnDSIn;
    FDSOut: TlnDSOut;
-----------------------------
Is DirectSound the only solution? How can I play using WinAPI? :(

Jacek
0
 
LVL 8

Expert Comment

by:gmayo
ID: 9927725
Have you tried the Clootie headers (not Jedi)?

Geoff M.
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 9927802
omg ! these jedi headers are totaly ****ed up .. names changed in structures and params in functions .. lovely

you only need TlnDSBuffer and TlnDSOut from unit lnDSAudio
0
 

Expert Comment

by:Engwi
ID: 9927884
JacekH

Have you tried the PlaySound function in the winmm.dll ??

Do a tdump on the dll, I think the function name is : PlaySoundA

Regards
 Engwi
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 

Author Comment

by:JacekH
ID: 9934959
I do need that code :( I used Turbo Pascal year ago and I'm quite new to Object Pascal. So I need a good example [comments are welcome ;-)]   Well, let's try with simpler question. Please, give me a full example of project to play wave with streaming, not loading the whole file to RAM and using PlaySound or MediaPlayer. It should can play even 1 hour long wave file, started with opening the port, preparing buffers etc. and finally callback a message at the end of playing. I've increased to 400 points.

Jacek
0
 
LVL 6

Assisted Solution

by:GloomyFriar
GloomyFriar earned 133 total points
ID: 9937357
The working code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

class KPlayer
{
public:
   KPlayer(char *FileName);
   BOOL IsPlaing(void);

private:
   HANDLE m_hThread;
   DWORD m_idThread;
   char m_FileName[_MAX_PATH + 1];
   static DWORD WINAPI PlayThreadFunc(LPVOID lpParameter);
};

KPlayer::KPlayer(char *FileName)
{
  strcpy(m_FileName, FileName);
  m_hThread = CreateThread(NULL, 0, PlayThreadFunc, this, 0, &m_idThread);
  if (NULL == m_hThread)
  {
    printf("ERROR: Can't create thread [%d]", GetLastError());
  }
}

BOOL KPlayer::IsPlaing(void)
{
  if (WAIT_TIMEOUT == WaitForSingleObject(m_hThread, 0))
  {
    return TRUE;
  }
  else
  {
    return FALSE;
  }
}

DWORD WINAPI KPlayer::PlayThreadFunc(LPVOID lpParameter)
{
  KPlayer *pPlayerObj = (KPlayer *)lpParameter;
  PlaySound(pPlayerObj->m_FileName, NULL, SND_SYNC);
  return 0;
}

int main(int argc, char **argv)
{
  if (argc < 2)
  {
    printf("\nUsage: %s FileName\n", argv[0]);
    exit(-1);
  }

  KPlayer player(argv[1]);

  for (int i=0; i<1000; i++)
  {
    if (player.IsPlaing())
    {
      printf("Still playing...\n");
    }
    else
    {
      printf("Play stopped.\n");
    }
   
    Sleep(15);
  }

  return 0;
}
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9937372
Ooops. Sorry. I've posted to wrong thread. Sorry again.

Nevertheless if you translate my code to pascal it'll be a solution ;-)
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9937381
Here is the Delphi code:

///////////////////////////////////////
unit th_splayer;

interface

uses
  Classes, mmsystem;

type
  KSPlayer = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

implementation


{ KSPlayer }

procedure KSPlayer.Execute;
begin
  { Place thread code here }
  while not Terminated do begin
    PlaySound('siren.wav', 0, 0);
  end;
end;

end.
////////////////////////////////////////
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 9937641
reread the question guys ... he needs to play the sound at different outputs !
PlaySound and alike use the default output
0
 

Author Comment

by:JacekH
ID: 9937950
GloomyFriar:
I wrote about PlaySound. It's not a solution, look above why.

I spent a lot of time and finally have found a code at Borland - the old one recorder/player. It looks that's what I need. Almost. Almost because it plays one file to one output. I'm not sure if I can play two files in perfect sample sync using this code. Maybe threads... I'll try. But question is still valid - if you have a good simple code [with comments ;-) ]  I'll pay all these 400 points.

Jacek
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 9938234
it's an interesting thing and I'd look into it if I had time
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9940764
>Almost because it plays one file to one output.
To play to different devices, use different device handlers.

>I'm not sure if I can play two files in perfect sample sync using this code.
I think, It's impposible, if you hardware have not some special function for it.
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9940806
>Almost because it plays one file to one output.
To play to different devices, use different device handlers.

>I'm not sure if I can play two files in perfect sample sync using this code.
I think, It's impposible, if you hardware have not some special function for it.
0
 

Author Comment

by:JacekH
ID: 9940995
From MM SDK:
"The sndPlaySound and PlaySound functions load an entire waveform-audio file into memory and, in effect, limit the size of file they can play. Use sndPlaySound and PlaySound to play waveform-audio files that are relatively small &#318; up to about 100K. These two functions also require the sound data to be in a format that is playable by one of the installed waveform-audio drivers, including the wave mapper."

So it's not streaming.

How about OpenASIO?

Jacek
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9941783
I can give you the sample code showing how to play sound with waveOutOpen/waveOutPrepareHeader/waveOutWrite.
But the sample in C not Delphi.
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now