www.pudn.com > 播放mp3的控件.rar > Audio.cpp


#include "common.h" 
#include "player.h" 
#include  
#include "args.h" 
#include  
#include  
 
#ifdef _DIRECTX 
#include  
 
static LPDIRECTSOUND lpDirectSound = NULL;          
static LPDIRECTSOUNDBUFFER DSBuffer = NULL; 
static bool DSInit = false; 
DWORD BufPos = 0; 
DWORD BufSize = 0; 
 
#endif 
 
// Copy of Args->OutMode, set in constructor 
esOutputMode OutMode; 
// Stuff for the wavfile renderer 
HMMIO    hmmioOut; 
MMIOINFO mmioinfoOut; 
MMCKINFO ckOutRIFF; 
MMCKINFO ckOut; 
 
// A internal copy of Args->BufferSize(set by constructor) 
unsigned long BufferSize; 
// Number of queued buffers 
long BufferCount; 
// Our critical section for wave out 
//CRITICAL_SECTION cs; 
// Needed for constructor 
WAVEFORMATEX WAVFx, outFormatex; 
// Our wavedevice handle for mmsystem output  
HWAVEOUT WaveDevice; 
// Copy of the pointer given in the constructor 
 
// Audio system events 
// 0 = Break event,deactivates PlaySamples function and Header unprepare cb  
// 1 = Callback notification,set to true when new data is to be send 
// 2 = 
// 3 = DirectSound "decoding stopped" notification 
bool MPAudioEvents[4]; 
 
bool Audio_Initialized = false; 
 
// Executes user defined function and give it the sample data and information 
bool ExecuteCallback(void *Data, DWORD len)   
{ 
    if (((Args->UseHdrCB) || (Args->OutputMode == omCallback)) && (Args->BufferCB))  
    { 
        unsigned long TSampleRate = Args->SampleRate; 
        unsigned long TSize = len; 
        long TBits = Args->AudioBits; 
        long TChannels = Args->Channels;         
        return Args->BufferCB(Data, TBits, TChannels, TSampleRate, TSize, Args->OutCBData); 
    } else  
        return true; 
} 
 
// Callback for queued WAVEHDR unprepare 
void CALLBACK wave_callback(HWAVE hWave, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) 
{ 
    WAVEHDR *Wh=(WAVEHDR *)dwParam1;  
    HGLOBAL WHMemHnd,DataMemHnd; 
     
    if (uMsg == WOM_DONE)  
    { 
        // If break flag is set, return  
        if(MPAudioEvents[0]) return;         
         
        // Unprepare header w/ cs's 
        //EnterCriticalSection(&cs);  
        waveOutUnprepareHeader((HWAVEOUT)hWave, Wh, sizeof(WAVEHDR)); 
        // Free the allocated memory from the chunk 
        WHMemHnd=GlobalHandle(Wh); 
        ExecuteCallback(Wh->lpData, Wh->dwBufferLength); 
        DataMemHnd=GlobalHandle(Wh->lpData); 
         
        GlobalUnlock(DataMemHnd); 
        GlobalUnlock(WHMemHnd); 
        GlobalFree(DataMemHnd); 
        GlobalFree(WHMemHnd); 
        // Decrease count of queued buffers 
        BufferCount--; 
        //LeaveCriticalSection(&cs); 
    } 
} 
 
#ifdef _DIRECTX 
 
volatile LPVOID DoubleBuffer = NULL; 
volatile DWORD  DXBufferSize = 0; 
volatile DWORD  DXBufferPos = 0; 
volatile HANDLE ReadyEvent = NULL;  
volatile HANDLE StoppedEvent = NULL;  
CRITICAL_SECTION BufferAccess; 
volatile UINT  Timer = 0; 
volatile bool  m_fPlayingDX, PauseDX; 
volatile DWORD m_cbBufOffset; 
 
bool WriteToBuffer(LPVOID buf, DWORD len)  
{ 
    EnterCriticalSection(&BufferAccess); 
    if (DXBufferPos + len >= DXBufferSize)  
    { 
        LeaveCriticalSection(&BufferAccess); 
        return false; 
    } 
    MoveMemory((void *)((char *)DoubleBuffer + DXBufferPos), buf, len); 
    DXBufferPos += len; 
    if (DXBufferSize - DXBufferPos < Args->BufferSize) ResetEvent(ReadyEvent); 
    LeaveCriticalSection(&BufferAccess); 
    return true; 
} 
 
_inline unsigned char GetSilenceChar() 
{ 
    if (Args->AudioBits == 16)  
        return 0; 
    else  
        return 0x80; 
} 
 
bool WriteSilence(DWORD size)  
{ 
    unsigned char sil_char = GetSilenceChar(); 
     
    EnterCriticalSection(&BufferAccess); 
    HRESULT hr; 
    LPBYTE lpbuf1 = NULL; 
    LPBYTE lpbuf2 = NULL; 
    DWORD dwsize1 = 0; 
    DWORD dwsize2 = 0; 
    DWORD dwbyteswritten1 = 0; 
    DWORD dwbyteswritten2 = 0; 
     
    // Lock the sound buffer 
    try  
    { 
        hr = DSBuffer->Lock (m_cbBufOffset, size, (void **)&lpbuf1, &dwsize1, (void **)&lpbuf2, &dwsize2, 0); 
        if (hr != DS_OK)  
            throw 1; 
        memset(lpbuf1, sil_char, dwsize1); 
        if (lpbuf2)  
            memset(lpbuf2, sil_char, dwsize2); 
        else dwsize2 = 0; 
         
        DSBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); 
        m_cbBufOffset = (m_cbBufOffset + dwsize1 + dwsize2) % DXBufferSize; 
    } 
    catch (...) 
    { 
        LeaveCriticalSection(&BufferAccess); 
        return false; 
    }         
    LeaveCriticalSection(&BufferAccess); 
    return true; 
} 
 
DWORD GetMaxWriteSize (void) 
{ 
    DWORD dwWriteCursor, dwPlayCursor, dwMaxSize; 
     
    // Get current play position 
    dwWriteCursor = dwPlayCursor = 0; 
    if (DSBuffer->GetCurrentPosition(&dwPlayCursor, &dwWriteCursor) == DS_OK) 
    { 
        if (m_cbBufOffset <= dwPlayCursor) 
            dwMaxSize = dwPlayCursor - m_cbBufOffset; 
        else // (m_cbBufOffset > dwPlayCursor) 
            dwMaxSize = DXBufferSize - m_cbBufOffset + dwPlayCursor; 
    } 
    else 
    { 
        // GetCurrentPosition call failed 
        LastError = peOutputError; 
        throw 1; 
    } 
    return (dwMaxSize); 
} 
 
bool SendToDX(DWORD size) 
{ 
    EnterCriticalSection(&BufferAccess); 
    HRESULT hr; 
    LPBYTE lpbuf1 = NULL; 
    LPBYTE lpbuf2 = NULL; 
    DWORD dwsize1 = 0; 
    DWORD dwsize2 = 0; 
    DWORD dwbyteswritten1 = 0; 
    DWORD dwbyteswritten2 = 0; 
     
    // Lock the sound buffer 
    try  
    { 
        hr = DSBuffer->Lock (m_cbBufOffset, size, (void **)&lpbuf1, &dwsize1, (void **)&lpbuf2, &dwsize2, 0); 
        if (hr != DS_OK)  
            throw 1; 
        MoveMemory(lpbuf1, DoubleBuffer, dwsize1); 
        if (lpbuf2)  
            MoveMemory(lpbuf2, (void *)((char *)DoubleBuffer + dwsize1), dwsize2); 
        else  
            dwsize2 = 0; 
        MoveMemory(DoubleBuffer, (void *)((char *)DoubleBuffer + dwsize1 + dwsize2), DXBufferPos - (dwsize1 + dwsize2)); 
        DXBufferPos -= dwsize1 + dwsize2; 
        DSBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); 
        m_cbBufOffset = (m_cbBufOffset + dwsize1 + dwsize2) % DXBufferSize; 
    } 
    catch (...) 
    { 
        LeaveCriticalSection(&BufferAccess); 
        return false; 
    }         
    LeaveCriticalSection(&BufferAccess); 
    if (DXBufferSize - DXBufferPos >= Args->BufferSize) SetEvent(ReadyEvent); 
    return true; 
} 
 
void StopDX(); 
void CALLBACK TimeProcDX(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); 
 
void StartDX() 
{ 
    StopDX(); 
     
    // Cue for playback if necessary 
    /*if (!m_fCued) 
    { 
    Cue (); 
    } 
    */ 
    DSBuffer->SetCurrentPosition(0); 
    WriteSilence(DXBufferSize);     
    // Begin DirectSound playback 
    m_cbBufOffset = 0; 
    HWND OldFocus = GetFocus(); 
    SetFocus(Args->wnd); 
    HRESULT hr = DSBuffer->Play (0, 0, DSBPLAY_LOOPING);     
    SetFocus(OldFocus); 
    if (hr == DS_OK) 
    { 
        Timer = timeSetEvent(Args->BufferSize * 1000 / (Args->Channels * Args->SampleRate * Args->AudioBits / 8), 0,  &TimeProcDX, 0, TIME_ONESHOT | TIME_CALLBACK_FUNCTION); 
        if (!Timer)  
        { 
            int i = GetLastError(); 
            LastError = peOutputError;            
            throw 1; 
        } 
         
        // Playback begun, no longer cued 
        m_fPlayingDX = TRUE; 
        //m_fCued = FALSE; 
    } 
    else 
    { 
        LastError = peOutputError; 
        throw 1; 
    } 
} 
 
void StopDX() 
{ 
    if (m_fPlayingDX) 
    { 
        // Stop DirectSound playback 
        // Delete Timer object 
        MPAudioEvents[3] = 1;         
        if (Timer)  
            timeKillEvent(Timer); 
        Timer = 0; 
        DSBuffer->Stop ();         
        m_fPlayingDX = FALSE; 
    } 
} 
 
void CALLBACK TimeProcDX(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) 
{ 
    if (Timer) { 
        timeKillEvent(Timer); 
        Timer = 0; 
    } 
    if (!MPAudioEvents[3] || DXBufferPos) 
    {   // All of sound not played yet, send more data to buffer 
        DWORD dwFreeSpace = GetMaxWriteSize (); 
         
        // Determine free space in sound buffer 
        if (dwFreeSpace) 
        {   // See how much wave data remains to be sent to buffer 
            EnterCriticalSection(&BufferAccess); 
            DWORD dwDataRemaining; 
            if (PauseDX)   
                dwDataRemaining = 0; 
            else 
                dwDataRemaining = DXBufferPos; 
            if (dwDataRemaining == 0) 
            {   // All wave data has been sent to buffer 
                // Fill free space with silence 
                LeaveCriticalSection(&BufferAccess); 
                if (WriteSilence (dwFreeSpace) == false) 
                {   // Error writing silence data 
                    LastError = peOutputError; 
                    throw 1; 
                }                 
            } 
            else  
            { 
                if (dwDataRemaining >= dwFreeSpace) 
                {   // Enough wave data remains to fill free space in buffer 
                    // Fill free space in buffer with wave data 
                    LeaveCriticalSection(&BufferAccess); 
                    if (SendToDX(dwFreeSpace) == false) 
                    {   // Error writing wave data 
                        LastError = peOutputError; 
                        throw 1; 
                    } 
                } 
                else 
                {   // Some wave data remains, but not enough to fill free space 
                    // Send wave data to buffer, fill remainder of free space with silence 
                    LeaveCriticalSection(&BufferAccess); 
                    if (SendToDX(dwDataRemaining)) 
                    { 
                        if (WriteSilence (dwFreeSpace - dwDataRemaining) == false) 
                        { 
                            // Error writing silence data 
                            LastError = peOutputError; 
                            throw 1; 
                        } 
                    } 
                    else 
                    {   // Error writing wave data 
                        LastError = peOutputError; 
                        throw 1; 
                    } 
                } 
            } 
        }         
        Timer = timeSetEvent(Args->BufferSize * 1000 / (Args->Channels * Args->SampleRate * Args->AudioBits / 8), 0,  TimeProcDX, 0, TIME_ONESHOT | TIME_CALLBACK_FUNCTION); 
    } 
    else 
    { 
        // All of sound has played, stop playback 
        StopDX (); 
        SetEvent(StoppedEvent); 
    }  
}  
 
#endif 
 
const IID IID_IDirectSound = {0x279AFA83, 0x4981, 0x11CE, {0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60}}; 
const IID CLSID_DirectSound = {0x47d4d946, 0x62e8, 0x11cf, {0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0}}; 
 
bool Init_MPAudio(){ 
    OutMode = Args->OutputMode; 
    if (OutMode==omCallback) { 
        if ((!Args->BufferCB) || (!Args->OutActionCB))  
        { 
            LastError = peOutputError; 
            throw 1; 
        } 
        if (!Args->OutActionCB(true, Args->OutCBData))  
        { 
            LastError = peOutputError; 
            throw 1; 
        } 
    } 
    // Output via wave mapper 
    if(OutMode==omMMSystem) { 
        HGLOBAL CBMem; 
        // Get device capatibilities to detect sound 
        if (!waveOutGetNumDevs()) { 
            LastError = peOutputError; 
            throw 1; 
        }  
        // Initialize waveout formatex structure with the options 
        BufferCount = 0; 
        outFormatex.wFormatTag        = WAVE_FORMAT_PCM; 
        outFormatex.wBitsPerSample    = Args->AudioBits; 
        outFormatex.nChannels         = Args->Channels; 
        if (Args->SampleRate > 44100) 
        { 
            outFormatex.nSamplesPerSec    = 44100; 
        } 
        else 
            outFormatex.nSamplesPerSec    = Args->SampleRate; 
         
        outFormatex.nAvgBytesPerSec   = outFormatex.nSamplesPerSec*Args->Channels*(Args->AudioBits/8); 
        outFormatex.nBlockAlign       = Args->Channels*(Args->AudioBits/8); 
         
        // Initialize our callback buffer, preventing that the user have 
        // to allocate one in his callback function(gains speed ;) 
        BufferSize = Args->BufferSize;  
         
        for(int i=0;i<4;i++) MPAudioEvents[i]=false; 
        // Open the device 
        MMRESULT res=waveOutOpen(&WaveDevice,(UINT)Args->AudioDevice,&outFormatex,(DWORD)wave_callback,0,CALLBACK_FUNCTION); 
        if(res!=MMSYSERR_NOERROR){ 
            LastError = peOutputError; 
            throw 1; 
        } 
        // Reset the wavedevice 
        waveOutReset(WaveDevice); 
        // Sleep a bit 
        Sleep(77); 
        if (Args->UseHdrCB)  
        { 
            if ((!Args->OutActionCB) || (!Args->OutActionCB(true, Args->OutCBData)))  
            { 
                Args->UseHdrCB = false; 
            } 
        }         
        Audio_Initialized = true; 
        return true; 
    } 
#ifdef _DIRECTX 
    if (OutMode==omDirectSound) { 
        DSInit = false; 
        if (!OLEInit) { 
            LastError = peInternalError; 
            throw 1; 
        } 
        try  
        { 
            lpDirectSound = NULL; 
            HRESULT       hr; 
            hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound,(void **) &lpDirectSound); 
            if (hr != S_OK) { 
                LastError = peOutputError; 
                throw 1; 
            } 
            hr = lpDirectSound->Initialize(NULL); //hr = DirectSoundCreate(NULL, &lpDirectSound, NULL); 
            if (hr != S_OK) { 
                LastError = peOutputError; 
                throw 1; 
            } 
            hr = lpDirectSound->SetCooperativeLevel(Args->wnd, DSSCL_PRIORITY); 
            if (hr != S_OK) {             
                lpDirectSound->Release(); 
                lpDirectSound = NULL; 
                LastError = peOutputError; 
                throw 1; 
            } 
            WAVEFORMATEX wf;      
            DSBUFFERDESC dsbdesc;      
            // Set up wave format structure.  
            memset(&wf, 0, sizeof(WAVEFORMATEX));  
            wf.wFormatTag = WAVE_FORMAT_PCM;      
            wf.nChannels = Args->Channels;  
            wf.nSamplesPerSec = Args->SampleRate;      
            wf.nBlockAlign = Args->Channels*(Args->AudioBits/8); 
            wf.nAvgBytesPerSec = Args->SampleRate*Args->Channels*(Args->AudioBits/8); 
            wf.wBitsPerSample = Args->AudioBits; 
            wf.cbSize = 0; 
             
            BufSize = Args->Buffers * Args->BufferSize; 
            BufPos = 0; 
             
            memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));  
            dsbdesc.dwSize = sizeof(DSBUFFERDESC);  
            dsbdesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME; 
            dsbdesc.dwBufferBytes = BufSize;  
            dsbdesc.lpwfxFormat = &wf; 
             
            hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, &DSBuffer, NULL); 
            if(DS_OK == hr) {  
                Audio_Initialized = true;             
                 
                InitializeCriticalSection(&BufferAccess);  
                ReadyEvent = CreateEvent(NULL, false, true, NULL); 
                StoppedEvent = CreateEvent(NULL, false, true, NULL); 
                DoubleBuffer = malloc(BufSize); 
                DXBufferSize = BufSize; 
                DXBufferPos = 0; 
                PauseDX = false; 
                Timer = 0; 
                MPAudioEvents[3] = 0; 
                m_fPlayingDX = false; 
                m_cbBufOffset = 0; 
                if (Args->UseHdrCB)  
                { 
                    if ((!Args->OutActionCB) || (!Args->OutActionCB(true, Args->OutCBData)))  
                    { 
                        Args->UseHdrCB = false; 
                    } 
                } 
                return true; 
            } 
            LastError = peOutputError; 
            throw 1; 
        } catch (...)  
        { 
            if (DSBuffer)  
                DSBuffer->Release(); 
            DSBuffer = NULL; 
            if (lpDirectSound)  
                lpDirectSound->Release(); 
            lpDirectSound = NULL; 
            if (DoubleBuffer)  
            { 
                DoubleBuffer = NULL; 
                free(DoubleBuffer); 
            } 
            if (ReadyEvent)  
            { 
                CloseHandle(ReadyEvent); 
                ReadyEvent = NULL; 
            } 
            if (StoppedEvent)  
            { 
                CloseHandle(StoppedEvent); 
                StoppedEvent = NULL; 
            } 
            throw 1; 
        } 
    } 
#endif 
    // WAV file output 
    if(OutMode==omWaveFile)  
    { 
        // Open our wavefile 
        if(!(hmmioOut=mmioOpen(Args->OutName,NULL,MMIO_ALLOCBUF|MMIO_WRITE|MMIO_CREATE))) { 
            LastError = peOutputError; 
            throw 1; 
        } 
        // Set the header in the structure 
        ckOutRIFF.fccType=mmioFOURCC('W', 'A', 'V', 'E'); 
        ckOutRIFF.cksize=0; 
        // Create chunk 
        if(mmioCreateChunk(hmmioOut,&ckOutRIFF,MMIO_CREATERIFF)!=MMSYSERR_NOERROR) { 
            LastError = peOutputError; 
            throw 1; 
        } 
        // Now set our audio options... 
        WAVFx.wFormatTag        = WAVE_FORMAT_PCM; 
        WAVFx.wBitsPerSample    = Args->AudioBits; 
        WAVFx.nChannels         = Args->Channels; 
        WAVFx.nSamplesPerSec    = Args->SampleRate; 
        WAVFx.nAvgBytesPerSec   = WAVFx.nSamplesPerSec*WAVFx.nChannels*WAVFx.wBitsPerSample/8; 
        WAVFx.nBlockAlign       = WAVFx.nChannels*WAVFx.wBitsPerSample/8; 
        WAVFx.cbSize          = 0; 
        ckOut.ckid=mmioFOURCC('f', 'm', 't', ' '); 
        ckOut.cksize=sizeof(WAVFx); 
        // ...and store it 
        if(mmioCreateChunk(hmmioOut,&ckOut,0)!=MMSYSERR_NOERROR) { 
            LastError = peOutputError; 
            throw 1; 
        } 
        // Write the stuff  
        if(mmioWrite(hmmioOut,(HPSTR)&WAVFx,sizeof(WAVFx))!=sizeof(WAVFx)){ 
            LastError = peOutputError; 
            throw 1; 
        } 
        if(mmioAscend(hmmioOut,&ckOut,0)!=MMSYSERR_NOERROR) { 
            LastError = peOutputError; 
            throw 1; 
        } 
        ckOut.ckid=mmioFOURCC('d', 'a', 't', 'a'); 
        ckOut.cksize=0; 
        if(mmioCreateChunk(hmmioOut,&ckOut,0)!=MMSYSERR_NOERROR){ 
            LastError = peOutputError; 
            throw 1; 
        } 
        /* 
        if (MMRESULT res = mmioGetInfo(hmmioOut, &mmioinfoOut,0)) { 
        throw 1; 
    }*/ 
        Audio_Initialized = true; 
        if (Args->UseHdrCB)  
        { 
            if ((!Args->OutActionCB) || (!Args->OutActionCB(true, Args->OutCBData)))  
            { 
                Args->UseHdrCB = false; 
            } 
        } 
        return true; 
    } 
    return false; 
} 
 
// Returns the buffer load in percent 
long GetBufferload(){ 
    if(OutMode==omMMSystem){ 
        unsigned long result; 
        result=((BufferCount-1) * Args->BufferSize)/((Args->Buffers * Args->BufferSize)/100); 
        if(result>=100) result=0;if(result<=0) result=0; 
        return result; 
    }; 
#ifdef _DIRECTX 
    if (OutMode == omDirectSound)  
    { 
        return DXBufferPos * 100 / DXBufferSize; 
    } 
#endif 
    return 0; 
} 
 
// Store "buf" with "len" size in buffer   
long PlaySamples(unsigned char *buf, long len){ 
    // Realtime sound output  
    if (OutMode==omCallback)  
    { 
        if ((MPAudioEvents[0]) || (MPAudioEvents[2])) return -1;  
        if (!ExecuteCallback(buf, len))  
        { 
            LastError = peOutputError; 
            throw 1; 
        } 
        return len; 
    } 
    if(OutMode==omMMSystem) 
    { 
        LPWAVEHDR WaveHeader;  
        HGLOBAL WHMemHnd,DataMemHnd; 
        // Return on break flag 
        if ((MPAudioEvents[0]) || (MPAudioEvents[2])) return -1;  
        // If callback flag set, execute it 
        // This copies the sampledata to our callback buffer and sets 
        // event 1(Execute callback,please) which will be handled by  
        // PlaySamples() because we can't execute the callback directly from here. 
        if(Args->UseHdrCB)  
        { 
            ExecuteCallback(buf, len); 
        }  
        // If buffer is full, wait 
        while (BufferCount >= Args->Buffers)  
        { 
            Sleep(77); 
            if ((MPAudioEvents[0]) || (MPAudioEvents[2])) return -1; 
        } 
        // Allocate "len" size buffer, and copy "buf" in it 
        WHMemHnd = GlobalAlloc(GMEM_MOVEABLE,sizeof(WAVEHDR)); 
        DataMemHnd=GlobalAlloc(GMEM_MOVEABLE,len); 
        WaveHeader=(LPWAVEHDR)GlobalLock(WHMemHnd); 
        WaveHeader->lpData=(LPSTR)GlobalLock(DataMemHnd); 
        CopyMemory(WaveHeader->lpData,buf,len); 
        WaveHeader->dwBufferLength=len; 
        WaveHeader->dwFlags=0; 
        // Prepare the header for output 
        // EnterCriticalSection(&cs); 
        MMRESULT Res=waveOutPrepareHeader(WaveDevice,WaveHeader,sizeof(WAVEHDR)); 
        if(Res) { 
            GlobalFree(DataMemHnd); 
            GlobalFree(WHMemHnd);   
            // LeaveCriticalSection(&cs); 
            LastError = peOutputError; 
            throw 1;             
        } 
        // Send it to mmsystem 
        Res = waveOutWrite(WaveDevice,WaveHeader,sizeof(WAVEHDR)); 
        if(Res) { 
            GlobalFree(DataMemHnd); 
            GlobalFree(WHMemHnd);   
            // LeaveCriticalSection(&cs); 
            LastError = peOutputError; 
            throw 1;             
        } 
        BufferCount++; 
        //LeaveCriticalSection(&cs);         
        return(len); 
    } 
#ifdef _DIRECTX 
    if (OutMode == omDirectSound) { 
        if (!m_fPlayingDX) StartDX(); 
        if ((MPAudioEvents[0]) || (MPAudioEvents[2])) return -1;  
        // If callback flag set, execute it 
        if(MPAudioEvents[1]){ 
            // Send data to user  
            if(Args->UseHdrCB) ExecuteCallback(buf, len); 
            // Reset event 
            MPAudioEvents[1]=false; 
        } 
        while (!WriteToBuffer(buf, len)) { 
            while (!MPAudioEvents[3])  
                if (WaitForSingleObject(ReadyEvent, 50) == WAIT_OBJECT_0) break; 
                if (MPAudioEvents[3]) break; 
        } 
    } 
#endif 
    if(OutMode == omWaveFile)  
    { 
        unsigned long i; 
        if ((MPAudioEvents[0]) || (MPAudioEvents[2])) return -1;  
        // If callback flag set, execute it 
        if(MPAudioEvents[1]){ 
            // Send data to user  
            if(Args->UseHdrCB) ExecuteCallback(buf, len); 
            // Reset event 
            MPAudioEvents[1]=false; 
        }          
        // Write the buffer to file 
        i = mmioWrite(hmmioOut, (const char *) buf, len); 
        Sleep(20); 
        return i; 
        /*for(i=0; iOutputMode == omMMSystem) { 
         
        WAVEOUTCAPS pwoc; 
        if (waveOutGetDevCaps(Args->AudioDevice, &pwoc, sizeof(pwoc)) == MMSYSERR_NOERROR) { 
            *Separate = pwoc.dwSupport & WAVECAPS_LRVOLUME; 
            return (pwoc.dwSupport & WAVECAPS_VOLUME); 
        } else  
            return false; 
    } 
#ifdef _DIRECTX 
    if (Args->OutputMode == omDirectSound) { 
        Separate = false; 
        return true; 
    } 
#endif 
    return false; 
} 
 
bool Audio_SetVolume(unsigned long Volume) { 
    if (Args->OutputMode == omMMSystem) { 
        if (Audio_Initialized) { 
            return (waveOutSetVolume(WaveDevice, Volume) == MMSYSERR_NOERROR); 
        } else  { 
            HWAVEOUT phwo; 
            WAVEFORMATEX outFormatex; 
            outFormatex.wFormatTag        = WAVE_FORMAT_PCM; 
            outFormatex.wBitsPerSample    = Args->AudioBits; 
            outFormatex.nChannels         = Args->Channels; 
            outFormatex.nSamplesPerSec    = Args->SampleRate; 
            outFormatex.nAvgBytesPerSec   = Args->SampleRate*Args->Channels*(Args->AudioBits/8); 
            outFormatex.nBlockAlign       = Args->Channels*(Args->AudioBits/8); 
            outFormatex.cbSize = 0; 
            if(waveOutOpen(&phwo, Args->AudioDevice, &outFormatex, 0, 0, CALLBACK_NULL | WAVE_ALLOWSYNC) == MMSYSERR_NOERROR) { 
                bool res; 
                res = (waveOutSetVolume(phwo, Volume) == MMSYSERR_NOERROR); 
                waveOutClose(phwo); 
                if (!res) LastError = peOutputError; 
                return res; 
            } else  
                return false;         
        } 
    } 
#ifdef _DIRECTX 
    if (Args->OutputMode == omDirectSound) { 
        if (DSBuffer) { 
            DWORD X = (Volume & 0xFFFF) * ((DSBVOLUME_MAX - DSBVOLUME_MIN) / 65535.0) + DSBVOLUME_MIN; 
            DSBuffer->SetVolume(X); 
            return true; 
        } 
        else { 
            LastError = peIncorrectMode; 
            return false; 
        } 
    } 
#endif     
    return false; 
} 
 
bool Audio_GetVolume(unsigned long *Volume) { 
    if (Args->OutputMode == omMMSystem) { 
        if (Audio_Initialized) { 
            return (waveOutGetVolume(WaveDevice, Volume) == MMSYSERR_NOERROR); 
        } else  { 
            HWAVEOUT phwo; 
            WAVEFORMATEX outFormatex; 
            outFormatex.wFormatTag        = WAVE_FORMAT_PCM; 
            outFormatex.wBitsPerSample    = Args->AudioBits; 
            outFormatex.nChannels         = Args->Channels; 
            outFormatex.nSamplesPerSec    = Args->SampleRate; 
            outFormatex.nAvgBytesPerSec   = Args->SampleRate*Args->Channels*(Args->AudioBits/8); 
            outFormatex.nBlockAlign       = Args->Channels*(Args->AudioBits/8); 
            outFormatex.cbSize = 0; 
            if(waveOutOpen(&phwo, Args->AudioDevice, &outFormatex, 0, 0, CALLBACK_NULL | WAVE_ALLOWSYNC) == MMSYSERR_NOERROR) { 
                bool res; 
                res = (waveOutGetVolume(phwo, Volume) == MMSYSERR_NOERROR); 
                waveOutClose(phwo); 
                return res; 
            } else  
                return false;         
        } 
    } 
#ifdef _DIRECTX 
    if (Args->OutputMode == omDirectSound) { 
        if (DSBuffer) { 
            DSBuffer->GetVolume((long *)Volume); 
            *Volume = (*Volume - DSBVOLUME_MIN) * (65535.0 / (DSBVOLUME_MAX - DSBVOLUME_MIN)); 
            *Volume = *Volume << 16 | *Volume;  
            return true; 
        } 
        else { 
            LastError = peIncorrectMode; 
            return false; 
        } 
    } 
#endif     
    return false; 
} 
 
// Destructor 
bool Done_MPAudio(){ 
    // Wave mapper output 
    Audio_Initialized = false; 
    if (OutMode==omCallback) 
    { 
        if (!Args->OutActionCB(false, Args->OutCBData))  
        { 
            return true; 
        } 
    } 
    if(OutMode==omMMSystem)  
    { 
        try 
        { 
            while (waveOutClose(WaveDevice) == WAVERR_STILLPLAYING) Sleep(77); 
        } 
        catch (...) {/* ignore all possible errors */} 
        if (Args->UseHdrCB && Args->OutActionCB) Args->OutActionCB(false, Args->OutCBData); 
        WaveDevice=NULL; 
        BufferCount=0; 
    } 
#ifdef _DIRECTX 
    if (OutMode == omDirectSound)  
    { 
        StopDX(); 
        if (DSBuffer) DSBuffer->Release(); 
        DSBuffer = NULL; 
        if (lpDirectSound) lpDirectSound->Release(); 
        lpDirectSound = NULL; 
        if (DoubleBuffer)  
            free(DoubleBuffer); 
        if (ReadyEvent)  
            CloseHandle(ReadyEvent); 
        if (StoppedEvent)  
            CloseHandle(StoppedEvent); 
        DeleteCriticalSection(&BufferAccess);  
        if (Args->UseHdrCB && Args->OutActionCB) Args->OutActionCB(false, Args->OutCBData); 
    } 
#endif 
    // Wav file output 
    if(OutMode==omWaveFile)  
    { 
        if (Args->UseHdrCB && Args->OutActionCB) Args->OutActionCB(false, Args->OutCBData); 
 
        if(mmioFlush(hmmioOut,MMIO_EMPTYBUF))  
        { 
            return true; 
        } 
        /*mmioinfoOut.dwFlags|=MMIO_DIRTY; 
        if(mmioSetInfo(hmmioOut,&mmioinfoOut,0)!=MMSYSERR_NOERROR){ 
        //throw 1; 
        return; 
    }*/ 
        if(mmioAscend(hmmioOut,&ckOut,0)!=MMSYSERR_NOERROR) 
        { 
            //throw 1; 
            return true; 
        } 
        if(mmioAscend(hmmioOut,&ckOutRIFF,0)!=MMSYSERR_NOERROR) 
        { 
            //throw 1; 
            return true; 
        } 
        if(mmioClose(hmmioOut,0)!=MMSYSERR_NOERROR)  
        { 
            //throw 1; 
            return true; 
        } 
        return true; 
    } 
    return true; 
}