//---------------------------------------------------------------------------------------
//
// Simple Windows DirectSound Example Code
//
// Jim Shaw 12/3/2004
//
//---------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------
// Always include windows.h for windows programs
//---------------------------------------------------------------------------------------
#include <windows.h>

#include <dsound.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

//---------------------------------------------------------------------------------------
// Windows variables
//---------------------------------------------------------------------------------------
HWND game_window;
char game_class[]="DSNDCWindow";

//---------------------------------------------------------------------------------------
// DirectSound variables
//---------------------------------------------------------------------------------------
IDirectSound8 *dsound=NULL;
IDirectSoundBuffer *dsprimarybuffer=NULL;
IDirectSoundBuffer8 *sfxbuffer8=NULL;

//---------------------------------------------------------------------------------------
// Prototypes
//---------------------------------------------------------------------------------------
int init_dsound(void);
int load_wav(char *);
void shutdown_dsound(void);

//---------------------------------------------------------------------------------------
// Main Windows function
//---------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    char filename[_MAX_PATH];
    OPENFILENAME ofn;
    BOOL res;
    WNDCLASSEX clas;
    int style;
    RECT rect;
    MSG msg;
    HDC hdc;

    //Here we create the Class we named above
    clas.cbSize = sizeof(WNDCLASSEX);
    clas.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    clas.lpfnWndProc = DefWindowProc;
    clas.cbClsExtra = 0;
    clas.cbWndExtra = 0;
    clas.hInstance = hInstance;
    clas.hIcon = NULL;
    clas.hCursor = NULL;
    clas.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);//<- background colour of window
    clas.lpszMenuName = NULL;
    clas.lpszClassName = game_class;//<- the class name
    clas.hIconSm = 0;
    //do it!
    RegisterClassEx(&clas);

    //style of the window - what boxes do we need (close minimised etc)
    style = WS_CAPTION|WS_SYSMENU|WS_MAXIMIZEBOX|WS_MINIMIZEBOX;
    //create the window
    game_window = CreateWindowEx(0, game_class, "DirectSound", style, CW_USEDEFAULT, CW_USEDEFAULT, 1,1, NULL, NULL, hInstance, 0);

    //adjust the window size so that a 200 x 40 window will fit inside its frame
    rect.left = rect.top = 0;
    rect.right = 200;
    rect.bottom = 40;
    AdjustWindowRectEx(&rect, style , FALSE, 0);
    SetWindowPos(game_window, NULL, 0,0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE|SWP_NOZORDER);

    //show the window on the desktop
    ShowWindow(game_window, nCmdShow);

    filename[0] = '\0';
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = NULL;
    ofn.hInstance = NULL;
    ofn.lpstrFilter = "WAV Files\0*.wav\0";
    ofn.lpstrCustomFilter = NULL;
    ofn.nMaxCustFilter = 0;
    ofn.nFilterIndex = 0;
    ofn.lpstrFile = filename;
    ofn.nMaxFile = _MAX_PATH;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.lpstrTitle = "Select Audio File";
    ofn.Flags = OFN_FILEMUSTEXIST|OFN_EXPLORER;
    ofn.nFileOffset=0;
    ofn.nFileExtension=0;
    ofn.lpstrDefExt="bin";
    ofn.lCustData=0;
    ofn.lpfnHook=NULL;
    ofn.lpTemplateName="";

    res = GetOpenFileName(&ofn);
    if (res == 0)
        return 0;

    if (init_dsound() != 0)
    {
        MessageBox(NULL, "A problem occurred initialising DirectSound.\nCheck your sound drivers and "
                            "DirectX version.\nThis demo requires DirectX9.0 or better.", "DirectX Initialisation Error", MB_ICONWARNING|MB_OK);
        shutdown_dsound();
        return 0;
    }

    if (load_wav(filename) != 0)
    {
        MessageBox(NULL, "A problem occurred loading the WAV file\n",
                            "File Loading Error", MB_ICONWARNING|MB_OK);
        shutdown_dsound();
        return 0;
    }

    hdc = GetDC(game_window);
    TextOut(hdc, 10,10, "Close me to stop sound!", 23);
    ReleaseDC(game_window, hdc);

    while (GetMessage(&msg, game_window, 0,0) >= 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    shutdown_dsound();

    return 0;
}

//---------------------------------------------------------------------------------------
// Initialise DirectSound
//---------------------------------------------------------------------------------------
int init_dsound(void)
{
    HRESULT err;
    DSCAPS dsoundcaps;
    DSBUFFERDESC dsbdesc;
    WAVEFORMATEX wfx;

    err = DirectSoundCreate8(NULL, &dsound, NULL);
    if (err != DS_OK)
        return 1;

    err = IDirectSound8_SetCooperativeLevel(dsound, game_window, DSSCL_EXCLUSIVE);
    if (err != DS_OK)
        return 1;

    memset(&dsoundcaps, 0, sizeof dsoundcaps);
    dsoundcaps.dwSize = sizeof dsoundcaps;

    err = IDirectSound8_GetCaps(dsound, &dsoundcaps);
    if (err != DS_OK)
        return 1;

    // Create and set primary buffer format
    memset(&dsbdesc, 0, sizeof dsbdesc);

    dsbdesc.dwSize = sizeof dsbdesc;
    dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;

    err = IDirectSound8_CreateSoundBuffer(dsound, &dsbdesc, &dsprimarybuffer, NULL);
    if (err != DS_OK)
        return 1;

    memset(&wfx, 0, sizeof wfx);
    wfx.cbSize = sizeof wfx;
    wfx.wFormatTag = WAVE_FORMAT_PCM;
    wfx.nChannels = 2;
    wfx.nSamplesPerSec = 44100;
    wfx.wBitsPerSample = 16;
    wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
    wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

    err = IDirectSoundBuffer_SetFormat(dsprimarybuffer, &wfx);
    if (err != DS_OK)
        return 1;

    //force dsound to ALWAYS keep DMA active
    err = IDirectSoundBuffer_Play(dsprimarybuffer, 0, 0, DSBPLAY_LOOPING);
    if (err != DS_OK)
        return 1;

    return 0;
}

//---------------------------------------------------------------------------------------
// Shutdown DirectSound
//---------------------------------------------------------------------------------------
void shutdown_dsound()
{
    if (sfxbuffer8)
    {
        IDirectSoundBuffer8_Stop(sfxbuffer8);
        IDirectSoundBuffer8_Release(sfxbuffer8);
    }
    if (dsprimarybuffer)
    {
        IDirectSoundBuffer_Stop(dsprimarybuffer);
        IDirectSoundBuffer_Release(dsprimarybuffer);
    }
    if (dsound) IDirectSound8_Release(dsound);

}

//---------------------------------------------------------------------------------------
// Convert linear volume to DirectSound db scale
//---------------------------------------------------------------------------------------
int vol_to_db(int vol)
{
    if (vol)
        return (int)(20.0 * 100.0 * log10((double)vol/127.0));
    return -10000;
}


//---------------------------------------------------------------------------------------
// Make a WAV FOURCC
//---------------------------------------------------------------------------------------
#define MRIFFID(a,b,c,d) (((d)<<24)|((c)<<16)|((b)<<8)|(a))

//---------------------------------------------------------------------------------------
// Load and play the given wav file
//---------------------------------------------------------------------------------------
int load_wav(char *filename)
{
    unsigned int *riff;
    FILE *f;
    long len;
    void *sfx;

    // Read in the supplied file
    f = fopen(filename, "rb");
    if (!f)
        return 1;
    fseek(f, 0, SEEK_END);
    len = ftell(f);
    fseek(f, 0, SEEK_SET);

    sfx = malloc(len);
    if (!sfx)
    {
        fclose(f);
        return 1;
    }

    fread(sfx, 1, len, f);
    fclose(f);

    riff = sfx;

    // Check it's a RIFF WAV file
    if (*riff == MRIFFID('R','I','F','F'))
    {
        IDirectSoundBuffer *sfxbuffer;
        DSBUFFERDESC dsbufferdesc;
        void *data1, *data2;
        DWORD size1, size2;
        unsigned char *wav_data;
        unsigned int wav_size;
        WAVEFORMATEX *wav_hdr;
        HRESULT err;

        // Strip through the WAV header looking for the WAVEFORMAT data

        riff += 3;//skip length and id
        while (*riff != MRIFFID('f','m','t',' '))
            riff = (unsigned int *)((unsigned char *)riff + 8 + *(riff+1));

        wav_hdr = (WAVEFORMATEX *)(riff+2);

        // Strip through the WAV header looking for the sample data

        riff = (unsigned int *)((unsigned char *)riff + 8 + *(riff+1));
        while (*riff != MRIFFID('d','a','t','a'))
            riff = (unsigned int *)((unsigned char *)riff + 8 + *(riff+1));

        wav_data = (unsigned char *)(riff+2);
        wav_size = *(riff+1);

        // Create a suitable buffer
        memset(&dsbufferdesc, 0, sizeof dsbufferdesc);
        dsbufferdesc.dwSize = sizeof dsbufferdesc;
        dsbufferdesc.dwFlags = DSBCAPS_STATIC|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLFREQUENCY|DSBCAPS_CTRLPAN;
        dsbufferdesc.dwBufferBytes = wav_size;
        dsbufferdesc.lpwfxFormat = wav_hdr;

        err = IDirectSound8_CreateSoundBuffer(dsound, &dsbufferdesc, &sfxbuffer, NULL);
        if (err != DS_OK)
        {
            free(sfx);
            return 1;
        }
        // Get the DirectSound8 interface to the buffer
        err = IDirectSoundBuffer_QueryInterface(sfxbuffer, &IID_IDirectSoundBuffer8, (void **)&sfxbuffer8);
        if (sfxbuffer8 == NULL)
        {
            free(sfx);
            return 1;
        }
        // Release the base object
        err = IDirectSoundBuffer_Release(sfxbuffer);

        // Lock the buffer
        err = IDirectSoundBuffer8_Lock(sfxbuffer8, 0, 0, (void **)&data1, &size1, (void **)&data2, &size2, DSBLOCK_ENTIREBUFFER);
        if (err != DS_OK)
        {
            free(sfx);
            return 1;
        }

        // Copy wav data from file into DirectSound buffer
        memcpy(data1, wav_data, wav_size);

        err = IDirectSoundBuffer8_Unlock(sfxbuffer8, data1, size1, data2, size2);

        // Set the sfx volume
        err = IDirectSoundBuffer8_SetVolume(sfxbuffer8, vol_to_db(127));

        // Play the sound
        err = IDirectSoundBuffer8_Play(sfxbuffer8,0,0,0);
        if (err != DS_OK)
        {
            free(sfx);
            return 1;
        }
    }

    // Tidy up
    free(sfx);

return 0;
}

