Clean up the OpenAL audio player a bit

Remove pointless volatile modifiers from member variables (there aren't
even any threads involved).

Eliminate all heap memory allocation during playback.

Move comments into the doxygen comments and add some more.

Throw typed exceptions.

Originally committed to SVN as r5889.
This commit is contained in:
Thomas Goyne 2011-11-19 01:14:13 +00:00
parent 70fcece459
commit 70ba90f024
2 changed files with 98 additions and 191 deletions

View File

@ -41,7 +41,7 @@
#include <libaegisub/log.h>
#include "audio_player_openal.h"
#include "frame_main.h"
#include "utils.h"
// Auto-link to OpenAL lib for MSVC
@ -49,82 +49,71 @@
#pragma comment(lib, "openal32.lib")
#endif
/// @brief Constructor
///
OpenALPlayer::OpenALPlayer()
: open(false)
, playing(false)
, volume(1.f)
, samplerate(0)
, bpf(0)
, start_frame(0)
, cur_frame(0)
, end_frame(0)
, device(0)
, context(0)
{
volume = 1.0f;
open = false;
playing = false;
start_frame = cur_frame = end_frame = bpf = 0;
provider = 0;
}
/// @brief Destructor
///
OpenALPlayer::~OpenALPlayer()
{
CloseStream();
}
/// @brief Open stream
///
void OpenALPlayer::OpenStream()
{
CloseStream();
// Get provider
provider = GetProvider();
bpf = provider->GetChannels() * provider->GetBytesPerSample();
try {
// Open device
device = alcOpenDevice(0);
if (!device) throw OpenALException("Failed opening default OpenAL device");
// Open device
device = alcOpenDevice(0);
if (!device) {
throw "Failed opening default OpenAL device";
}
// Create context
context = alcCreateContext(device, 0);
if (!context) throw OpenALException("Failed creating OpenAL context");
if (!alcMakeContextCurrent(context)) throw OpenALException("Failed selecting OpenAL context");
// Create context
context = alcCreateContext(device, 0);
if (!context) {
alcCloseDevice(device);
throw "Failed creating OpenAL context";
// Clear error code
alGetError();
// Generate buffers
alGenBuffers(num_buffers, buffers);
if (alGetError() != AL_NO_ERROR) throw OpenALException("Error generating OpenAL buffers");
// Generate source
alGenSources(1, &source);
if (alGetError() != AL_NO_ERROR) {
alDeleteBuffers(num_buffers, buffers);
throw OpenALException("Error generating OpenAL source");
}
}
if (!alcMakeContextCurrent(context)) {
catch (...)
{
alcDestroyContext(context);
alcCloseDevice(device);
throw "Failed selecting OpenAL context";
}
// Clear error code
alGetError();
// Generate buffers
alGenBuffers(num_buffers, buffers);
if (alGetError() != AL_NO_ERROR) {
alcDestroyContext(context);
alcCloseDevice(device);
throw "Error generating OpenAL buffers";
}
// Generate source
alGenSources(1, &source);
if (alGetError() != AL_NO_ERROR) {
alDeleteBuffers(num_buffers, buffers);
alcDestroyContext(context);
alcCloseDevice(device);
throw "Error generating OpenAL source";
context = 0;
device = 0;
throw;
}
// Determine buffer length
samplerate = provider->GetSampleRate();
buffer_length = samplerate / num_buffers / 2; // buffers for half a second of audio
decode_buffer.resize(samplerate * bpf / num_buffers / 2); // buffers for half a second of audio
// Now ready
open = true;
}
/// @brief Close stream
/// @return
///
void OpenALPlayer::CloseStream()
{
if (!open) return;
@ -136,15 +125,14 @@ void OpenALPlayer::CloseStream()
alcDestroyContext(context);
alcCloseDevice(device);
context = 0;
device = 0;
// No longer working
open = false;
}
/// @brief Play
/// @param start
/// @param count
///
void OpenALPlayer::Play(int64_t start,int64_t count)
void OpenALPlayer::Play(int64_t start, int64_t count)
{
if (playing) {
// Quick reset
@ -175,10 +163,6 @@ void OpenALPlayer::Play(int64_t start,int64_t count)
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
}
/// @brief Stop
/// @param timerToo
/// @return
///
void OpenALPlayer::Stop(bool timerToo)
{
if (!open) return;
@ -195,53 +179,34 @@ void OpenALPlayer::Stop(bool timerToo)
alSourceStop(source);
alSourcei(source, AL_BUFFER, 0);
if (timerToo && displayTimer) {
displayTimer->Stop();
}
if (timerToo && displayTimer) {
displayTimer->Stop();
}
}
/// @brief DOCME
/// @param count
///
void OpenALPlayer::FillBuffers(ALsizei count)
{
LOG_D("player/audio/openal") << "count=" << count << " " << "buffers_free=" << buffers_free;
if (count > buffers_free) count = buffers_free;
if (count < 1) count = 1;
// Get memory to hold sound buffers
void *data = malloc(buffer_length * bpf);
// Do the actual filling/queueing
ALuint bufid = buf_first_free;
while (count > 0) {
ALsizei fill_len = buffer_length;
if (fill_len > (ALsizei)(end_frame - cur_frame)) fill_len = (ALsizei)(end_frame - cur_frame);
if (fill_len < 0) fill_len = 0;
LOG_D("player/audio/openal") << "buffer_length=" << buffer_length << " fill_len=" << fill_len << " end_frame-cur-frame=" << end_frame-cur_frame;
for (count = mid(1, count, buffers_free); count > 0; --count) {
ALsizei fill_len = mid<ALsizei>(0, decode_buffer.size() / bpf, end_frame - cur_frame);
if (fill_len > 0)
// Get fill_len frames of audio
provider->GetAudioWithVolume(data, cur_frame, fill_len, volume);
if (fill_len < buffer_length)
provider->GetAudioWithVolume(&decode_buffer[0], cur_frame, fill_len, volume);
if ((size_t)fill_len * bpf < decode_buffer.size())
// And zerofill the rest
memset((char*)data+(fill_len*bpf), 0, (buffer_length-fill_len)*bpf);
memset(&decode_buffer[fill_len * bpf], 0, decode_buffer.size() - fill_len * bpf);
cur_frame += fill_len;
alBufferData(buffers[bufid], AL_FORMAT_MONO16, data, buffer_length*bpf, samplerate);
alSourceQueueBuffers(source, 1, &buffers[bufid]); // FIXME: collect buffer handles and queue all at once instead of one at a time?
if (++bufid >= num_buffers) bufid = 0;
count--; buffers_free--;
alBufferData(buffers[buf_first_free], AL_FORMAT_MONO16, &decode_buffer[0], decode_buffer.size(), samplerate);
alSourceQueueBuffers(source, 1, &buffers[buf_first_free]); // FIXME: collect buffer handles and queue all at once instead of one at a time?
buf_first_free = (buf_first_free + 1) % num_buffers;
--buffers_free;
}
buf_first_free = bufid;
// Free buffer memory again
free(data);
}
/// @brief DOCME
///
void OpenALPlayer::Notify()
{
ALsizei newplayed;
@ -251,14 +216,12 @@ void OpenALPlayer::Notify()
if (newplayed > 0) {
// Reclaim buffers
ALuint *bufs = new ALuint[newplayed];
ALsizei i = 0;
while (i < newplayed) {
bufs[i++] = buffers[buf_first_queued];
if (++buf_first_queued >= num_buffers) buf_first_queued = 0;
ALuint bufs[num_buffers];
for (ALsizei i = 0; i < newplayed; ++i) {
bufs[i] = buffers[buf_first_queued];
buf_first_queued = (buf_first_queued + 1) % num_buffers;
}
alSourceUnqueueBuffers(source, newplayed, bufs);
delete[] bufs;
buffers_free += newplayed;
// Update
@ -269,62 +232,29 @@ void OpenALPlayer::Notify()
FillBuffers(newplayed);
}
LOG_D("player/audio/openal") << "frames played=" << (buffers_played - num_buffers) * buffer_length << " num frames=" << end_frame - start_frame;
LOG_D("player/audio/openal") << "frames played=" << (buffers_played - num_buffers) * decode_buffer.size() / bpf << " num frames=" << end_frame - start_frame;
// Check that all of the selected audio plus one full set of buffers has been queued
if ((buffers_played - num_buffers) * buffer_length > (ALsizei)(end_frame - start_frame)) {
// Then stop
if ((int64_t)(buffers_played - num_buffers) * decode_buffer.size() > (end_frame - start_frame) * bpf) {
Stop(true);
}
}
/// @brief DOCME
/// @return
///
bool OpenALPlayer::IsPlaying()
{
return playing;
}
/// @brief Set end
/// @param pos
///
void OpenALPlayer::SetEndPosition(int64_t pos)
{
end_frame = pos;
}
/// @brief Set current position
/// @param pos
///
void OpenALPlayer::SetCurrentPosition(int64_t pos)
{
cur_frame = pos;
}
/// @brief DOCME
/// @return
///
int64_t OpenALPlayer::GetStartPosition()
{
return start_frame;
}
/// @brief DOCME
/// @return
///
int64_t OpenALPlayer::GetEndPosition()
{
return end_frame;
}
/// @brief Get current position
///
int64_t OpenALPlayer::GetCurrentPosition()
{
// FIXME: this should be based on not duration played but actual sample being heard
// (during video playback, cur_frame might get changed to resync)
long extra = playback_segment_timer.Time();
return buffers_played * buffer_length + start_frame + extra * samplerate / 1000;
return buffers_played * decode_buffer.size() / bpf + start_frame + extra * samplerate / 1000;
}
#endif // WITH_OPENAL

View File

@ -49,78 +49,63 @@
#include <AL/alc.h>
#endif
#ifndef AGI_PRE
#include <vector>
#endif
#include <libaegisub/exception.h>
DEFINE_SIMPLE_EXCEPTION_NOINNER(OpenALException, agi::Exception, "audio/player/openal/generic")
/// DOCME
/// @class OpenALPlayer
/// @brief DOCME
///
/// DOCME
class OpenALPlayer : public AudioPlayer, wxTimer {
private:
/// DOCME
bool open;
/// DOCME
volatile bool playing;
/// DOCME
volatile float volume;
/// DOCME
/// Number of OpenAL buffers to use
static const ALsizei num_buffers = 8;
/// DOCME
ALsizei buffer_length;
bool open; ///< Is the player ready to play?
bool playing; ///< Is audio currently playing?
/// DOCME
ALsizei samplerate;
float volume; ///< Current audio volume
ALsizei samplerate; ///< Sample rate of the audio
int bpf; ///< Bytes per frame
/// DOCME
volatile unsigned long start_frame; // first frame of playback
int64_t start_frame; ///< First frame of playbacka
int64_t cur_frame; ///< Next frame to write to playback buffers
int64_t end_frame; ///< Last frame to play
/// DOCME
volatile unsigned long cur_frame; // last written frame + 1
ALCdevice *device; ///< OpenAL device handle
ALCcontext *context; ///< OpenAL sound context
ALuint buffers[num_buffers]; ///< OpenAL sound buffers
ALuint source; ///< OpenAL playback source
/// DOCME
volatile unsigned long end_frame; // last frame to play
/// Index into buffers, first free (unqueued) buffer to be filled
ALsizei buf_first_free;
/// DOCME
unsigned long bpf; // bytes per frame
/// Index into buffers, first queued (non-free) buffer
ALsizei buf_first_queued;
/// DOCME
AudioProvider *provider;
/// Number of free buffers
ALsizei buffers_free;
/// DOCME
ALCdevice *device; // device handle
/// DOCME
ALCcontext *context; // sound context
/// DOCME
ALuint buffers[num_buffers]; // sound buffers
/// DOCME
ALuint source; // playback source
/// DOCME
ALsizei buf_first_free; // index into buffers, first free (unqueued) buffer
/// DOCME
ALsizei buf_first_queued; // index into buffers, first queued (non-free) buffer
/// DOCME
ALsizei buffers_free; // number of free buffers
/// DOCME
/// Number of buffers which have been fully played since playback was last started
ALsizei buffers_played;
/// DOCME
wxStopWatch playback_segment_timer;
/// Buffer to decode audio into
std::vector<char> decode_buffer;
/// Fill count OpenAL buffers
void FillBuffers(ALsizei count);
protected:
void Notify(); // from wxTimer
/// wxTimer override to periodically fill available buffers
void Notify();
public:
OpenALPlayer();
@ -131,23 +116,15 @@ public:
void Play(int64_t start,int64_t count);
void Stop(bool timerToo=true);
bool IsPlaying();
bool IsPlaying() { return playing; }
int64_t GetStartPosition();
int64_t GetEndPosition();
int64_t GetStartPosition() { return start_frame; }
int64_t GetEndPosition() { return end_frame; }
int64_t GetCurrentPosition();
void SetEndPosition(int64_t pos);
void SetCurrentPosition(int64_t pos);
/// @brief DOCME
/// @param vol
/// @return
///
void SetVolume(double vol) { volume = vol; }
/// @brief DOCME
/// @return
///
double GetVolume() { return volume; }
};
#endif