WaveOutStream - playing wave in WinApi

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
JacekHAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Lee_NoverCommented:
what do you mean to different outputs ? front/rear speakers ?
deAudio (commercial) does it nicely .. and much much more :)
0
JacekHAuthor Commented:
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
gmayoCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Cloud Class® Course: SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

Lee_NoverCommented:
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
Lee_NoverCommented:
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
JacekHAuthor Commented:
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
Lee_NoverCommented:
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
JacekHAuthor Commented:
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
gmayoCommented:
Have you tried the Clootie headers (not Jedi)?

Geoff M.
0
Lee_NoverCommented:
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
EngwiCommented:
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
JacekHAuthor Commented:
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
GloomyFriarCommented:
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
GloomyFriarCommented:
Ooops. Sorry. I've posted to wrong thread. Sorry again.

Nevertheless if you translate my code to pascal it'll be a solution ;-)
0
GloomyFriarCommented:
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
Lee_NoverCommented:
reread the question guys ... he needs to play the sound at different outputs !
PlaySound and alike use the default output
0
JacekHAuthor Commented:
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
Lee_NoverCommented:
it's an interesting thing and I'd look into it if I had time
0
GloomyFriarCommented:
>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
GloomyFriarCommented:
>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
JacekHAuthor Commented:
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
GloomyFriarCommented:
I can give you the sample code showing how to play sound with waveOutOpen/waveOutPrepareHeader/waveOutWrite.
But the sample in C not Delphi.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.