1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441:
| unit UcSoundPlayer;
interface
uses Classes,Windows,MMSystem,Messages, UeSoundState;
type cSoundPlayer=class
private mBuffers:array[0..7] of TWAVEHDR; mBufferSize: integer; mDataEnd: integer; mDataStart: integer; mDevice: HWAVEOUT; mPlayingBufferCount: integer; mRepeat: boolean;
procedure LoadBuffer(Index: integer); function LoadMpeg1File(): PWaveFormatEx; function LoadWaveFile(): PWaveFormatEx;
protected
mBeepAtLeast: boolean; mDesiredLoops: integer; mLoopsToPlay: integer; mOnFinishedPlayback: TNotifyEvent; mSource: TStream; mState: eSoundState; mWnd: HWND;
procedure MsgHandler(var Msg: TMessage);
public
constructor Create; virtual; destructor Destroy; override;
function Pause(): boolean; virtual; function Play(): boolean; virtual; function Reset(): boolean; virtual; function Stop(): boolean; virtual;
property State: eSoundState read mState;
published
property BeepAtLeast: boolean read mBeepAtLeast write mBeepAtLeast; property Loop: boolean read mRepeat write mRepeat; property Loops: integer read mDesiredLoops write mDesiredLoops; property OnFinishedPlayback: TNotifyEvent read mOnFinishedPlayback write mOnFinishedPlayback; property Stream: TStream read mSource write mSource; end;
implementation
uses SysUtils,Forms;
const WAVEHDRSIZE=sizeof(TWAVEHDR); const WAVE_FORMAT_MPEG=$50; const WAVE_FORMAT_MPEGLAYER3=$55; const ACM_MPEG_LAYER1=1; const ACM_MPEG_LAYER2=2; const ACM_MPEG_LAYER3=4; const ACM_MPEG_STEREO=1; const ACM_MPEG_JOINTSTEREO=2; const ACM_MPEG_DUALCHANNEL=4; const ACM_MPEG_SINGLECHANNEL=8; const ACM_MPEG_ID_MPEG1=$10; const MPEGLAYER3_ID_MPEG=1; const MPEGLAYER3_FLAG_PADDING_ON=1; const MPEGLAYER3_FLAG_PADDING_OFF=2; const BUFFERSIZE=4096; const SAMPLINGFREQ: array[0..3] of integer=(44100,48000,32000,0); const ACM_CHANNELS: array[0..3] of integer=(ACM_MPEG_STEREO,ACM_MPEG_JOINTSTEREO,ACM_MPEG_DUALCHANNEL,ACM_MPEG_SINGLECHANNEL); const ACM_LAYERS: array[1..3] of integer=(ACM_MPEG_LAYER1,ACM_MPEG_LAYER2,ACM_MPEG_LAYER3); const BITRATES: array[0..2, 0..15] of integer= ( (0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0), (0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0), (0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0) );
type TWAVHeader=packed record RIFFHeader: packed array [0..3] of Char; FileSize: Longint; WAVEHeader: packed array [0..3] of Char; FormatHeader: packed array [0..3] of Char; FormatSize: Longint; FormatEx:TWaveFormatEx; end;
type TChunk=packed record Signature: LongWord; Length: longint; end;
type TMPEG1WAVEFORMAT=packed record wfx: TWaveFormatEx; fwHeadLayer: word; dwHeadBitrate: DWORD; fwHeadMode: WORD; fwHeadModeExt: WORD; wHeadEmphasis: WORD; fwHeadFlags: WORD; dwPTSLow: DWORD; dwPTSHigh: DWORD; end; type PMPEG1WAVEFORMAT=^TMPEG1WAVEFORMAT;
type TMPEGLAYER3WAVEFORMAT=packed record wfx: tWAVEFORMATEX; wID: WORD; fdwFlags: DWORD; nBlockSize: WORD; nFramesPerBlock: WORD; nCodecDelay: WORD; end; type PMPEGLAYER3WAVEFORMAT=^TMPEGLAYER3WAVEFORMAT;
type EWPlayer=Exception;
{$WARN SYMBOL_DEPRECATED OFF}
constructor cSoundPlayer.Create(); begin mState:=SS_STOPPED; mRepeat:=false; mBeepAtLeast:=false; mWnd:=AllocateHWnd(MsgHandler); end;
destructor cSoundPlayer.Destroy(); begin try Stop(); finally DeallocateHWnd(mWnd); end; inherited; end;
{$WARN SYMBOL_DEPRECATED ON}
procedure cSoundPlayer.LoadBuffer(Index:integer); var lLoadableBufferSize: integer; begin with mBuffers[Index],mSource do begin dwFlags:=0;
lLoadableBufferSize:=mDataEnd-Position-mBufferSize; if lLoadableBufferSize>=0 then lLoadableBufferSize:=mBufferSize else inc(lLoadableBufferSize,mBufferSize);
dwBufferLength:=Read(lpData^,lLoadableBufferSize); if dwBufferLength=0 then exit;
if waveOutPrepareHeader(mDevice,@mBuffers[Index],WAVEHDRSIZE)=MMSYSERR_NOERROR then inc(mPlayingBufferCount); end; end;
function cSoundPlayer.LoadMpeg1File: PWaveFormatEx; var lIndex: integer; lIndex2: integer; lBuffer: PByteArray; lFrame: DWORD; lIsLayer3: boolean; begin Result:=nil; lFrame:=0; lBuffer:=nil; try mSource.Position:=0; try GetMem(lBuffer,BUFFERSIZE); repeat mDataStart:=mSource.Position; lIndex2:=mSource.Read(lBuffer^,BUFFERSIZE); for lIndex:=0 to lIndex2-5 do if(lBuffer^[lIndex]=$FF)and(lBuffer^[lIndex+1]and $F0=$F0)and(lBuffer^[lIndex+1]and 6<>0)then begin lFrame:=PDWORD(longint(lBuffer)+lIndex)^; mDataStart:=mDataStart+lIndex; lIndex2:=0; break; end; if lIndex2<>0 then mSource.Seek(-3,soFromCurrent); until lIndex2=0; finally if lBuffer<>nil then FreeMem(lBuffer); end;
if lFrame=0 then Abort; lIsLayer3:=(lFrame shr 13)and 3=3;
if lIsLayer3 then GetMem(Result,sizeof(TMPEGLAYER3WAVEFORMAT)) else GetMem(Result,sizeof(TMPEG1WAVEFORMAT)); with Result^ do begin if lIsLayer3 then wFormatTag:=WAVE_FORMAT_MPEGLAYER3 else wFormatTag:=WAVE_FORMAT_MPEG; if(lFrame shr 30)and 3=3 then nChannels:=1 else nChannels:=2; nSamplesPerSec:=SamplingFreq[(lFrame shr 18) and 3]; nAvgBytesPerSec:=BITRATES[((lFrame shr 13) and 3)-1,(lFrame shr 20) and $f]*1000 shr 3; nBlockAlign:=1; wBitsPerSample:=0; if lIsLayer3 then cbSize:=sizeof(TMPEGLAYER3WAVEFORMAT)-sizeof(TWAVEFORMATEX) else cbSize:=sizeof(TMPEG1WAVEFORMAT)-sizeof(TWAVEFORMATEX); end;
if lIsLayer3 then with PMPEGLAYER3WAVEFORMAT(result)^ do begin wID:=MPEGLAYER3_ID_MPEG; if lFrame and $20000<>0 then fdwFlags:=MPEGLAYER3_FLAG_PADDING_ON else fdwFlags:=MPEGLAYER3_FLAG_PADDING_OFF; with wfx do if nSamplesPerSec>=32000 then nBlockSize:=1152*nAvgBytesPerSec div nSamplesPerSec else nBlockSize:=576*nAvgBytesPerSec div nSamplesPerSec; nFramesPerBlock:=1; nCodecDelay:=$0571; end else with PMPEG1WAVEFORMAT(Result)^ do begin fwHeadLayer:=ACM_Layers[(lFrame shr 13) and 3]; dwHeadBitrate:=bitrates[((lFrame shr 13) and 3)-1,(lFrame shr 20) and $f]; fwHeadMode:=ACM_Channels[(lFrame shr 30) and 3]; fwHeadModeExt:=0; wHeadEmphasis:=((lFrame shr 24) and 3)+1; dwPTSLow:=0; dwPTSHigh:=0; fwHeadFlags:=ACM_MPEG_ID_MPEG1; end; mDataEnd:=mSource.Size; except if Result<>nil then FreeAndNil(Result); end; end;
function cSoundPlayer.LoadWaveFile: PWaveFormatEx; var lWaveHdr: TWAVHeader; lChunk: TChunk; begin Result:=nil; try mSource.Position:=0; mSource.ReadBuffer(lWaveHdr,sizeof(TWAVHeader)); if lWaveHdr.WAVEHeader<>'WAVE' then Abort; with lWaveHdr.FormatEx do if wFormatTag=WAVE_FORMAT_PCM then begin cbSize:=0; mSource.Position:=lWaveHdr.FormatSize+20; end;
GetMem(Result,lWaveHdr.FormatEx.cbSize+sizeof(TWaveFormatEx)); Result^:=lWaveHdr.FormatEx;
mSource.ReadBuffer(pointer(cardinal(Result)+sizeof(TWaveFormatEx))^,Result^.cbSize); mSource.ReadBuffer(lChunk,sizeof(lChunk)); while lChunk.Signature<>$61746164 do begin mSource.position:=mSource.position+lChunk.Length; mSource.ReadBuffer(lChunk,sizeof(lChunk)); end;
mDataStart:=mSource.position; mDataEnd:=mDataStart+lChunk.Length; except if Result<>nil then FreeAndNil(Result); end; end;
procedure cSoundPlayer.MsgHandler(var Msg: TMessage); var lIndex: integer; begin with Msg do case Msg of MM_WOM_DONE: begin waveOutUnprepareHeader(wParam,PWAVEHDR(lParam),WAVEHDRSIZE); dec(mPlayingBufferCount); if mState=SS_STOPPED then exit; LoadBuffer(PWAVEHDR(lParam)^.dwUser); if PWAVEHDR(lParam)^.dwBufferLength=0 then begin if mRepeat or (mLoopsToPlay>0) then begin mSource.Position:=mDataStart; LoadBuffer(PWAVEHDR(lParam)^.dwUser); waveOutWrite(wParam,PWAVEHDR(lParam),WAVEHDRSIZE); dec(mLoopsToPlay); if mLoopsToPlay<0 then mLoopsToPlay:=0; end else if mPlayingBufferCount<1 then begin mState:=SS_STOPPED; waveOutReset(wParam); waveOutClose(wParam); for lIndex:=0 to 7 do FreeMem(mBuffers[lIndex].lpData); if Assigned(mOnFinishedPlayback) then mOnFinishedPlayback(Self); end; end else waveOutWrite(wParam,PWAVEHDR(lParam),WAVEHDRSIZE); end; else Result:=DefWindowProc(mWnd,Msg,wParam,lParam); end; end;
function cSoundPlayer.Pause(): boolean; begin try case mState of SS_STOPPED: Abort; SS_PLAYING: begin mState:=SS_PAUSED; if waveOutPause(mDevice)<>MMSYSERR_NOERROR then Abort; end; SS_PAUSED: begin mState:=SS_PLAYING; if waveOutRestart(mDevice)<>MMSYSERR_NOERROR then Abort; end; end; result:=true; except result:=false; end; end;
function cSoundPlayer.Play(): boolean; var lWaveInfo: PWaveFormatEx; lIndex: integer; lResult: MMRESULT; begin if mState=SS_PLAYING then begin Result:=false; exit; end; if mState=SS_PAUSED then begin Result:=Pause(); exit; end;
lWaveInfo:=nil; try lWaveInfo:=LoadWaveFile(); if lWaveInfo=nil then lWaveInfo:=LoadMpeg1File(); if lWaveInfo=nil then Abort;
mLoopsToPlay:=mDesiredLoops-1; mPlayingBufferCount:=0;
with lWaveInfo^ do mBufferSize:=nBlockAlign*nSamplesPerSec shr 6;
for lIndex:=0 to 7 do begin GetMem(mBuffers[lIndex].lpData,mBufferSize); mBuffers[lIndex].dwLoops:=1; mBuffers[lIndex].dwUser:=lIndex; end;
lResult:=waveOutOpen(@mDevice,WAVE_MAPPER,lWaveInfo,mWnd,cardinal(self),CALLBACK_WINDOW or WAVE_ALLOWSYNC); if lResult<>MMSYSERR_NOERROR then raise EWPlayer.Create('MM System Error: '+IntToStr(lResult)); FreeMem(lWaveInfo);
mSource.Position:=mDataStart; for lIndex:=0 to 7 do LoadBuffer(lIndex);
mState:=SS_PLAYING; for lIndex:=0 to 7 do if mBuffers[lIndex].dwBufferLength<>0 then waveOutWrite(mDevice,@mBuffers[lIndex],WAVEHDRSIZE);
Result:=true; except mState:=SS_STOPPED;
FreeMem(lWaveInfo); for lIndex:=0 to 7 do FreeMem(mBuffers[lIndex].lpData); Result:=false;
if mBeepAtLeast then Windows.MessageBeep(MB_ICONQUESTION);
mState:=SS_STOPPED; end; end;
function cSoundPlayer.Reset() :boolean; begin try mState:=SS_STOPPED; if waveOutReset(mDevice)<>MMSYSERR_NOERROR then Abort; Result:=true; except Result:=false; end; end;
function cSoundPlayer.Stop(): boolean; var lIndex: integer; begin Result:=false; if mState=SS_STOPPED then exit; try mState:=SS_STOPPED; if waveOutClose(mDevice)<>MMSYSERR_NOERROR then begin for lIndex:=0 to 7 do mBuffers[lIndex].dwBufferLength:=0; lIndex:=16; while (mPlayingBufferCount>0) and (lIndex>0) do begin Sleep(100); dec(lIndex); end; if waveOutClose(mDevice)<>MMSYSERR_NOERROR then Abort; for lIndex:=0 to 7 do FreeMem(mBuffers[lIndex].lpData); end; Result:=true; except end; end;
end. |