forked from mia/Aegisub
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:
parent
70fcece459
commit
70ba90f024
2 changed files with 98 additions and 191 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue