• Advertisement
Sign in to follow this  

XAudio2 - How come I am only able to play sound once with out reloading audio data?

This topic is 2772 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

For those familiar with XAudio2 you should know that in order to play a sound you have to load the audio data into a IXAudio2SourceVoice. Currently I am able to load the proper data into a IXAudio2SourceVoice and play it back. My problem is once I start a IXAudio2SourceVoice playing the sound, I can't then restart the same IXAudio2SourceVoice.

// Populate and load IXAudio2SourceVoice
// Play voice the 1st time
voice->Start( 0 );
// Voice is done playing
// Play voice the 2nd time
voice->Start( 0 ); // Nothing happens

Hear is how I am setting up XAudio2
#include "AudioPrecompile.h"
#include "Audio.h"
#include "Wave.h"

//-------------------------------------------------------------------------------
// Constructor
//-------------------------------------------------------------------------------
Audio::Audio()
{
m_xAudio2 = NULL;
m_masteringVoice = NULL;
}

//-------------------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------------------
Audio::~Audio()
{
DeleteAllTracks();
SAFE_RELEASE(m_xAudio2);
SAFE_NULL( m_masteringVoice );
CoUninitialize();
}

//-------------------------------------------------------------------------------
// Iterate through all tracks and free resources
//-------------------------------------------------------------------------------
void Audio::DeleteAllTracks()
{
for( std::map< std::wstring, Track >::iterator i = m_tracks.begin(); i != m_tracks.end(); i++)
{
i->second.GetSourceVoice()->DestroyVoice();
//SAFE_DELETE_ARRAY( i->second.GetData() );
}
}

//-------------------------------------------------------------------------------
// Set up XAudio2 Engine and parse audio card for Master Voice
//-------------------------------------------------------------------------------
HRESULT Audio::Initialize()
{
HRESULT hr = S_OK;

// If we are using windows Initialize COM
#ifndef _XBOX
CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif

// Set up engine based of debug or release
#ifdef NO_DEBUG // Release
// Create standard instance of the XAudio2
if( FAILED ( hr = XAudio2Create(&m_xAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR) ) )
return hr;
#else // Debug
// Create debug instance of XAudio2
if( FAILED ( hr = XAudio2Create(&m_xAudio2, XAUDIO2_DEBUG_ENGINE, XAUDIO2_DEFAULT_PROCESSOR) ) )
return hr;
#endif

// Determine the number of audio devices
UINT32 deviceCount; // Number of devices
m_xAudio2->GetDeviceCount(&deviceCount);

XAUDIO2_DEVICE_DETAILS deviceDetails; // Structure to hold details about audio device
int preferredDevice = 0; // Device to be used

// Loop through devices
for (unsigned int i = 0; i < deviceCount; i++)
{
// Get device details
m_xAudio2->GetDeviceDetails(i,&deviceDetails);
// Check conditions ( default device )
if (deviceDetails.Role == DefaultCommunicationsDevice)
{
preferredDevice = i;
break;
}

}

// Create and configure a mastering voice using preferred device
if( FAILED ( hr = m_xAudio2->CreateMasteringVoice( &m_masteringVoice,
XAUDIO2_DEFAULT_CHANNELS,
XAUDIO2_DEFAULT_SAMPLERATE,
0,
preferredDevice,
NULL ) ) )
return hr;

return hr;
}

//-------------------------------------------------------------------------------
// Load a RIFF wave file
//-------------------------------------------------------------------------------
HRESULT Audio::AddTrack( std::wstring path, std::wstring ID, bool loop )
{
HRESULT hr = S_OK;
Wave wav;

// Create and clear a track
Track track;
ZeroMemory( &track, sizeof( Track ) );

// Use Wave object to open a .wav file
// This will fill out a WAVEFORMATEXTENSIBLE and XAUDIO2_BUFFER structure
if( FAILED( hr = wav.Open( path, track ) ) )
return hr;

// Create a voice using the WAVEFORMATEXTENSIBLE structure
IXAudio2SourceVoice * voice;
if( FAILED( hr = m_xAudio2->CreateSourceVoice( &voice, // Pointer to source voice
(WAVEFORMATEX*)&track.m_waveformat, // Pointer to wave data
0, // Flags
XAUDIO2_DEFAULT_FREQ_RATIO // Highest allowable frequency ratio
) ) )
return hr;

// Store the new voice in the track
track.SetSourceVoice( voice );

// Buffer to submit
XAUDIO2_BUFFER buffer = {0};

// Determine if we are to loop this file
buffer.LoopCount = loop ? XAUDIO2_LOOP_INFINITE : 0;
//get a pointer to the SoundData's internal buffer
buffer.pAudioData = track.m_sampleData;
buffer.AudioBytes = track.m_size;
//no more audio data follows this
buffer.Flags = XAUDIO2_END_OF_STREAM;

// Submit data to voice
if( FAILED( hr = track.GetSourceVoice()->SubmitSourceBuffer( &buffer ) ) )
return hr;

// Store are track
m_tracks[ID] = track;

return S_OK;
}

//-----------------------------------------------------------------------------
// Perform per-frame update of audio
//-----------------------------------------------------------------------------
HRESULT Audio::UpdateAudio()
{
//OutputDebugString(L"testestestest");
return S_OK;
}

//-------------------------------------------------------------------------------
// Play source voice
//-------------------------------------------------------------------------------
HRESULT Audio::Play( std::wstring ID )
{
HRESULT hr = S_OK;

//Starts consumption and processing of audio by the voice
if ( FAILED ( hr = m_tracks[ ID ].GetSourceVoice()->Start( 0 ) ) )
return hr;

return S_OK;
}

//-------------------------------------------------------------------------------
// Stop source voice
//-------------------------------------------------------------------------------
HRESULT Audio::Stop( std::wstring ID )
{
HRESULT hr = S_OK;

// Stops consumption and processing of audio by the voice
if ( FAILED ( hr = m_tracks[ ID ].GetSourceVoice()->Stop( 0, XAUDIO2_COMMIT_NOW ) ) )
return hr;

return S_OK;
}


I am not sure what I doing wrong or how to go about fixing it. If you need any other code let me know?

Regards

Chad

Share this post


Link to post
Share on other sites
Advertisement
Definitely check out the docs for XAudio2:

"After Start is called it has no further effect if called again before IXAudio2SourceVoice::Stop is called. In addition multiple calls to Start without matching calls to IXAudio2SourceVoice::Stop will result in warning messages in debug builds."

So yeah, just call Stop first, then Start again.

Share this post


Link to post
Share on other sites
In my implementation, each sound is saved to a buffer, when played, the audio "engine" grabs a free track from a pool it creates on creation. With a large enough pool(32 "tracks" ) most sounds in game are short enough for this not to be an issue, i've had no problems with polyphony so far. If any mods are around, perhaps it's time gamedev had an "audio" forum?

Share this post


Link to post
Share on other sites
Quote:
Original post by GreenToad
Definitely check out the docs for XAudio2:

"After Start is called it has no further effect if called again before IXAudio2SourceVoice::Stop is called. In addition multiple calls to Start without matching calls to IXAudio2SourceVoice::Stop will result in warning messages in debug builds."

So yeah, just call Stop first, then Start again.


Hey,

I have been reading the docs but I obviously am missing that part of it and I assume others. Anyways, under your suggestion I am gathering that changing my "Play" function to something like..
//-------------------------------------------------------------------------------
// Play source voice
//-------------------------------------------------------------------------------
HRESULT Audio::Play( std::wstring ID )
{
HRESULT hr = S_OK;

Stop(ID);

//Starts consumption and processing of audio by the voice
if ( FAILED ( hr = m_tracks[ ID ].GetSourceVoice()->Start( 0 ) ) )
return hr;

return S_OK;
}


would solve my problem. But unfortunately this does not work. Do you have any other suggestions as to where I should be going from this point. In the mean time I will start re-reading the docs becouse I for the life of me can't see a reference to where it tell me that I have to call Stop on the voice before playing it again.

Thanks so much for the response....

Chad

Share this post


Link to post
Share on other sites
Quote:
Original post by Burnt_Fyr
In my implementation, each sound is saved to a buffer, when played, the audio "engine" grabs a free track from a pool it creates on creation. With a large enough pool(32 "tracks" ) most sounds in game are short enough for this not to be an issue, i've had no problems with polyphony so far. If any mods are around, perhaps it's time gamedev had an "audio" forum?



Hey,

So your suggestion that upon initialization of my "audio manager" that I create a pool of IXAudio2SourceVoice to be used. And as I create those I do not submit a buffer to them. Then when I need to play a track I search that pool of IXAudio2SourceVoice for a free one, SubmitSourceBuffer, and play sound. Do you then FlushSourceBuffers and repopulate them the same way next time you need to play that sound?

Sorry if I seem confused. Well the reality is I am. I would love to hear more about your implementation.

Regards

Chad

Share this post


Link to post
Share on other sites
I'm in no better a position then you, there is little information on Xaudio2 available.

My "Audiomanager" as you put it is a collection of objects, a sample device, and a mixer, and a few public methods.

The sampler is just a sound factory, each sound object is created with a pointer to the buffer.

The mixer is exactly as you would expect, it contains a pool of tracks, a set of busses (submix voices) and a master. each track is started on creation.


Audio::Play(SoundID) { // UINT, or char*, or string identifier

Sound* Sampler->GetSound(SoundID); // get the sound from the sampler...

Track* thistrack = Mixer->GetFreeTrack(); // get a track from the mixer...

thistrack->Submit(Sound->buffer);
}




Not pretty, but it's simple, and has worked so far.

Share this post


Link to post
Share on other sites
Quote:
Original post by Burnt_Fyr
I'm in no better a position then you, there is little information on Xaudio2 available.

My "Audiomanager" as you put it is a collection of objects, a sample device, and a mixer, and a few public methods.

The sampler is just a sound factory, each sound object is created with a pointer to the buffer.

The mixer is exactly as you would expect, it contains a pool of tracks, a set of busses (submix voices) and a master. each track is started on creation.

*** Source Snippet Removed ***

Not pretty, but it's simple, and has worked so far.


Thanks for so more insight into your ideas. Question(s)

1 - Are you calling a Stop() before you call a Start() like GreenToad suggested me?

2 - When you are creating your pool of tracks when are you calling CreateSourceVoice? Obviously you can't do this when a track is created because you need to pass a Pointer to your data to it. Are you doing this call every time go to play a sound?

Regards

Chad

[Edited by - chadsxe on July 22, 2010 9:50:38 AM]

Share this post


Link to post
Share on other sites
Hey chadsxe,

So I tried this in my own engine, it looks like stopping and starting the voice is similar to pausing and playing. Looks like if you want to completely restart the sound, I'd stop the voice, delete it, and make a new one.

You could also try doing something along the lines of calling voice->FlushSourceBuffers(), then resubmitting the sample using voice->SubmitSourceBuffer, though I'm not sure if this will work.

For reference, that text I quoted is in DirectX Audio > Referece > Xaudio2 > Interfaces > IXaudio2SourceVoice > Stop.

Share this post


Link to post
Share on other sites
Quote:
Original post by GreenToad
Hey chadsxe,

So I tried this in my own engine, it looks like stopping and starting the voice is similar to pausing and playing. Looks like if you want to completely restart the sound, I'd stop the voice, delete it, and make a new one.

You could also try doing something along the lines of calling voice->FlushSourceBuffers(), then resubmitting the sample using voice->SubmitSourceBuffer, though I'm not sure if this will work.

For reference, that text I quoted is in DirectX Audio > Referece > Xaudio2 > Interfaces > IXaudio2SourceVoice > Stop.


Hi,

Currently I am storing the sound buffer info inside struct, and every time I need to play the audio I destroy the old voice, recreate the voice, and then resubmit the buffer. It is working but I have this feeling that it is not the most efficient way to go about it. I am going to now try your suggestion of "FlushSourceBuffers". This seems like it might be a tad bit cleaner of an implementation then recreating a voice every time.

None the less thanks to all poster and I will again check in with another update and my current code tomorrow.

Regards

Chad

Share this post


Link to post
Share on other sites
currently calling stop on program exit :) The tracks, busses, and master voice are all created in the mixers constructor, or ::init() function if you prefer.



class sound {
std::string m_filename;
WAVEFORMATEX m_format;
void* m_pData; // used to hold Xaudio2source buffer, or other wave data
}


Mixer::Mixer(int nTracks, int nBusses) {
// create and start tracks store in m_pTracks;
// create busses, store in m_pBusses;
// create the mastering voice, store in m_pMaster


}



So all buffers and voices are created at load time.
when a sound is played, the audio manager requests a free track from the mixer, the sound from the soundsampler, and pushes the sound's buffer onto the track's sourcevoice. This voice is already started and will consume buffer data immediately. You can use a callback (haven't implemented my self yet) to notify the track that the sourcevoice has no pending buffers, and is free to be used again. As I said previously with around 32 tracks i've yet to need the call back, and return the next track in m_pTracks, wrapping the nexttrack pointer to the beggining when i've iterated through the full array/vector of tracks.

My implementation is just that, and as you can see is a work in progress, so don't run to far with out testing it in your situation. The client code only needs to know about the Audio "manager", which acts as a facade into the mixer/ sampler/Xaudio2 devices.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement