Initialize DirectSound in c++ (with Dev-C++)

Eef2c9c98663e5231e2596790e82149a
0
outrider 101 Oct 31, 2007 at 07:33

Hi,

I found no thread that is actually dealing with my topic, so I decided to start a new one:

I use Dev-C++, downloaded the DirectX-DevPack and installed it. But I cannot find any tutorials on how to write a small program (can be command line only) that initializes the directsound routines and uses them for a small buffer playback of (let’s say) a sine wave or a triangle wave.

Note:
I don’t want to play a .wav file or something like that - there are plenty of tutorials for that out there.
I want to write a program allows me to fill the playback buffer with my own data and then play that buffer.

Is there anyone here who has done this before?

Regards,
outrider

8 Replies

Please log in or register to post a reply.

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Oct 31, 2007 at 08:19

There are plenty of tutorials in the DirectX SDK and on msdn.

Eef2c9c98663e5231e2596790e82149a
0
outrider 101 Oct 31, 2007 at 08:57

Yes, thank you. I am aware of that and have a copy of it on my hard drive.
But (it just so happens) I am a total c++ newbie and would appreciate some basic help. I know this forum is not for helping lazy newbies and I do not consider myself lazy at all. I just do not understand the documentation as much as I need to get a simple sine wave played.
A simple main.cpp where the sound system is initialized would help a lot!

That’s how far I got (and I do know that it’s not very far indeed):
I linked the dsound.lib as described in the documentation and included the Dsound.h file.

#include <cstdlib>
#include <iostream>
#include <Dsound.h>

using namespace std;

int main(int argc, char *argv[])
{
    system("PAUSE");
    return 0;
}

Thank you all very much for your help!

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Oct 31, 2007 at 10:30

Any reason why it has to be DirectSound? DevMaster some real nice tutorials on OpenAL, and they walk you through it all: Lesson 1

Eef2c9c98663e5231e2596790e82149a
0
outrider 101 Oct 31, 2007 at 11:24

Ok, I got some place now…
Program compiles and runs and no error is thrown, but I cannot hear a damn thing.
An idea anyone?

Kenneth:
It’s a task I have to complete for school, so I can choose pnly between ALSA, Mac Core Audio and DirectSound, but my group mate is doing the ALSA interface already and I don’t have access to a macintosh. So.. :-(

#include <cstdlib>
#include <string>
#include <iostream>
#include <Dsound.h>

using namespace std;


int main(int argc, char *argv[]){
    HRESULT hr;
    LPDIRECTSOUND8 lpds; 
    LPDIRECTSOUNDBUFFER lpdsbuffer;
    LPDIRECTSOUNDBUFFER lpdsbuffer2;
    LPVOID lpvWrite;
    DWORD  dwLength;

    // Buffer Setup
    DSBUFFERDESC dsbdesc; 
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
    dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
    dsbdesc.dwFlags = DSBCAPS_STATIC;

    // Sound Object Creation
    DirectSoundCreate8(NULL, &lpds, NULL);
    lpds->SetCooperativeLevel(NULL, DSSCL_PRIORITY);
    lpds->Initialize(NULL);
    lpds->CreateSoundBuffer(&dsbdesc, &lpdsbuffer, NULL);
    
    // Filling my own buffer
    int rate = 11025;
    int chan = 2;
    float *buffer = new float [rate*chan];
    float *ptr = buffer;
  
    for (int i=0;i<rate;i++){
          ptr[0]=1;
        ptr[1]=1;
        ptr+=chan;
    }
    
    // Locking the soundbuffer and copying my buffer's data into the soundbuffer
    if (DS_OK == lpdsbuffer->Lock(
      0,          // Offset at which to start lock.
      0,          // Size of lock; ignored because of flag.
      &lpvWrite,  // Gets address of first part of lock.
      &dwLength,  // Gets size of first part of lock.
      NULL,       // Address of wraparound not needed. 
      NULL,       // Size of wraparound not needed.
      DSBLOCK_ENTIREBUFFER))  // Flag.
    {
      memcpy(lpvWrite, buffer, dwLength);
      lpdsbuffer->Unlock(lpvWrite, dwLength, NULL, 0);
    }
    
    // Playback
    lpdsbuffer->SetCurrentPosition(0);
    lpdsbuffer->Play(0,0,0);
       
    
    return 0;
}
46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Oct 31, 2007 at 11:31

Ah ok, I thought it might be a personal project. :)

The reason you are not hearing anything, is because your program exits immedialtely after the sound is started. Audio always runs in the background, in a seperate thread, so execution returns to your main() function right after the call to Play(). This is also what keeps audio from stuttering in games, even though they run with a low framerate. Putting the ‘system(“PAUSE”)’ you had earlier after the Play() call should do the trick.

Fd6da5364c8127802249d098df401207
0
valderama 101 Nov 07, 2007 at 21:32

Hi,

think you have do to the same homework as I :)

I still have some problems, which you have already solved:

so, i try to use a streaming buffer, instead of a static buffer. so i want to use a buffer with the size of 64 samples and refill it as often as needed.

so if you have tried to use a streaming buffer as well, please post your code here or help my finding the error in mine…

greetz, walter

#include <dsound.h>
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "windows.h"

static const bool WRITE_TO_FILE = true;

static const float M_PI = 3.14159265;

static const int SAMPLE_RATE = 44100;
static const int BUFFER_SIZE = 64;
static const int CHANNELS = 2;
static const int AMPLITUDE = 1;
static const float TIME_PER_SAMPLE = 1.0f / (float) SAMPLE_RATE;

enum 
{
    TRIANGLE, 
    SQUARE, 
    SAWTOOTH, 
    SINE 
};

//GLOBALS

//direct sound object
static LPDIRECTSOUND8 lpDS;

//parameter defaults
int waveform = SINE;
int frequency = 440;
float duration = 1;

float* buffer;

using namespace std;

bool InitAudio(){
    if (DS_OK==DirectSoundCreate8(NULL,&lpDS,NULL)) //create direct sound object
    {
        HRESULT hr = lpDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_NORMAL);
        if (FAILED(hr))
        {
            printf("SetCooperativeLevel failed!");
            return (FALSE);
            /*
            switch(hr){
                case DSERR_ALLOCATED:
                    printf("DSERR_ALLOCATED\n");
                    break;
                case DSERR_INVALIDPARAM:
                    printf("DSERR_INVALIDPARAM\n");
                    break;
                case DSERR_UNINITIALIZED:
                    printf("DSERR_UNINITIALIZED\n");
                    break;
                case DSERR_UNSUPPORTED:
                    printf("DSERR_UNSUPPORTED\n");
                    break;
            };
            */
        }
        return TRUE;
    }
    else  //DSObj creation was unsuccessful
    {
        printf("Could Not Create Direct Sound Object");
        return (FALSE);
    }
}

void ShutdownAudio(){
    if (lpDS)
    {
        IDirectSound_Release(lpDS);
    }
    else
    {
       printf("Sound was not initialized and cannot Exit!");
    }
}

HRESULT CreateBasicBuffer(LPDIRECTSOUND8 lpDirectSound, LPDIRECTSOUNDBUFFER8* ppDsb8) 
{ 
    WAVEFORMATEX wfx; 
    DSBUFFERDESC dsbdesc; 
    LPDIRECTSOUNDBUFFER pDsb = NULL;
    HRESULT hr;
 
    // Set up WAV format structure. 
    memset(&wfx, 0, sizeof(WAVEFORMATEX)); 
    wfx.wFormatTag = WAVE_FORMAT_PCM; 
    wfx.nChannels = 2; 
    wfx.nSamplesPerSec = 44100; 
    wfx.nBlockAlign = 8; 
    wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; 
    wfx.wBitsPerSample = 32; 
 
    // Set up DSBUFFERDESC structure. // DSBCAPS_GETCURRENTPOSITION2 
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
    dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
    dsbdesc.dwFlags = 
    DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY
    | DSBCAPS_GLOBALFOCUS; 
    dsbdesc.dwBufferBytes = BUFFER_SIZE*CHANNELS*sizeof(float);//duration * wfx.nAvgBytesPerSec;  //fix the buffer bytes! might be 64 BUFFER_SIZE//
    dsbdesc.lpwfxFormat = &wfx; 
 
    // Create buffer. 
    hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, &pDsb, NULL); 
    if (SUCCEEDED(hr)) 
    { 
        hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)ppDsb8);
        pDsb->Release();
    } 
    return hr;
}

BOOL AppWriteDataToBuffer( 
        LPDIRECTSOUNDBUFFER8 lpDsb,  // The buffer.
        DWORD dwOffset,              // Our own write cursor.
        float* lpbSoundData,         // Start of our data.
        DWORD dwSoundBytes)          // Size of block to copy.
{ 
    LPVOID  lpvPtr1; 
    DWORD dwBytes1; 
    LPVOID  lpvPtr2; 
    DWORD dwBytes2; 
    HRESULT hr; 

    // Obtain memory address of write block. This will be in two parts
    // if the block wraps around.

    hr = lpDsb->Lock(dwOffset, dwSoundBytes, &lpvPtr1, 
        &dwBytes1, &lpvPtr2, &dwBytes2, 0); 

    // If the buffer was lost, restore and retry lock. 

    if (DSERR_BUFFERLOST == hr) 
    { 
        lpDsb->Restore(); 
        hr = lpDsb->Lock(dwOffset, dwSoundBytes, 
            &lpvPtr1, &dwBytes1,
            &lpvPtr2, &dwBytes2, 0); 
    } 
    if (SUCCEEDED(hr)) 
    { 
        // Write to pointers. 

        //memcpy(lpvWrite, &buffer[0], dwLength); 
        CopyMemory(lpvPtr1, lpbSoundData, dwBytes1); //CopyMemory(void* dest, void * source, size_t length);
        if (NULL != lpvPtr2) 
        { 
            CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2); 
        } 

        // Release the data back to DirectSound. 

        hr = lpDsb->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); 
        if (SUCCEEDED(hr)) 
        { 
            // Success. 
            return TRUE; 
        } 
    } 

    // Lock, Unlock, or Restore failed. 

    return FALSE; 
    } 


int main(int argc, char** argv) // waveform(int[0,3]) f(hz) duration(s)
{
    //input params
    if( 0 != argv[1] ){
        waveform = atoi(argv[1]);
    }
    if( 0 != argv[2] ){
        frequency = atoi(argv[2]);
    }
    if( 0 != argv[3] ){
        duration = atof(argv[3]);
    } 

    if ( !InitAudio() ) {
        printf("InitAudio failed!\n");
        return 1;
    }
    
    FILE *fout;
    if (WRITE_TO_FILE){
        fout = fopen("data.dat", "wt");
    }
    
    //create secondary buffer
    LPDIRECTSOUNDBUFFER8 secondaryBuffer = 0;   
    CreateBasicBuffer(lpDS, &secondaryBuffer);

    float time = 0;
    
    int buffer_amount = int(ceil( (float)duration * ((float)SAMPLE_RATE / (float)BUFFER_SIZE)));
    //cout << buffer_amount << "\n";
    for(int i=0; i<buffer_amount;i++){
    
        //int bufSiz = CHANNELS*duration*SAMPLE_RATE;
        buffer = new float[BUFFER_SIZE*CHANNELS];

        float *ptr = buffer;
        
        for(int j=0; j<BUFFER_SIZE; j++) {       
        
            switch(waveform){
                case SINE:
                    ptr[1] = AMPLITUDE * sinf(time * frequency * M_PI * 2.0f);
                    break;
                case TRIANGLE:
                    ptr[1] = AMPLITUDE * sinf(time * frequency * M_PI * 2.0f);
                    break;
                case SQUARE:
                    ptr[1] = AMPLITUDE * sinf(time * frequency * M_PI * 2.0f);
                    break;
                case SAWTOOTH:
                    ptr[1] = AMPLITUDE * sinf(time * frequency * M_PI * 2.0f);
                    break;
            };      
            
            ptr[2] = ptr[1];

            if (WRITE_TO_FILE){
                fprintf(fout, "%f %f\n", time, ptr[1]);
            }

            time += TIME_PER_SAMPLE;
            ptr += CHANNELS;
        }

        LPDWORD playCursor = 0;
        LPDWORD writeCursor = 0;

        secondaryBuffer->GetCurrentPosition(playCursor, writeCursor);
        //cout << playCursor << "\n";
        //cout << writeCursor << "\n";

        AppWriteDataToBuffer(secondaryBuffer, 0, &buffer[0], BUFFER_SIZE * CHANNELS * sizeof(float));
        //buffer voll, kopiers in secondary buffer und ab die post
        
        
        //while(
        secondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);

    }   
    
    if (WRITE_TO_FILE){
        fclose(fout);
    }

    cout << "waveform: " << waveform << " - frequency: " << frequency << " - duration: " << duration << "\n";
    
    /*LPVOID lpvWrite;
    DWORD  dwLength;
    //when you lock the buffer you can write into it.
    secondaryBuffer->Lock(0, 0, &lpvWrite, &dwLength, 0, 0, DSBLOCK_ENTIREBUFFER);
    memcpy(lpvWrite, &buffer[0], dwLength);
    secondaryBuffer->Unlock(lpvWrite, dwLength, 0, 0);
    secondaryBuffer->Play(0, 0, 0);  //for streaming with this flag: DSBPLAY_LOOPING */
    
    //wait for keypress to exit
    char c;
    cout << "press enter ...";
    cin.get(c);

    //close direct sound device
    ShutdownAudio();
    
    //delete the buffer
    //delete buffer;

    return 0;
}
Eef2c9c98663e5231e2596790e82149a
0
outrider 101 Nov 08, 2007 at 08:23

So, I switched to streaming buffers, too. And my program is nearly complete, BUT:
if I use streaming buffers, I need position notifications, so I can fill the first half of my 1024byte buffer when the second half is played and fill the second half of the buffer when the first half is played.
I managed to get it working for ONE position notification. however, when I set up more than one notification, the initialisation ->SetNotificationPosition fails.

Why doesn’t it work with 2 NotificationPositions?! Do I need an extra SoundBuffer?
In the DX SDK there’s an example solution using more than one position notification, too. It works for them, I did the same, but Initialisation fails every time I try…

#include "stdafx.h"
#include <dsound.h>
#define _USE_MATH_DEFINES
#include <cmath>
#include <windows.h>
#include <string>
#include <iostream>


using namespace std;

LPDIRECTSOUND8 lpds;
LPDIRECTSOUNDBUFFER lpdsbuffer;
WAVEFORMATEX wfx;
DSBUFFERDESC dsbdesc;
HWND hwnd;
short* buffer = new short[44100 * 2];
bool playing = false;

HWND GetConsoleHwnd(void){
       HWND cWindowHandle;         
       LPCWSTR newtitle = (LPCWSTR) L"Audio Programming - Lesson 1";
       SetConsoleTitle(newtitle);
       Sleep(40);
       cWindowHandle=FindWindow(NULL, newtitle);
       return(cWindowHandle);
}

HRESULT createSoundObject(void){
    HRESULT hr;
    hr = DirectSoundCreate8(NULL,&lpds,NULL);
    hr = CoInitializeEx(NULL, 0);
    hr = lpds->SetCooperativeLevel(hwnd,DSSCL_NORMAL);
    return hr;
}

WAVEFORMATEX setWaveFormat(void){
    WAVEFORMATEX wfx; 
    memset(&wfx, 0, sizeof(WAVEFORMATEX)); 
    wfx.wFormatTag = WAVE_FORMAT_PCM; 
    wfx.nChannels = 2; 
    wfx.wBitsPerSample = 16;
    wfx.nSamplesPerSec = 44100; 
    wfx.nBlockAlign = 4;
    wfx.nAvgBytesPerSec = 44100 * 4; 
    return wfx;
}

DSBUFFERDESC setBufferDescription(){
    DSBUFFERDESC dsbdesc;
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
    dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
    dsbdesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2;
    dsbdesc.dwBufferBytes = 1024;
    dsbdesc.lpwfxFormat = &wfx;
    return dsbdesc;
}

HRESULT createSecondarySoundBuffer(void){
    HRESULT hr = lpds->CreateSoundBuffer(&dsbdesc,&lpdsbuffer,NULL);
    return hr;
}

HRESULT fillSoundBuffer(bool first){
    bool firsthalf = first;
    LPVOID lpvWrite;
    DWORD  dwLength;
    HRESULT hr = lpdsbuffer->Lock(0,dsbdesc.dwBufferBytes,&lpvWrite,&dwLength,NULL,NULL,DSBLOCK_ENTIREBUFFER);
    if(SUCCEEDED(hr)){
        memcpy(lpvWrite, buffer, dwLength);
        hr = lpdsbuffer->Unlock(lpvWrite,dwLength,NULL,NULL);
    }
    return hr;
}

BOOL fillBufferWithWaveForm(string form){
    
    if(form == "sine"){
            short* ptr = buffer;
            unsigned short i;
            float amp = 1.0f;
            float tmp;
            short output;
            for(i=0;i<44100;i++){
                tmp = amp * sinf(1000.0f * 2.0f * M_PI * ((float)i/44100.0f));
                output = (short)(tmp*32767);
                ptr[0] = output;
                ptr[1] = output;
                ptr += 2;
            }
            return true;
    }
}


int _tmain(int argc, char* argv[])
{
    HRESULT hr;
    BOOL formFound = false;
    string waveform = "sine";

      
    // Rename the ConsoleWindow and retrieve its 
    // handle by looking up its new name in Windows
    hwnd = GetConsoleHwnd();

    // Create the Basic LPDIRECTSOUND8 object
    hr = createSoundObject();

    // Set the default wave format (hard coded)
    wfx = setWaveFormat();

    // Set the default buffer description (hard coded)
    dsbdesc = setBufferDescription();

    // Create the secondary sound buffer
    hr = createSecondarySoundBuffer();

    // Generate a waveform and put it into the playback array
    formFound = fillBufferWithWaveForm(waveform);

    // fill the secondary sound buffer entirely with the playback array data
    hr = fillSoundBuffer(true);

    // Start playback at position 0 and loop this sound until explicitely stopped
    lpdsbuffer->SetCurrentPosition(0);
    
    // Create Notification Handles
    HANDLE NotifyEvent[2];
    NotifyEvent[0] = CreateEvent(NULL,TRUE,FALSE,NULL);
    NotifyEvent[1] = CreateEvent(NULL,TRUE,FALSE,NULL);
    
    LPDIRECTSOUNDNOTIFY8 lpDsNotify;
    DSBPOSITIONNOTIFY PositionNotify[2];
    
    if (hr = lpdsbuffer->QueryInterface(IID_IDirectSoundNotify8,(LPVOID*)&lpDsNotify) == DS_OK){ 
        PositionNotify[0].dwOffset = 512;
        PositionNotify[0].hEventNotify = NotifyEvent[0];
        PositionNotify[1].dwOffset = 1024;
        PositionNotify[1].hEventNotify = NotifyEvent[1];
        if(hr = lpDsNotify->SetNotificationPositions(2, PositionNotify) != DS_OK){
            cout << hr << endl;
            cout << "Error while setting up Notification Positions!" << endl;
        }
        else{
            lpDsNotify->Release();
        }
    }
    else{
        cout << "Notification settings failed!" << endl;
    }
    
    
    hr = lpdsbuffer->Play(0,0,DSBPLAY_LOOPING);
    printf("Buffer set up successfully! Playing back sound... \n \n");
    
    playing = true;
    while(playing){
        hr = WaitForMultipleObjects(2,NotifyEvent,FALSE,INFINITE);
        //cout << hr << endl;
        if(hr-WAIT_OBJECT_0 == 0){
            cout << "Notify bei 512!" << endl;
            ResetEvent(NotifyEvent[0]);
        }
        else if(hr-WAIT_OBJECT_0 == 1){
            cout << "Notify bei 1024!" << endl;
            ResetEvent(NotifyEvent[1]);
        }
    }
    
    system("PAUSE");
    lpdsbuffer->Stop();
    lpdsbuffer->Release();
    lpds->Release();
    return 0;
}
Eef2c9c98663e5231e2596790e82149a
0
outrider 101 Nov 08, 2007 at 08:27

oh my god, I am so stupid!!!

setting a notification at position 1024, when the buffer only holds 0-1023 MIGHT be the problem :-)

Now it works, I just need to keep an eye on the data from my data-array, so that the right position and right amount of data gets streamed to the soundbuffer.

good luck, walter!