123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- //
- // Graphic Scene Engine, http://glscene.org
- //
- (*
- Basic sound manager based on WinMM
- *)
- unit GXS.Sounds.WaveOut;
- interface
- uses
- Winapi.MMSystem,
- System.Classes,
- System.SysUtils,
- GXS.SoundManager,
- GXS.SoundFileObjects;
- type
- (* Basic sound manager based on WinMM WaveOut function.
- This manager has NO 3D miximing capacity, this is merely a default manager
- that should work on any windows based system, and help showcasing/testing
- basic GLSS core functionality.
- Apart from 3D, mute, pause, priority and volume are ignored too, and only
- sampling conversions supported by the windows ACM driver are supported
- (ie. no 4bits samples playback etc.). *)
- TgxSMWaveOut = class(TgxSoundManager)
- protected
- function DoActivate: Boolean; override;
- procedure DoDeActivate; override;
- procedure KillSource(aSource: TgxBaseSoundSource); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure UpdateSources; override;
- published
- property MaxChannels default 4;
- end;
- procedure PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer;
- sampling: TgxSoundSampling); overload;
- function PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer; waveFormat: TWaveFormatEx)
- : HWaveOut; overload;
- // -------------------------------------------------------------
- implementation
- // -------------------------------------------------------------
- type
- TSoundState = (ssPlaying, ssFinished);
- TWaveOutPlayingRec = record
- CurrentState: TSoundState;
- WaveOutDevice: HWaveOut;
- WaveHeader: wavehdr;
- end;
- PWaveOutPlayingRec = ^TWaveOutPlayingRec;
- procedure _waveOutCallBack2(hwo: HWaveOut; uMsg: Cardinal;
- dwInstance, dwParam1, dwParam2: Integer); stdcall;
- begin
- if uMsg = WOM_DONE then
- waveOutClose(hwo);
- end;
- // PlayOnWaveOut (waveformat)
- //
- function PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer; waveFormat: TWaveFormatEx)
- : HWaveOut;
- var
- hwo: HWaveOut;
- wh: wavehdr;
- mmres: MMRESULT;
- begin
- mmres := waveOutOpen(@hwo, WAVE_MAPPER, @waveFormat, Cardinal(@_waveOutCallBack2), 0,
- CALLBACK_FUNCTION);
- Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
- wh.dwBufferLength := lengthInBytes;
- wh.lpData := pcmData;
- wh.dwFlags := 0;
- wh.dwLoops := 1;
- wh.lpNext := nil;
- mmres := waveOutPrepareHeader(hwo, @wh, SizeOf(wavehdr));
- Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
- mmres := waveOutWrite(hwo, @wh, SizeOf(wavehdr));
- Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
- Result := hwo;
- end;
- procedure PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer; sampling: TgxSoundSampling);
- var
- wfx: TWaveFormatEx;
- begin
- wfx := sampling.waveFormat;
- PlayOnWaveOut(pcmData, lengthInBytes, wfx);
- end;
- // ------------------
- // ------------------ TgxSMWaveOut ------------------
- // ------------------
- // Create
- //
- constructor TgxSMWaveOut.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- MaxChannels := 4;
- end;
- destructor TgxSMWaveOut.Destroy;
- begin
- inherited Destroy;
- end;
- function TgxSMWaveOut.DoActivate: Boolean;
- begin
- Result := True;
- end;
- procedure TgxSMWaveOut.DoDeActivate;
- var
- i: Integer;
- begin
- for i := Sources.Count - 1 downto 0 do
- KillSource(Sources[i]);
- end;
- procedure TgxSMWaveOut.KillSource(aSource: TgxBaseSoundSource);
- var
- pRec: PWaveOutPlayingRec;
- begin
- if aSource.ManagerTag <> 0 then
- begin
- pRec := PWaveOutPlayingRec(aSource.ManagerTag);
- if pRec.CurrentState = ssPlaying then
- waveOutReset(pRec.WaveOutDevice);
- waveOutUnprepareHeader(pRec.WaveOutDevice, @pRec.WaveHeader, SizeOf(wavehdr));
- waveOutClose(pRec.WaveOutDevice);
- Dispose(pRec);
- aSource.ManagerTag := 0;
- end;
- end;
- (* Note: This callback function is called from another thread, from MSDN docs:
- "Applications should not call any system-defined functions from inside a
- callback function, except for EnterCriticalSection, LeaveCriticalSection,
- midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage,
- PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent,
- and timeSetEvent. Calling other wave functions will cause deadlock." *)
- procedure _waveOutCallBack(hwo: HWaveOut; uMsg: Cardinal;
- dwInstance, dwParam1, dwParam2: Integer); stdcall;
- begin
- if uMsg = WOM_DONE then
- begin
- PWaveOutPlayingRec(TgxSoundSource(dwInstance).ManagerTag).CurrentState := ssFinished;
- end;
- end;
- procedure TgxSMWaveOut.UpdateSources;
- var
- i, n: Integer;
- wfx: TWaveFormatEx;
- smp: TgxSoundSample;
- wh: wavehdr;
- mmres: MMRESULT;
- hwo: HWaveOut;
- pRec: PWaveOutPlayingRec;
- begin
- // count nb of playing sources and delete done ones
- n := 0;
- for i := Sources.Count - 1 downto 0 do
- if Sources[i].ManagerTag <> 0 then
- if PWaveOutPlayingRec(Sources[i].ManagerTag).CurrentState = ssPlaying then
- Inc(n)
- else
- Sources.Delete(i);
- // start sources if some capacity remains, and forget the others
- for i := Sources.Count - 1 downto 0 do
- if Sources[i].ManagerTag = 0 then
- begin
- if n < MaxChannels then
- begin
- smp := Sources[i].Sample;
- if Assigned(smp) and (smp.Data <> nil) then
- begin
- wfx := smp.Data.sampling.waveFormat;
- mmres := waveOutOpen(@hwo, WAVE_MAPPER, @wfx, Cardinal(@_waveOutCallBack),
- Integer(Sources[i]), CALLBACK_FUNCTION);
- Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
- FillChar(wh, SizeOf(wh), 0);
- wh.dwBufferLength := smp.lengthInBytes;
- wh.lpData := smp.Data.pcmData;
- wh.dwLoops := Sources[i].NbLoops;
- if wh.dwLoops > 1 then
- wh.dwFlags := WHDR_BEGINLOOP + WHDR_ENDLOOP
- else
- wh.dwFlags := 0;
- wh.lpNext := nil;
- new(pRec);
- pRec.WaveOutDevice := hwo;
- pRec.WaveHeader := wh;
- pRec.CurrentState := ssPlaying;
- mmres := waveOutPrepareHeader(hwo, @pRec.WaveHeader, SizeOf(wavehdr));
- Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
- Sources[i].ManagerTag := Integer(pRec);
- mmres := waveOutWrite(hwo, @pRec.WaveHeader, SizeOf(wavehdr));
- Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
- Inc(n);
- end
- else
- Sources.Delete(i);
- end
- else
- Sources.Delete(i);
- end;
- end;
- // -----------------------------------------------
- initialization
- // -----------------------------------------------
- RegisterClasses([TgxSMWaveOut]);
- end.
|