Fixed Alsa player, now mostly works. (Incorrect position reporting, strange samplerate glitch at start of playback.) And more fixes to build system.
Originally committed to SVN as r1128.
This commit is contained in:
parent
a278e4e4a3
commit
14582ad00a
2 changed files with 213 additions and 265 deletions
|
@ -1,5 +1,4 @@
|
||||||
EXTRA_DIST = res.rc $(srcdir)/*.h \
|
EXTRA_DIST = res.rc $(srcdir)/*.h
|
||||||
subtitles_provider_libass.cpp
|
|
||||||
SUFFIXES = .c .cpp .rc
|
SUFFIXES = .c .cpp .rc
|
||||||
|
|
||||||
SUBDIRS = bitmaps posix
|
SUBDIRS = bitmaps posix
|
||||||
|
@ -12,7 +11,8 @@ REVISION := $(if $(SVNREV),-DBUILD_SVN_REVISION=$(SVNREV)) $(if $(DARCSREV),-DBU
|
||||||
BUILDINFO := -DBUILD_CREDIT="\"$(shell whoami)\"" $(REVISION)
|
BUILDINFO := -DBUILD_CREDIT="\"$(shell whoami)\"" $(REVISION)
|
||||||
|
|
||||||
AM_CPPFLAGST = -DAEGISUB -Iposix -include posix/defines.h $(BUILDINFO)
|
AM_CPPFLAGST = -DAEGISUB -Iposix -include posix/defines.h $(BUILDINFO)
|
||||||
LDADD = posix/libposix.a ../lua51/src/liblua.a ../FexTrackerSource/libfex.a ../csri/lib/.libs/libcsri.a
|
aegisub_LDADD = posix/libposix.a ../lua51/src/liblua.a ../FexTrackerSource/libfex.a ../csri/lib/.libs/libcsri.a
|
||||||
|
aegisub_LDFLAGS =
|
||||||
|
|
||||||
if USE_LIBASS
|
if USE_LIBASS
|
||||||
LIBASS=subtitles_provider_libass.cpp
|
LIBASS=subtitles_provider_libass.cpp
|
||||||
|
@ -23,15 +23,15 @@ endif
|
||||||
AUDIO_PLAYER=audio_player.cpp
|
AUDIO_PLAYER=audio_player.cpp
|
||||||
if HAVE_PORTAUDIO
|
if HAVE_PORTAUDIO
|
||||||
AUDIO_PLAYER += audio_player_portaudio.cpp
|
AUDIO_PLAYER += audio_player_portaudio.cpp
|
||||||
LDADD += -lportaudio
|
aegisub_LDFLAGS += -lportaudio
|
||||||
endif
|
endif
|
||||||
if HAVE_ALSA
|
if HAVE_ALSA
|
||||||
AUDIO_PLAYER += audio_player_alsa.cpp
|
AUDIO_PLAYER += audio_player_alsa.cpp
|
||||||
LDADD += -lasound
|
aegisub_LDFLAGS += -lasound
|
||||||
endif
|
endif
|
||||||
if HAVE_PULSEAUDIO
|
if HAVE_PULSEAUDIO
|
||||||
AUDIO_PLAYER += audio_player_pulse.cpp
|
AUDIO_PLAYER += audio_player_pulse.cpp
|
||||||
LDADD += -lpulse
|
aegisub_LDFLAGS += -lpulse
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_RUBY
|
if HAVE_RUBY
|
||||||
|
@ -54,6 +54,19 @@ else
|
||||||
FFMPEG=
|
FFMPEG=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
## These aren't built, but are listed here so 'make dist' can always find all the sources
|
||||||
|
EXTRA_aegisub_SOURCES = \
|
||||||
|
subtitles_provider_libass.cpp \
|
||||||
|
spellchecker_hunspell.cpp \
|
||||||
|
audio_player_portaudio.cpp \
|
||||||
|
audio_player_alsa.cpp \
|
||||||
|
audio_player_pulse.cpp \
|
||||||
|
audio_player_dsound.cpp \
|
||||||
|
audio_provider_lavc.cpp \
|
||||||
|
lavc_file.cpp \
|
||||||
|
video_provider_lavc.cpp
|
||||||
|
|
||||||
aegisub_SOURCES = \
|
aegisub_SOURCES = \
|
||||||
$(AUDIO_PLAYER) \
|
$(AUDIO_PLAYER) \
|
||||||
$(AUTO4RUBY) \
|
$(AUTO4RUBY) \
|
||||||
|
|
|
@ -47,27 +47,23 @@
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
|
||||||
|
|
||||||
//////////////
|
///////////////
|
||||||
// Prototypes
|
// Alsa player
|
||||||
class AlsaPlayer;
|
class AlsaPlayer : public AudioPlayer {
|
||||||
|
|
||||||
|
|
||||||
//////////
|
|
||||||
// Thread
|
|
||||||
class AlsaPlayerThread : public wxThread {
|
|
||||||
private:
|
private:
|
||||||
wxMutex play_mutex; // held while playing
|
bool open;
|
||||||
wxSemaphore play_notify; // posted when a playback operation is set up
|
volatile bool playing;
|
||||||
wxSemaphore stop_notify; // when set, audio playback should stop asap
|
volatile float volume;
|
||||||
wxSemaphore shutdown_notify; // when set, thread should shutdown asap
|
|
||||||
wxMutex parameter_mutex;
|
|
||||||
|
|
||||||
AudioProvider *provider; // provides sample data!
|
volatile unsigned long start_frame; // first frame of playback
|
||||||
|
volatile unsigned long cur_frame; // last written frame + 1
|
||||||
|
volatile unsigned long end_frame; // last frame to play
|
||||||
|
unsigned long bpf; // bytes per frame
|
||||||
|
|
||||||
|
AudioProvider *provider;
|
||||||
snd_pcm_t *pcm_handle; // device handle
|
snd_pcm_t *pcm_handle; // device handle
|
||||||
snd_pcm_stream_t stream; // stream direction
|
snd_pcm_stream_t stream; // stream direction
|
||||||
snd_pcm_hw_params_t *hwparams; // hardware info
|
snd_async_handler_t *pcm_callback;
|
||||||
char *pcm_name; // device name
|
|
||||||
|
|
||||||
snd_pcm_format_t sample_format;
|
snd_pcm_format_t sample_format;
|
||||||
unsigned int rate; // sample rate of audio
|
unsigned int rate; // sample rate of audio
|
||||||
|
@ -75,40 +71,10 @@ private:
|
||||||
int periods; // number of bytes in a frame
|
int periods; // number of bytes in a frame
|
||||||
snd_pcm_uframes_t bufsize; // size of buffer in frames
|
snd_pcm_uframes_t bufsize; // size of buffer in frames
|
||||||
|
|
||||||
volatile double volume; // volume to get audio at
|
|
||||||
volatile unsigned long start_frame; // first frame of playback
|
|
||||||
volatile unsigned long cur_frame; // last written frame + 1
|
|
||||||
volatile unsigned long end_frame; // last frame to play
|
|
||||||
|
|
||||||
void SetUpHardware();
|
void SetUpHardware();
|
||||||
void PlaybackLoop();
|
void SetUpAsync();
|
||||||
|
|
||||||
public:
|
static void async_write_handler(snd_async_handler_t *pcm_callback);
|
||||||
AlsaPlayerThread(AudioProvider *_provider);
|
|
||||||
~AlsaPlayerThread();
|
|
||||||
wxThread::ExitCode Entry();
|
|
||||||
|
|
||||||
// The following methods are all thread safe
|
|
||||||
void Play(unsigned long _start_frame, unsigned long _num_frames); // Notify thread to stop any playback and instead play specified range
|
|
||||||
void SetEndPosition(unsigned long _end_frame); // Notify thread to use new end position
|
|
||||||
void Stop(); // Notify thread to stop audio playback
|
|
||||||
void StopWait(); // Notify thread to stop, and wait for it to do it
|
|
||||||
void Shutdown(); // Notify the thread to stop playback and die
|
|
||||||
void SetVolume(double new_volume); // Set volume
|
|
||||||
unsigned long GetStartPosition(); // Get first played back frame number
|
|
||||||
unsigned long GetEndPosition(); // Get last frame number to be played back
|
|
||||||
unsigned long GetCurrentPosition(); // Get currently played back frame number
|
|
||||||
bool IsPlaying();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
///////////////
|
|
||||||
// Alsa player
|
|
||||||
class AlsaPlayer : public AudioPlayer {
|
|
||||||
private:
|
|
||||||
float volume;
|
|
||||||
|
|
||||||
AlsaPlayerThread *thread;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AlsaPlayer();
|
AlsaPlayer();
|
||||||
|
@ -145,143 +111,60 @@ public:
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
AlsaPlayer::AlsaPlayer() {
|
AlsaPlayer::AlsaPlayer()
|
||||||
|
{
|
||||||
volume = 1.0f;
|
volume = 1.0f;
|
||||||
thread = NULL;
|
open = false;
|
||||||
|
playing = false;
|
||||||
|
start_frame = cur_frame = end_frame = bpf = 0;
|
||||||
|
provider = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
// Destructor
|
// Destructor
|
||||||
AlsaPlayer::~AlsaPlayer() {
|
AlsaPlayer::~AlsaPlayer()
|
||||||
|
{
|
||||||
CloseStream();
|
CloseStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
// Open stream
|
// Open stream
|
||||||
void AlsaPlayer::OpenStream() {
|
void AlsaPlayer::OpenStream()
|
||||||
|
{
|
||||||
CloseStream();
|
CloseStream();
|
||||||
|
|
||||||
// Get provider
|
// Get provider
|
||||||
AudioProvider *provider = GetProvider();
|
provider = GetProvider();
|
||||||
|
bpf = provider->GetChannels() * provider->GetBytesPerSample();
|
||||||
|
|
||||||
thread = new AlsaPlayerThread(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////
|
|
||||||
// Close stream
|
|
||||||
void AlsaPlayer::CloseStream() {
|
|
||||||
if (!thread) return;
|
|
||||||
|
|
||||||
thread->Shutdown();
|
|
||||||
thread->Wait();
|
|
||||||
thread = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////
|
|
||||||
// Play
|
|
||||||
void AlsaPlayer::Play(__int64 start,__int64 count) {
|
|
||||||
// Make sure that it's stopped
|
|
||||||
thread->Stop();
|
|
||||||
|
|
||||||
thread->Play(start, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////
|
|
||||||
// Stop
|
|
||||||
void AlsaPlayer::Stop(bool timerToo) {
|
|
||||||
if (thread) thread->Stop();
|
|
||||||
|
|
||||||
if (timerToo && displayTimer) {
|
|
||||||
displayTimer->Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool AlsaPlayer::IsPlaying()
|
|
||||||
{
|
|
||||||
return thread && thread->IsPlaying();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////
|
|
||||||
// Set end
|
|
||||||
void AlsaPlayer::SetEndPosition(__int64 pos) {
|
|
||||||
if (thread) thread->SetEndPosition(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
|
||||||
// Set current position
|
|
||||||
void AlsaPlayer::SetCurrentPosition(__int64 pos) {
|
|
||||||
assert(false); // not supported (yet?)
|
|
||||||
// I believe this isn't used anywhere. I hope not.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
__int64 AlsaPlayer::GetStartPosition()
|
|
||||||
{
|
|
||||||
if (thread) return thread->GetStartPosition();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
__int64 AlsaPlayer::GetEndPosition()
|
|
||||||
{
|
|
||||||
if (thread) return thread->GetEndPosition();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
|
||||||
// Get current position
|
|
||||||
__int64 AlsaPlayer::GetCurrentPosition() {
|
|
||||||
if (thread) return thread->GetCurrentPosition();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// Thread constructor
|
|
||||||
AlsaPlayerThread::AlsaPlayerThread(AudioProvider *_provider)
|
|
||||||
: wxThread(wxTHREAD_JOINABLE)
|
|
||||||
, play_notify(0, 1)
|
|
||||||
, stop_notify(0, 1)
|
|
||||||
, shutdown_notify(0, 1)
|
|
||||||
, provider(_provider)
|
|
||||||
{
|
|
||||||
// We want playback
|
// We want playback
|
||||||
stream = SND_PCM_STREAM_PLAYBACK;
|
stream = SND_PCM_STREAM_PLAYBACK;
|
||||||
// Use default device and automatic sample type conversion
|
// And get a device name
|
||||||
wxString device = Options.AsText(_T("Audio Alsa Device"));
|
wxString device = Options.AsText(_T("Audio Alsa Device"));
|
||||||
pcm_name = strdup(device.mb_str(wxConvUTF8));
|
|
||||||
|
|
||||||
// Allocate params structure
|
|
||||||
snd_pcm_hw_params_alloca(&hwparams);
|
|
||||||
|
|
||||||
// Open device for blocking access
|
// Open device for blocking access
|
||||||
if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0) {
|
if (snd_pcm_open(&pcm_handle, device.mb_str(wxConvUTF8), stream, 0) < 0) { // supposedly we don't want SND_PCM_ASYNC even for async playback
|
||||||
throw _T("Error opening default PCM device");
|
throw _T("Error opening specified PCM device");
|
||||||
}
|
}
|
||||||
|
|
||||||
SetUpHardware();
|
SetUpHardware();
|
||||||
|
|
||||||
|
// Register async handler
|
||||||
|
SetUpAsync();
|
||||||
|
|
||||||
|
// Now ready
|
||||||
|
open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////
|
void AlsaPlayer::SetUpHardware()
|
||||||
// Thread destructor
|
|
||||||
AlsaPlayerThread::~AlsaPlayerThread() {
|
|
||||||
free(pcm_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayerThread::SetUpHardware()
|
|
||||||
{
|
{
|
||||||
|
// Allocate params structure
|
||||||
|
snd_pcm_hw_params_t *hwparams;
|
||||||
|
snd_pcm_hw_params_malloc(&hwparams);
|
||||||
|
|
||||||
// Get hardware params
|
// Get hardware params
|
||||||
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
|
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
|
||||||
throw _T("Error setting up default PCM device");
|
throw _T("Error setting up default PCM device");
|
||||||
|
@ -293,7 +176,16 @@ void AlsaPlayerThread::SetUpHardware()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sample format
|
// Set sample format
|
||||||
sample_format = SND_PCM_FORMAT_S16_LE; // TODO: support other formats
|
switch (provider->GetBytesPerSample()) {
|
||||||
|
case 1:
|
||||||
|
sample_format = SND_PCM_FORMAT_S8;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
sample_format = SND_PCM_FORMAT_S16_LE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw _T("Can only handle 8 and 16 bit sound");
|
||||||
|
}
|
||||||
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, sample_format) < 0) {
|
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, sample_format) < 0) {
|
||||||
throw _T("Could not set sample format");
|
throw _T("Could not set sample format");
|
||||||
}
|
}
|
||||||
|
@ -313,8 +205,9 @@ void AlsaPlayerThread::SetUpHardware()
|
||||||
throw _T("Could not set number of channels");
|
throw _T("Could not set number of channels");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set periods (size in bytes of one frame?)
|
// Set periods (number of bytes ideally written at a time)
|
||||||
periods = provider->GetChannels() * provider->GetBytesPerSample();
|
// Somewhat arbitrary for now (256 frames)
|
||||||
|
periods = provider->GetChannels() * provider->GetBytesPerSample() * 256;
|
||||||
if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0) {
|
if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0) {
|
||||||
throw _T("Could not set periods");
|
throw _T("Could not set periods");
|
||||||
}
|
}
|
||||||
|
@ -333,145 +226,187 @@ void AlsaPlayerThread::SetUpHardware()
|
||||||
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
|
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
|
||||||
throw _T("Failed applying sound hardware settings");
|
throw _T("Failed applying sound hardware settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And free memory again
|
||||||
|
snd_pcm_hw_params_free(hwparams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////
|
void AlsaPlayer::SetUpAsync()
|
||||||
// Thread entry point
|
|
||||||
wxThread::ExitCode AlsaPlayerThread::Entry()
|
|
||||||
{
|
{
|
||||||
// Loop as long as we aren't told to shutdown
|
// Prepare software params struct
|
||||||
while (shutdown_notify.TryWait() == wxSEMA_BUSY) {
|
snd_pcm_sw_params_t *sw_params;
|
||||||
// Wait for a playback operation
|
snd_pcm_sw_params_malloc (&sw_params);
|
||||||
while (play_notify.WaitTimeout(100) == wxSEMA_NO_ERROR) {
|
|
||||||
// So playback was posted... now play something
|
// Get current parameters
|
||||||
play_mutex.Lock();
|
if (snd_pcm_sw_params_current(pcm_handle, sw_params) < 0) {
|
||||||
PlaybackLoop();
|
throw _T("Couldn't get current SW params");
|
||||||
play_mutex.Unlock();
|
}
|
||||||
|
|
||||||
|
// How full the buffer must be before playback begins
|
||||||
|
if (snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, bufsize/2) < 0) {
|
||||||
|
throw _T("Failed setting start threshold");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The the largest write guaranteed never to block
|
||||||
|
if (snd_pcm_sw_params_set_avail_min(pcm_handle, sw_params, bufsize/4) < 0) {
|
||||||
|
throw _T("Failed setting min available buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply settings
|
||||||
|
if (snd_pcm_sw_params(pcm_handle, sw_params) < 0) {
|
||||||
|
throw _T("Failed applying SW params");
|
||||||
|
}
|
||||||
|
|
||||||
|
// And free struct again
|
||||||
|
snd_pcm_sw_params_free(sw_params);
|
||||||
|
|
||||||
|
// Attach async handler
|
||||||
|
if (snd_async_add_pcm_handler(&pcm_callback, pcm_handle, async_write_handler, this) < 0) {
|
||||||
|
throw _T("Failed attaching async handler");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlsaPlayerThread::PlaybackLoop()
|
////////////////
|
||||||
|
// Close stream
|
||||||
|
void AlsaPlayer::CloseStream()
|
||||||
{
|
{
|
||||||
unsigned long datalen = rate/2;
|
if (!open) return;
|
||||||
void *data = malloc(datalen * periods);
|
|
||||||
unsigned long cur_pos = 0;
|
|
||||||
|
|
||||||
while (true) {
|
Stop();
|
||||||
// Get/set parameters
|
|
||||||
parameter_mutex.Lock();
|
|
||||||
double vol = volume;
|
|
||||||
cur_frame = cur_pos;
|
|
||||||
unsigned long end_pos = end_frame;
|
|
||||||
parameter_mutex.Unlock();
|
|
||||||
|
|
||||||
// Check for stop
|
// Remove async handler
|
||||||
if (stop_notify.TryWait() != wxSEMA_BUSY) {
|
snd_async_del_handler(pcm_callback);
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
snd_pcm_close(pcm_handle);
|
||||||
|
|
||||||
|
// No longer working
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////
|
||||||
|
// Play
|
||||||
|
void AlsaPlayer::Play(__int64 start,__int64 count)
|
||||||
|
{
|
||||||
|
if (playing) {
|
||||||
|
// Quick reset
|
||||||
|
playing = false;
|
||||||
snd_pcm_drop(pcm_handle);
|
snd_pcm_drop(pcm_handle);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for end of stream
|
// Set params
|
||||||
if (cur_pos >= end_pos) {
|
start_frame = start;
|
||||||
snd_pcm_drain(pcm_handle);
|
cur_frame = start;
|
||||||
break;
|
end_frame = start + count;
|
||||||
}
|
playing = true;
|
||||||
|
|
||||||
// Write some frames
|
// Prepare a bit
|
||||||
provider->GetAudioWithVolume(data, cur_pos, datalen, vol);
|
snd_pcm_prepare (pcm_handle);
|
||||||
snd_pcm_sframes_t written = snd_pcm_writei(pcm_handle, data, datalen);
|
async_write_handler(pcm_callback);
|
||||||
cur_pos += written;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(data);
|
// And go!
|
||||||
|
snd_pcm_start(pcm_handle);
|
||||||
|
|
||||||
|
// Update timer
|
||||||
|
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayerThread::Play(unsigned long _start_frame, unsigned long _num_frames)
|
////////
|
||||||
|
// Stop
|
||||||
|
void AlsaPlayer::Stop(bool timerToo)
|
||||||
{
|
{
|
||||||
StopWait();
|
if (!open) return;
|
||||||
|
if (!playing) return;
|
||||||
|
|
||||||
cur_frame = start_frame = _start_frame;
|
// Reset data
|
||||||
end_frame = start_frame + _num_frames;
|
playing = false;
|
||||||
|
start_frame = 0;
|
||||||
|
cur_frame = 0;
|
||||||
|
end_frame = 0;
|
||||||
|
|
||||||
play_notify.Post();
|
// Then drop the playback
|
||||||
|
snd_pcm_drop(pcm_handle);
|
||||||
|
|
||||||
|
if (timerToo && displayTimer) {
|
||||||
|
displayTimer->Stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayerThread::SetEndPosition(unsigned long _end_frame)
|
bool AlsaPlayer::IsPlaying()
|
||||||
{
|
{
|
||||||
parameter_mutex.Lock();
|
return playing;
|
||||||
end_frame = _end_frame;
|
}
|
||||||
parameter_mutex.Unlock();
|
|
||||||
|
|
||||||
|
///////////
|
||||||
|
// Set end
|
||||||
|
void AlsaPlayer::SetEndPosition(__int64 pos)
|
||||||
|
{
|
||||||
|
end_frame = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Stop playback thread
|
// Set current position
|
||||||
void AlsaPlayerThread::Stop()
|
void AlsaPlayer::SetCurrentPosition(__int64 pos)
|
||||||
{
|
{
|
||||||
stop_notify.Post();
|
cur_frame = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayerThread::StopWait()
|
__int64 AlsaPlayer::GetStartPosition()
|
||||||
{
|
{
|
||||||
stop_notify.Post();
|
|
||||||
// Now wait for the play mutex to have been freed
|
|
||||||
play_mutex.Lock();
|
|
||||||
play_mutex.Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayerThread::Shutdown()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
shutdown_notify.Post();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayerThread::SetVolume(double new_volume)
|
|
||||||
{
|
|
||||||
parameter_mutex.Lock();
|
|
||||||
volume = new_volume;
|
|
||||||
parameter_mutex.Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long AlsaPlayerThread::GetStartPosition()
|
|
||||||
{
|
|
||||||
wxMutexLocker lock(parameter_mutex);
|
|
||||||
return start_frame;
|
return start_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned long AlsaPlayerThread::GetEndPosition()
|
__int64 AlsaPlayer::GetEndPosition()
|
||||||
{
|
{
|
||||||
wxMutexLocker lock(parameter_mutex);
|
|
||||||
return end_frame;
|
return end_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned long AlsaPlayerThread::GetCurrentPosition()
|
////////////////////////
|
||||||
|
// Get current position
|
||||||
|
__int64 AlsaPlayer::GetCurrentPosition()
|
||||||
{
|
{
|
||||||
// Step 1: Get last written frame number
|
return cur_frame; // FIXME
|
||||||
parameter_mutex.Lock();
|
|
||||||
unsigned long pos = cur_frame;
|
|
||||||
parameter_mutex.Unlock();
|
|
||||||
|
|
||||||
// Step 2: ???
|
|
||||||
|
|
||||||
// Step 3: Profit!
|
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool AlsaPlayerThread::IsPlaying()
|
void AlsaPlayer::async_write_handler(snd_async_handler_t *pcm_callback)
|
||||||
{
|
{
|
||||||
return play_mutex.TryLock() == wxMUTEX_BUSY;
|
// TODO: check for broken pipes in here and restore as needed
|
||||||
|
AlsaPlayer *player = (AlsaPlayer*)snd_async_handler_get_callback_private(pcm_callback);
|
||||||
|
|
||||||
|
if (player->cur_frame >= player->end_frame + player->rate) {
|
||||||
|
// More than a second past end of stream
|
||||||
|
snd_pcm_drain(player->pcm_handle);
|
||||||
|
player->playing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_sframes_t frames = snd_pcm_avail_update(player->pcm_handle);
|
||||||
|
|
||||||
|
if (player->cur_frame >= player->end_frame) {
|
||||||
|
// Past end of stream, add some silence
|
||||||
|
void *buf = calloc(frames, player->bpf);
|
||||||
|
snd_pcm_writei(player->pcm_handle, buf, frames);
|
||||||
|
free(buf);
|
||||||
|
player->cur_frame += frames;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *buf = malloc(frames * player->bpf);
|
||||||
|
player->provider->GetAudioWithVolume(buf, player->cur_frame, frames, player->volume);
|
||||||
|
snd_pcm_writei(player->pcm_handle, buf, frames);
|
||||||
|
free(buf);
|
||||||
|
player->cur_frame += frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue