#pragma once

#include "kpi_impl.h"
#include "kpi_device.h"

class KbWaveOutDevice : public KbKpiUnknownImpl<IKpiOutDevice>
{
private:
    enum{
        cNumBuffers = 2
    };
    int m_nSampleRate;
    int m_nChannels;
    int m_nBps;
    int m_nBufferSizeSample;
    int m_nBufferSizeBytes;
    int m_nBlockAlign;

    struct WaveOutEvent
    {
        HANDLE    hEvent;
        DWORD     dwPrepared;
        void  __fastcall SetEvent(void){::SetEvent(hEvent);}
        void  __fastcall ResetEvent(void){::ResetEvent(hEvent);}
        DWORD __fastcall WaitFor(DWORD dwTime);
        WaveOutEvent(void);
        ~WaveOutEvent(void);
    };

    CRITICAL_SECTION m_cs;
    BYTE     *m_pOutput;
    HWAVEOUT  m_hwo;
    WAVEHDR   m_WavHdrs[cNumBuffers];
    WaveOutEvent m_woEvents[cNumBuffers];
    int       m_nStopCnt;
    int       m_nCurBufIndex;
    UINT64    m_qwWriteSamples;
    BOOL      m_bError;
    static HWAVEOUT __fastcall OpenWaveDevice(int nDeviceID,
                                              WAVEFORMATEX *wfx,
                                              DWORD_PTR dwProc,
                                              DWORD_PTR dwParam,
                                              MMRESULT *mmr);
    static void __fastcall CloseWaveDevice(HWAVEOUT hWaveOut);

    static void CALLBACK waveOutProc(HWAVEOUT hwo,UINT uMsg, DWORD_PTR dwInstance,
                                        DWORD_PTR dwParam1, DWORD_PTR dwParam2);
    void __fastcall waveOutProcInternal(UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
    HWAVEOUT __fastcall OpenWaveDevice(int nDeviceID,
                                       int nSampleRate,
                                       int nChannels,
                                       int nBps,
                                       bool bMme);

    void __fastcall CreateWaveHdr(void);
    void __fastcall WaveOutWrite(int nIndex, BYTE *pOutput, int nSizeSample);
    void __fastcall WaveOutUnprepareHeader(int nIndex);
public:
    virtual BOOL   WINAPI Output(BYTE *pOutput, int nSizeSample);//デバイスにデータを出力
    virtual void   WINAPI Wait(void);           //次の書き込みが可能になるまで待つ
    virtual UINT64 WINAPI GetPosition(void);    //デバイスの再生位置をサンプル単位で返す
    virtual void   WINAPI Stop(void);            //再生を停止
    virtual void   WINAPI Pause(void);           //一時停止
    virtual void   WINAPI Restart(void);         //再開
    virtual BOOL   WINAPI SetVolume(int nVolume);//音量制御
    virtual BOOL   WINAPI SetRate(int nRate);    //サンプリングレートを変換
    virtual DWORD  WINAPI UpdateConfig(void *pvReserved){return 0;}

    BOOL __fastcall Open(const wchar_t *wcszDeviceName,
                         const wchar_t *wcszId, 
                         const KPI_DEVICE_FORMAT *cpFormat, //再生周波数等
                         int nBufferSizeSample);
	KbWaveOutDevice(void);
	virtual ~KbWaveOutDevice(void);
};
