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

View file

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