Jump to content
  • Advertisement
john_connor

C++ OpenAL queued buffers: making a music "playlist"

Recommended Posts

hi,

i'm trying to make a little sound class that manages all the openAL code in my app. i want to make a music playlist by queuing some buffers with musics to a source. it works. but forwarding the current "sampling position" of the source doesnt work if the first buffer has been processed.

<buffer1buffer1buffer1> <buffer2buffer2buffer2> <buffer3buffer3buffer3> ... and so on ...

...........| (sampler position)

as soon as the end of the first buffer is processed, and i want to skip some 3 seconds or so, it jumps back to the beginning of buffer 1 (and i dont know why)

1. how can i query the the buffers in a queued source?

2. how do i calculate the size in seconds of a buffer?

3. how do i forward the sampling position over several buffers in the queue?

thanks for any advice !!!

Share this post


Link to post
Share on other sites
Advertisement
Posted (edited)

update:

AL_BUFFERS_PROCESSED doesnt give me 1 when the source finished playing the first track ... despite whats written in the OpenAL 1.1 Specification !!!

does anyone know what could be wrong ?

Edited by john_connor

Share this post


Link to post
Share on other sites

Could you show us some code? It's hard to guess a what is wrong when we can't see what you're doing. I'm also a bit confused as to why you are using queued buffers if you want a gap between samples; usually the point of using queued buffers is to avoid there being any gap between playback?

Share this post


Link to post
Share on other sites
Posted (edited)

#include <al.h>
#include <alc.h>

#include <vector>
#include <string>
#include <chrono>
#include <iostream>

#include "WAV.h"


using namespace std;
using namespace chrono;


ALCdevice* device = nullptr;
ALCcontext* context = nullptr;
ALCint* attribute_list = NULL;

ALuint source = 0;
vector<ALuint> buffers;


ALenum Format(const CWAV& wav)
{
	if (wav.Header.NumChannels == 1)
		return wav.Header.BitsPerSample == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;

	return wav.Header.BitsPerSample == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
}


ALuint Load(const std::string & filename)
{
	/* load ... */
	CWAV wav;
	if (!CWAV::Load(filename, wav))
		return 0;

	/* create buffer */
	ALuint buffer = 0;
	alGenBuffers(1, &buffer);
	alBufferData(buffer, Format(wav), wav.AudioData.data(), wav.AudioData.size(), wav.Header.SampleRate);
	return buffer;
}


bool Initialize()
{
	device = alcOpenDevice(NULL);
	if (!device)
		return false;

	context = alcCreateContext(device, attribute_list);
	if (!context)
		return false;

	alcMakeContextCurrent(context);

	/* create source */
	alGenSources(1, &source);

	/* create some buffers */
	buffers.push_back(Load("track1.wav"));
	buffers.push_back(Load("track2.wav"));
	buffers.push_back(Load("track3.wav"));

	/* only buffers of the same format can be queued to the same source */
	ALint frequency_required = 0;
	ALint bits_required = 0;
	ALint channels_required = 0;

	for (auto& buffer : buffers)
	{
		ALint frequency = 0;
		ALint bits = 0;
		ALint channels = 0;

		alGetBufferi(buffer, AL_FREQUENCY, &frequency);
		alGetBufferi(buffer, AL_BITS, &bits);
		alGetBufferi(buffer, AL_CHANNELS, &channels);

		/* set buffer requirements */
		if (frequency_required == 0 && bits_required == 0 && channels_required == 0)
		{
			frequency_required = frequency;
			bits_required = bits;
			channels_required = channels;
		}

		/* check buffer requirements */
		if (frequency != frequency_required)
			return false;

		if (bits != bits_required)
			return false;

		if (channels != channels_required)
			return false;
	}

	/* queue buffers to source */
	alSourceQueueBuffers(source, buffers.size(), buffers.data());

	/* play source */
	alSourcei(source, AL_LOOPING, AL_TRUE);
	alSourcePlay(source);

	auto error = alGetError();
	return error == AL_NO_ERROR;
}


void CleanUp()
{
	/* detele source */
	alSourcei(source, AL_BUFFER, 0);
	alDeleteSources(1, &source);

	/* delete buffers */
	alDeleteBuffers(buffers.size(), buffers.data());

	alcMakeContextCurrent(nullptr);

	if (context)
		alcDestroyContext(context);

	if (device)
		alcCloseDevice(device);
}


int Time()
{
	static auto t_start = high_resolution_clock::now();
	auto t_now = high_resolution_clock::now();
	return duration_cast<duration<int>>(t_now - t_start).count();
}


int main(int arc, char* argv[])
{
	// prepare AL
	if (Initialize())
	{
		/* loop */
		while (true)
		{
			/* for each second */
			static int t1 = Time();
			int t2 = Time();
			if (t1 != t2)
			{
				t1 = t2;

				/* DEBUG */
				ALint buffers_processed = 0;
				alGetSourcei(source, AL_BUFFERS_PROCESSED, &buffers_processed);
				cout << "buffers_processed: " << buffers_processed << endl;
			}
		}
	}
	else 
	{
		cout << "failed to initialize" << endl;
		cin.get();
	}

	// release AL resources
	CleanUp();
	
	return 0;
}

 

WAV.cpp

WAV.h

above there is a test app. put some 3 wav music file beside the app, then just run it. each second it shows you AL_BUFFERS_PROCESSED in the cout console.

rename the music files to track1.wav, track2.wav and track3.wav

i get "buffers_processed: 0" all the time ...

Edited by john_connor

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Irusan, son of Arusan said:

Could you show us some code? It's hard to guess a what is wrong when we can't see what you're doing. I'm also a bit confused as to why you are using queued buffers if you want a gap between samples; usually the point of using queued buffers is to avoid there being any gap between playback?

how would you implement a "playlist" without queuing ?? ...

the thing is: when buffer 1 is finished playing, the "sampling position" of the source jumps back to 0 seconds as it starts to play buffer 2 ... how am i supposed to set the "sampling position" to somewhere in the middle of buffer 2 then ??

my conclusion is:

the AL implementation doesnt do what it is supposed to (namely giving me a 1 when buffer 1 has finished playing)

Edited by john_connor

Share this post


Link to post
Share on other sites

It's looping because this bit of code tells it to loop:

	alSourcei(source, AL_LOOPING, AL_TRUE);
	alSourcePlay(source);

Change that AL_TRUE to AL_FALSE and it won't loop anymore, and AL_BUFFERS_PROCESSED will get set to 1 after the first playthrough.

As to how you do a playlist without using queued buffers (which are a specific kind of thing in OpenAL), you simply keep track of time yourself, notice when the current sample has stopped, and use alSourcePlay to kick off the next one at the time of your choosing.

Share this post


Link to post
Share on other sites

THANK YOU VERY MUCH !!!

G-sus, without looping it works as expected 😉

5 minutes ago, Irusan, son of Arusan said:

As to how you do a playlist without using queued buffers (which are a specific kind of thing in OpenAL), you simply keep track of time yourself, notice when the current sample has stopped, and use alSourcePlay to kick off the next one at the time of your choosing.

am i supposed to create a "sound thread" that runs besides my app, managing all music / soundeffect-related stuff ?

or do it as my ap runs (about 60 times per second) ? how do you manage musics in you game / sim / whatever ?

i appreciate any advices !!

Share this post


Link to post
Share on other sites

You can do it in the main thread for this kind of simple maintenance. A separate sound thread is needed for more complex stuff.

Mind you, it occurs to me you could buffer a 3s sample of silence instead. I'd still prefer to control my own scheduling but it should work.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!