Jump to content


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


8 replies to this topic

#1 outrider

    New Member

  • Members
  • Pip
  • 5 posts

Posted 31 October 2007 - 07:33 AM

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

#2 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 31 October 2007 - 08:19 AM

There are plenty of tutorials in the DirectX SDK and on msdn.
"Stupid bug! You go squish now!!" - Homer Simpson

#3 outrider

    New Member

  • Members
  • Pip
  • 5 posts

Posted 31 October 2007 - 08:57 AM

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!

#4 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 31 October 2007 - 10:30 AM

Any reason why it has to be DirectSound? DevMaster some real nice tutorials on OpenAL, and they walk you through it all: Lesson 1
"Stupid bug! You go squish now!!" - Homer Simpson

#5 outrider

    New Member

  • Members
  • Pip
  • 5 posts

Posted 31 October 2007 - 11:24 AM

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;

}



#6 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 31 October 2007 - 11:31 AM

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.
"Stupid bug! You go squish now!!" - Homer Simpson

#7 valderama

    New Member

  • Members
  • Pip
  • 1 posts

Posted 07 November 2007 - 09:32 PM

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;
}


#8 outrider

    New Member

  • Members
  • Pip
  • 5 posts

Posted 08 November 2007 - 08:23 AM

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;

}



#9 outrider

    New Member

  • Members
  • Pip
  • 5 posts

Posted 08 November 2007 - 08:27 AM

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!





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users