Improve audio open error reporting and remove a pile of unused stuff in AudioPlayer

Originally committed to SVN as r6223.
This commit is contained in:
Thomas Goyne 2012-01-08 01:33:39 +00:00
parent a927672c27
commit df9c6b627f
26 changed files with 226 additions and 303 deletions

View file

@ -292,18 +292,14 @@ void AudioController::OpenAudio(const wxString &url)
/* /*
* Assume it's not a URI but instead a filename in the platform's native format. * Assume it's not a URI but instead a filename in the platform's native format.
*/ */
wxFileName fn(url); try {
if (!fn.FileExists()) provider = AudioProviderFactory::GetProvider(url);
{ StandardPaths::SetPathValue("?audio", wxFileName(url).GetPath());
config::mru->Remove("Audio", STD_STR(url)); }
agi::FileNotFoundError fnf(STD_STR(url)); catch (...) {
throw agi::AudioOpenError( config::mru->Remove("Audio", STD_STR(url));
"Failed opening audio file (parsing as plain filename)", throw;
&fnf);
} }
provider = AudioProviderFactory::GetProvider(url);
StandardPaths::SetPathValue("?audio", fn.GetPath());
} }
try try

View file

@ -445,6 +445,27 @@ public:
}; };
namespace agi { namespace agi {
DEFINE_BASE_EXCEPTION(AudioControllerError, Exception) /// Base class for all audio-related errors
DEFINE_SIMPLE_EXCEPTION(AudioOpenError, AudioControllerError, "audio_controller/open_failed") DEFINE_BASE_EXCEPTION(AudioError, Exception)
/// Opening the audio failed for any reason
DEFINE_SIMPLE_EXCEPTION(AudioOpenError, AudioError, "audio/open")
/// There are no audio providers available to open audio files
DEFINE_SIMPLE_EXCEPTION(NoAudioProvidersError, AudioOpenError, "audio/open/no_providers")
/// The file exists, but no providers could find any audio tracks in it
DEFINE_SIMPLE_EXCEPTION(AudioDataNotFoundError, AudioOpenError, "audio/open/no_tracks")
/// There are audio tracks, but no provider could actually read them
DEFINE_SIMPLE_EXCEPTION(AudioProviderOpenError, AudioOpenError, "audio/open/provider")
/// The audio cache failed to initialize
DEFINE_SIMPLE_EXCEPTION(AudioCacheOpenError, AudioOpenError, "audio/open/cache")
/// There are no audio players available
DEFINE_SIMPLE_EXCEPTION(NoAudioPlayersError, AudioOpenError, "audio/open/no_players")
/// The audio player failed to initialize
DEFINE_SIMPLE_EXCEPTION(AudioPlayerOpenError, AudioOpenError, "audio/open/player")
} }

View file

@ -55,6 +55,8 @@
#ifdef WITH_LIBPULSE #ifdef WITH_LIBPULSE
#include "audio_player_pulse.h" #include "audio_player_pulse.h"
#endif #endif
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
@ -70,38 +72,20 @@ AudioPlayer::~AudioPlayer() {
CloseStream(); CloseStream();
} }
/// @brief Ask to stop later
void AudioPlayer::RequestStop() {
wxCommandEvent event(wxEVT_STOP_AUDIO, 1000);
event.SetEventObject(this);
AddPendingEvent(event); // thread safe
}
DEFINE_EVENT_TYPE(wxEVT_STOP_AUDIO)
BEGIN_EVENT_TABLE(AudioPlayer, wxEvtHandler)
EVT_COMMAND (1000, wxEVT_STOP_AUDIO, AudioPlayer::OnStopAudio)
END_EVENT_TABLE()
void AudioPlayer::OnStopAudio(wxCommandEvent &) {
Stop(false);
}
AudioPlayer* AudioPlayerFactory::GetAudioPlayer() { AudioPlayer* AudioPlayerFactory::GetAudioPlayer() {
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Player")->GetString()); std::vector<std::string> list = GetClasses(OPT_GET("Audio/Player")->GetString());
if (list.empty()) throw "No audio players are available."; if (list.empty()) throw agi::NoAudioPlayersError("No audio players are available.", 0);
wxString error; std::string error;
for (unsigned int i=0;i<list.size();i++) { for (size_t i = 0; i < list.size(); ++i) {
try { try {
AudioPlayer *player = Create(list[i]); return Create(list[i]);
if (player) return player; }
catch (agi::AudioPlayerOpenError const& err) {
error += list[i] + " factory: " + err.GetChainedMessage() + "\n";
} }
catch (wxString err) { error += list[i] + " factory: " + err + "\n"; }
catch (const wxChar *err) { error += list[i] + " factory: " + wxString(err) + "\n"; }
catch (...) { error += list[i] + " factory: Unknown error\n"; }
} }
throw error; throw agi::AudioPlayerOpenError(error, 0);
} }
void AudioPlayerFactory::RegisterProviders() { void AudioPlayerFactory::RegisterProviders() {

View file

@ -41,6 +41,8 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_player_alsa.h" #include "audio_player_alsa.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include "compat.h" #include "compat.h"
#include "frame_main.h" #include "frame_main.h"
@ -365,7 +367,7 @@ void AlsaPlayer::OpenStream()
} }
else else
{ {
throw 1; // FIXME throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0);
} }
} }

View file

@ -71,4 +71,3 @@ public:
}; };
#endif #endif

View file

@ -40,6 +40,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_dsound.h" #include "audio_player_dsound.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
@ -75,7 +76,7 @@ void DirectSoundPlayer::OpenStream() {
// Initialize the DirectSound object // Initialize the DirectSound object
HRESULT res; HRESULT res;
res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device
if (FAILED(res)) throw "Failed initializing DirectSound"; if (FAILED(res)) throw agi::AudioPlayerOpenError("Failed initializing DirectSound", 0);
// Set DirectSound parameters // Set DirectSound parameters
directSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(),DSSCL_PRIORITY); directSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(),DSSCL_PRIORITY);
@ -106,11 +107,11 @@ void DirectSoundPlayer::OpenStream() {
// Create the buffer // Create the buffer
IDirectSoundBuffer *buf; IDirectSoundBuffer *buf;
res = directSound->CreateSoundBuffer(&desc,&buf,NULL); res = directSound->CreateSoundBuffer(&desc,&buf,NULL);
if (res != DS_OK) throw "Failed creating DirectSound buffer"; if (res != DS_OK) throw agi::AudioPlayerOpenError("Failed creating DirectSound buffer", 0);
// Copy interface to buffer // Copy interface to buffer
res = buf->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*) &buffer); res = buf->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*) &buffer);
if (res != S_OK) throw "Failed casting interface to IDirectSoundBuffer8"; if (res != S_OK) throw agi::AudioPlayerOpenError("Failed casting interface to IDirectSoundBuffer8", 0);
// Set data // Set data
offset = 0; offset = 0;

View file

@ -48,6 +48,7 @@
#include "audio_player_dsound2.h" #include "audio_player_dsound2.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
@ -669,7 +670,7 @@ DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int
thread_handle.handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0); thread_handle.handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);
if (!thread_handle) if (!thread_handle)
throw "Failed creating playback thread in DirectSoundPlayer2. This is bad."; throw agi::AudioPlayerOpenError("Failed creating playback thread in DirectSoundPlayer2. This is bad.", 0);
HANDLE running_or_error[] = { thread_running, error_happened }; HANDLE running_or_error[] = { thread_running, error_happened };
switch (WaitForMultipleObjects(2, running_or_error, FALSE, INFINITE)) switch (WaitForMultipleObjects(2, running_or_error, FALSE, INFINITE))
@ -680,10 +681,10 @@ DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int
case WAIT_OBJECT_0 + 1: case WAIT_OBJECT_0 + 1:
// error happened, we fail // error happened, we fail
throw error_message; throw agi::AudioPlayerOpenError(error_message, 0);
default: default:
throw "Failed wait for thread start or thread error in DirectSoundPlayer2. This is bad."; throw agi::AudioPlayerOpenError("Failed wait for thread start or thread error in DirectSoundPlayer2. This is bad.", 0);
} }
} }
@ -714,7 +715,7 @@ void DirectSoundPlayer2Thread::Play(int64_t start, int64_t count)
case WAIT_OBJECT_0+1: // Error case WAIT_OBJECT_0+1: // Error
throw error_message; throw error_message;
default: default:
throw "Unexpected result from WaitForMultipleObjects in DirectSoundPlayer2Thread::Play"; throw agi::InternalError("Unexpected result from WaitForMultipleObjects in DirectSoundPlayer2Thread::Play", 0);
} }
} }

View file

@ -42,6 +42,7 @@
#include "audio_player_openal.h" #include "audio_player_openal.h"
#include "audio_controller.h"
#include "utils.h" #include "utils.h"
// Auto-link to OpenAL lib for MSVC // Auto-link to OpenAL lib for MSVC
@ -49,6 +50,8 @@
#pragma comment(lib, "openal32.lib") #pragma comment(lib, "openal32.lib")
#endif #endif
DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal")
OpenALPlayer::OpenALPlayer() OpenALPlayer::OpenALPlayer()
: open(false) : open(false)
, playing(false) , playing(false)
@ -76,25 +79,25 @@ void OpenALPlayer::OpenStream()
try { try {
// Open device // Open device
device = alcOpenDevice(0); device = alcOpenDevice(0);
if (!device) throw OpenALException("Failed opening default OpenAL device"); if (!device) throw OpenALException("Failed opening default OpenAL device", 0);
// Create context // Create context
context = alcCreateContext(device, 0); context = alcCreateContext(device, 0);
if (!context) throw OpenALException("Failed creating OpenAL context"); if (!context) throw OpenALException("Failed creating OpenAL context", 0);
if (!alcMakeContextCurrent(context)) throw OpenALException("Failed selecting OpenAL context"); if (!alcMakeContextCurrent(context)) throw OpenALException("Failed selecting OpenAL context", 0);
// 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) throw OpenALException("Error generating OpenAL buffers"); if (alGetError() != AL_NO_ERROR) throw OpenALException("Error generating OpenAL buffers", 0);
// 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"); throw OpenALException("Error generating OpenAL source", 0);
} }
} }
catch (...) catch (...)

View file

@ -53,10 +53,6 @@
#include <vector> #include <vector>
#endif #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

View file

@ -43,11 +43,16 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_player_oss.h" #include "audio_player_oss.h"
#include "frame_main.h"
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "include/aegisub/audio_provider.h"
#include "main.h" #include "main.h"
#include "utils.h" #include "utils.h"
DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss")
/// @brief Constructor /// @brief Constructor
/// ///
OSSPlayer::OSSPlayer() OSSPlayer::OSSPlayer()
@ -81,7 +86,7 @@ void OSSPlayer::OpenStream()
wxString device = lagi_wxString(OPT_GET("Audio/OSS/Device")->GetString()); wxString device = lagi_wxString(OPT_GET("Audio/OSS/Device")->GetString());
dspdev = ::open(device.mb_str(wxConvUTF8), O_WRONLY, 0); dspdev = ::open(device.mb_str(wxConvUTF8), O_WRONLY, 0);
if (dspdev < 0) { if (dspdev < 0) {
throw OSSError("OSS player: opening device failed"); throw OSSError("OSS player: opening device failed", 0);
} }
// Use a reasonable buffer policy for low latency (OSS4) // Use a reasonable buffer policy for low latency (OSS4)
@ -93,7 +98,7 @@ void OSSPlayer::OpenStream()
// Set number of channels // Set number of channels
int channels = provider->GetChannels(); int channels = provider->GetChannels();
if (ioctl(dspdev, SNDCTL_DSP_CHANNELS, &channels) < 0) { if (ioctl(dspdev, SNDCTL_DSP_CHANNELS, &channels) < 0) {
throw OSSError("OSS player: setting channels failed"); throw OSSError("OSS player: setting channels failed", 0);
} }
// Set sample format // Set sample format
@ -106,17 +111,17 @@ void OSSPlayer::OpenStream()
sample_format = AFMT_S16_LE; sample_format = AFMT_S16_LE;
break; break;
default: default:
throw OSSError("OSS player: can only handle 8 and 16 bit sound"); throw OSSError("OSS player: can only handle 8 and 16 bit sound", 0);
} }
if (ioctl(dspdev, SNDCTL_DSP_SETFMT, &sample_format) < 0) { if (ioctl(dspdev, SNDCTL_DSP_SETFMT, &sample_format) < 0) {
throw OSSError("OSS player: setting sample format failed"); throw OSSError("OSS player: setting sample format failed", 0);
} }
// Set sample rate // Set sample rate
rate = provider->GetSampleRate(); rate = provider->GetSampleRate();
if (ioctl(dspdev, SNDCTL_DSP_SPEED, &rate) < 0) { if (ioctl(dspdev, SNDCTL_DSP_SPEED, &rate) < 0) {
throw OSSError("OSS player: setting samplerate failed"); throw OSSError("OSS player: setting samplerate failed", 0);
} }
// Now ready // Now ready

View file

@ -49,12 +49,8 @@
#endif #endif
#include "include/aegisub/audio_player.h" #include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include <libaegisub/exception.h>
DEFINE_SIMPLE_EXCEPTION_NOINNER(OSSError, agi::Exception, "audio/player/oss")
class AudioProvider;
class OSSPlayer; class OSSPlayer;
/// DOCME /// DOCME

View file

@ -42,11 +42,15 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "audio_player_portaudio.h" #include "audio_player_portaudio.h"
#include "charset_conv.h"
#include "audio_controller.h"
#include "compat.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include "main.h" #include "main.h"
#include "utils.h" #include "utils.h"
DEFINE_SIMPLE_EXCEPTION(PortAudioError, agi::AudioPlayerOpenError, "audio/player/open/portaudio")
// Uncomment to enable extremely spammy debug logging // Uncomment to enable extremely spammy debug logging
//#define PORTAUDIO_DEBUG //#define PORTAUDIO_DEBUG
@ -54,7 +58,7 @@ PortAudioPlayer::PortAudioPlayer() {
PaError err = Pa_Initialize(); PaError err = Pa_Initialize();
if (err != paNoError) if (err != paNoError)
throw PortAudioError(std::string("Failed opening PortAudio:") + Pa_GetErrorText(err)); throw PortAudioError(std::string("Failed opening PortAudio:") + Pa_GetErrorText(err), 0);
volume = 1.0f; volume = 1.0f;
pa_start = 0.0; pa_start = 0.0;
@ -109,7 +113,7 @@ void PortAudioPlayer::OpenStream() {
const PaHostErrorInfo *pa_err = Pa_GetLastHostErrorInfo(); const PaHostErrorInfo *pa_err = Pa_GetLastHostErrorInfo();
LOG_D_IF(pa_err->errorCode != 0, "audio/player/portaudio") << "HostError: API: " << pa_err->hostApiType << ", " << pa_err->errorText << ", " << pa_err->errorCode; LOG_D_IF(pa_err->errorCode != 0, "audio/player/portaudio") << "HostError: API: " << pa_err->hostApiType << ", " << pa_err->errorText << ", " << pa_err->errorCode;
LOG_D("audio/player/portaudio") << "Failed initializing PortAudio stream with error: " << Pa_GetErrorText(err); LOG_D("audio/player/portaudio") << "Failed initializing PortAudio stream with error: " << Pa_GetErrorText(err);
throw PortAudioError("Failed initializing PortAudio stream with error: " + std::string(Pa_GetErrorText(err))); throw PortAudioError("Failed initializing PortAudio stream with error: " + std::string(Pa_GetErrorText(err)), 0);
} }
} }

View file

@ -37,15 +37,15 @@
#ifdef WITH_PORTAUDIO #ifdef WITH_PORTAUDIO
#include "include/aegisub/audio_player.h" #include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include <libaegisub/exception.h>
extern "C" { extern "C" {
#include <portaudio.h> #include <portaudio.h>
} }
DEFINE_SIMPLE_EXCEPTION_NOINNER(PortAudioError, agi::Exception, "audio/player/portaudio") #ifndef AGI_PRE
#include <map>
#include <string>
#endif
/// @class PortAudioPlayer /// @class PortAudioPlayer
/// @brief PortAudio Player /// @brief PortAudio Player

View file

@ -43,11 +43,11 @@
#endif #endif
#include "audio_player_pulse.h" #include "audio_player_pulse.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include "utils.h" #include "utils.h"
/// @brief Constructor
///
PulseAudioPlayer::PulseAudioPlayer() PulseAudioPlayer::PulseAudioPlayer()
: context_notify(0, 1) : context_notify(0, 1)
, context_success(0, 1) , context_success(0, 1)
@ -60,32 +60,20 @@ PulseAudioPlayer::PulseAudioPlayer()
is_playing = false; is_playing = false;
} }
/// @brief Destructor
///
PulseAudioPlayer::~PulseAudioPlayer() PulseAudioPlayer::~PulseAudioPlayer()
{ {
if (open) CloseStream(); CloseStream();
} }
/// @brief Open stream
///
void PulseAudioPlayer::OpenStream() void PulseAudioPlayer::OpenStream()
{ {
//printf("Opening PulseAudio stream\n");
if (open) CloseStream(); if (open) CloseStream();
// Get provider
AudioProvider *provider = GetProvider();
// Initialise a mainloop // Initialise a mainloop
//printf("Initialising threaded main loop\n"); //printf("Initialising threaded main loop\n");
mainloop = pa_threaded_mainloop_new(); mainloop = pa_threaded_mainloop_new();
if (!mainloop) { if (!mainloop) {
throw "Failed to initialise PulseAudio threaded mainloop object"; throw agi::AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object", 0);
} }
//printf("Starting main loop\n"); //printf("Starting main loop\n");
pa_threaded_mainloop_start(mainloop); pa_threaded_mainloop_start(mainloop);
@ -95,7 +83,7 @@ void PulseAudioPlayer::OpenStream()
context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub"); context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub");
if (!context) { if (!context) {
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
throw "Failed to create PulseAudio context"; throw agi::AudioPlayerOpenError("Failed to create PulseAudio context", 0);
} }
pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this); pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this);
@ -113,9 +101,7 @@ void PulseAudioPlayer::OpenStream()
pa_context_unref(context); pa_context_unref(context);
pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_stop(mainloop);
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
wxString s(pa_strerror(paerror), wxConvUTF8); throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
s.Prepend("PulseAudio reported error: ");
throw s.c_str();
} }
// otherwise loop once more // otherwise loop once more
} }
@ -137,7 +123,7 @@ void PulseAudioPlayer::OpenStream()
pa_context_unref(context); pa_context_unref(context);
pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_stop(mainloop);
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
throw "PulseAudio could not create stream"; throw agi::AudioPlayerOpenError("PulseAudio could not create stream", 0);
} }
pa_stream_set_state_callback(stream, (pa_stream_notify_cb_t)pa_stream_notify, this); pa_stream_set_state_callback(stream, (pa_stream_notify_cb_t)pa_stream_notify, this);
pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this); pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this);
@ -147,9 +133,7 @@ void PulseAudioPlayer::OpenStream()
paerror = pa_stream_connect_playback(stream, NULL, NULL, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); paerror = pa_stream_connect_playback(stream, NULL, NULL, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL);
if (paerror) { if (paerror) {
printf("PulseAudio reported error: %s (%d)\n", pa_strerror(paerror), paerror); printf("PulseAudio reported error: %s (%d)\n", pa_strerror(paerror), paerror);
wxString s(pa_strerror(paerror), wxConvUTF8); throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
s.Prepend("PulseAudio reported error: ");
throw s.c_str();
} }
while (true) { while (true) {
stream_notify.Wait(); stream_notify.Wait();
@ -158,7 +142,7 @@ void PulseAudioPlayer::OpenStream()
} else if (sstate == PA_STREAM_FAILED) { } else if (sstate == PA_STREAM_FAILED) {
paerror = pa_context_errno(context); paerror = pa_context_errno(context);
printf("PulseAudio player: Stream connection failed: %s (%d)\n", pa_strerror(paerror), paerror); printf("PulseAudio player: Stream connection failed: %s (%d)\n", pa_strerror(paerror), paerror);
throw "PulseAudio player: Something went wrong connecting the stream"; throw agi::AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream", 0);
} }
} }
//printf("Connected playback stream, now playing\n\n"); //printf("Connected playback stream, now playing\n\n");
@ -168,11 +152,6 @@ void PulseAudioPlayer::OpenStream()
open = true; open = true;
} }
/// @brief Close stream
/// @return
///
void PulseAudioPlayer::CloseStream() void PulseAudioPlayer::CloseStream()
{ {
if (!open) return; if (!open) return;
@ -192,12 +171,6 @@ void PulseAudioPlayer::CloseStream()
open = false; open = false;
} }
/// @brief Play
/// @param start
/// @param count
///
void PulseAudioPlayer::Play(int64_t start,int64_t count) void PulseAudioPlayer::Play(int64_t start,int64_t count)
{ {
//printf("Starting PulseAudio playback\n"); //printf("Starting PulseAudio playback\n");
@ -249,12 +222,6 @@ void PulseAudioPlayer::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 PulseAudioPlayer::Stop(bool timerToo) void PulseAudioPlayer::Stop(bool timerToo)
{ {
if (!is_playing) return; if (!is_playing) return;
@ -286,61 +253,31 @@ void PulseAudioPlayer::Stop(bool timerToo)
} }
} }
/// @brief DOCME
/// @return
///
bool PulseAudioPlayer::IsPlaying() bool PulseAudioPlayer::IsPlaying()
{ {
return is_playing; return is_playing;
} }
/// @brief Set end
/// @param pos
///
void PulseAudioPlayer::SetEndPosition(int64_t pos) void PulseAudioPlayer::SetEndPosition(int64_t pos)
{ {
end_frame = pos; end_frame = pos;
} }
/// @brief Set current position
/// @param pos
///
void PulseAudioPlayer::SetCurrentPosition(int64_t pos) void PulseAudioPlayer::SetCurrentPosition(int64_t pos)
{ {
cur_frame = pos; cur_frame = pos;
} }
/// @brief DOCME
/// @return
///
int64_t PulseAudioPlayer::GetStartPosition() int64_t PulseAudioPlayer::GetStartPosition()
{ {
return start_frame; return start_frame;
} }
/// @brief DOCME
/// @return
///
int64_t PulseAudioPlayer::GetEndPosition() int64_t PulseAudioPlayer::GetEndPosition()
{ {
return end_frame; return end_frame;
} }
/// @brief Get current position
/// @return
///
int64_t PulseAudioPlayer::GetCurrentPosition() int64_t PulseAudioPlayer::GetCurrentPosition()
{ {
if (!is_playing) return 0; if (!is_playing) return 0;
@ -356,52 +293,28 @@ int64_t PulseAudioPlayer::GetCurrentPosition()
return start_frame + playtime * provider->GetSampleRate() / (1000*1000); return start_frame + playtime * provider->GetSampleRate() / (1000*1000);
} }
/// @brief Called by PA to notify about contetxt operation completion /// @brief Called by PA to notify about contetxt operation completion
/// @param c
/// @param success
/// @param thread
///
void PulseAudioPlayer::pa_context_success(pa_context *c, int success, PulseAudioPlayer *thread) void PulseAudioPlayer::pa_context_success(pa_context *c, int success, PulseAudioPlayer *thread)
{ {
thread->context_success_val = success; thread->context_success_val = success;
thread->context_success.Post(); thread->context_success.Post();
} }
/// @brief Called by PA to notify about other context-related stuff /// @brief Called by PA to notify about other context-related stuff
/// @param c
/// @param thread
///
void PulseAudioPlayer::pa_context_notify(pa_context *c, PulseAudioPlayer *thread) void PulseAudioPlayer::pa_context_notify(pa_context *c, PulseAudioPlayer *thread)
{ {
thread->cstate = pa_context_get_state(thread->context); thread->cstate = pa_context_get_state(thread->context);
thread->context_notify.Post(); thread->context_notify.Post();
} }
/// @brief Called by PA when an operation completes /// @brief Called by PA when an operation completes
/// @param p
/// @param success
/// @param thread
///
void PulseAudioPlayer::pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread) void PulseAudioPlayer::pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread)
{ {
thread->stream_success_val = success; thread->stream_success_val = success;
thread->stream_success.Post(); thread->stream_success.Post();
} }
/// @brief Called by PA to request more data (and other things?) /// @brief Called by PA to request more data (and other things?)
/// @param p
/// @param length
/// @param thread
/// @return
///
void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread) void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread)
{ {
if (!thread->is_playing) return; if (!thread->is_playing) return;
@ -434,12 +347,7 @@ void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPl
thread->cur_frame += frames; thread->cur_frame += frames;
} }
/// @brief Called by PA to notify about other stuff /// @brief Called by PA to notify about other stuff
/// @param p
/// @param thread
///
void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread) void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread)
{ {
thread->sstate = pa_stream_get_state(thread->stream); thread->sstate = pa_stream_get_state(thread->stream);

View file

@ -38,7 +38,6 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include "include/aegisub/audio_player.h" #include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
class PulseAudioPlayer; class PulseAudioPlayer;
@ -141,16 +140,7 @@ public:
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; }
}; };

View file

@ -41,6 +41,7 @@
#include <wx/thread.h> #include <wx/thread.h>
#endif #endif
#include "audio_controller.h"
#ifdef WITH_AVISYNTH #ifdef WITH_AVISYNTH
#include "audio_provider_avs.h" #include "audio_provider_avs.h"
#endif #endif
@ -56,13 +57,6 @@
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
/// @brief Get audio with volume
/// @param buf
/// @param start
/// @param count
/// @param volume
/// @return
///
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const { void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
try { try {
GetAudio(buf,start,count); GetAudio(buf,start,count);
@ -92,8 +86,9 @@ void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count,
} }
AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int cache) { AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int cache) {
AudioProvider *provider = NULL; AudioProvider *provider = 0;
bool found = false; bool found_file = false;
bool found_audio = false;
std::string msg; std::string msg;
if (!OPT_GET("Provider/Audio/PCM/Disable")->GetBool()) { if (!OPT_GET("Provider/Audio/PCM/Disable")->GetBool()) {
@ -104,16 +99,16 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int c
catch (agi::FileNotFoundError const& err) { catch (agi::FileNotFoundError const& err) {
msg = "PCM audio provider: " + err.GetMessage() + " not found.\n"; msg = "PCM audio provider: " + err.GetMessage() + " not found.\n";
} }
catch (AudioOpenError const& err) { catch (agi::AudioOpenError const& err) {
found = true; found_file = true;
msg += err.GetMessage() + "\n"; msg += err.GetChainedMessage() + "\n";
} }
} }
if (!provider) { if (!provider) {
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Provider")->GetString()); std::vector<std::string> list = GetClasses(OPT_GET("Audio/Provider")->GetString());
if (list.empty()) throw AudioOpenError("No audio providers are available."); if (list.empty()) throw agi::NoAudioProvidersError("No audio providers are available.", 0);
for (unsigned int i=0;i<list.size();i++) { for (size_t i = 0; i < list.size() ; ++i) {
try { try {
provider = Create(list[i], filename); provider = Create(list[i], filename);
if (provider) break; if (provider) break;
@ -121,20 +116,25 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int c
catch (agi::FileNotFoundError const& err) { catch (agi::FileNotFoundError const& err) {
msg += list[i] + ": " + err.GetMessage() + " not found.\n"; msg += list[i] + ": " + err.GetMessage() + " not found.\n";
} }
catch (AudioOpenError const& err) { catch (agi::AudioDataNotFoundError const& err) {
found = true; found_file = true;
msg += list[i] + ": " + err.GetMessage() + "\n"; msg += list[i] + ": " + err.GetChainedMessage() + "\n";
}
catch (agi::AudioOpenError const& err) {
found_audio = true;
found_file = true;
msg += list[i] + ": " + err.GetChainedMessage() + "\n";
} }
} }
} }
if (!provider) { if (!provider) {
if (found) { if (found_audio)
throw AudioOpenError(msg); throw agi::AudioProviderOpenError(msg, 0);
} if (found_file)
else { throw agi::AudioDataNotFoundError(msg, 0);
throw agi::FileNotFoundError(STD_STR(filename)); throw agi::FileNotFoundError(STD_STR(filename));
}
} }
bool needsCache = provider->NeedsCache(); bool needsCache = provider->NeedsCache();
// Give it a converter if needed // Give it a converter if needed
@ -155,7 +155,7 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int c
// Convert to HD // Convert to HD
if (cache == 2) return new HDAudioProvider(provider, &progress); if (cache == 2) return new HDAudioProvider(provider, &progress);
throw AudioOpenError("Unknown caching method"); throw agi::AudioCacheOpenError("Unknown caching method", 0);
} }
/// @brief Register all providers /// @brief Register all providers

View file

@ -46,6 +46,8 @@
#endif #endif
#include "audio_provider_avs.h" #include "audio_provider_avs.h"
#include "audio_controller.h"
#include "charset_conv.h" #include "charset_conv.h"
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
@ -55,45 +57,53 @@
/// @brief Constructor /// @brief Constructor
/// @param _filename /// @param _filename
/// ///
AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) try : filename(filename) { AvisynthAudioProvider::AvisynthAudioProvider(wxString filename)
AVSValue script; : filename(filename)
wxMutexLocker lock(AviSynthMutex); {
try {
AVSValue script;
wxMutexLocker lock(AviSynthMutex);
wxFileName fn(filename); wxFileName fn(filename);
if (!fn.FileExists()) if (!fn.FileExists())
throw agi::FileNotFoundError(STD_STR(filename)); throw agi::FileNotFoundError(STD_STR(filename));
// Include // Include
if (filename.EndsWith(".avs")) { if (filename.EndsWith(".avs")) {
char *fname = env->SaveString(fn.GetShortPath().mb_str(csConvLocal)); char *fname = env->SaveString(fn.GetShortPath().mb_str(csConvLocal));
script = env->Invoke("Import", fname); script = env->Invoke("Import", fname);
}
// Use DirectShowSource
else {
const char * argnames[3] = { 0, "video", "audio" };
AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true };
// Load DirectShowSource.dll from app dir if it exists
wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
if (dsspath.FileExists()) {
env->Invoke("LoadPlugin",env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal)));
} }
// Load audio with DSS if it exists // Use DirectShowSource
if (env->FunctionExists("DirectShowSource")) {
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
}
// Otherwise fail
else { else {
throw AudioOpenError("No suitable audio source filter found. Try placing DirectShowSource.dll in the Aegisub application directory."); const char * argnames[3] = { 0, "video", "audio" };
} AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true };
}
LoadFromClip(script); // Load DirectShowSource.dll from app dir if it exists
} wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
catch (AvisynthError &err) { if (dsspath.FileExists()) {
throw AudioOpenError("Avisynth error: " + std::string(err.msg)); env->Invoke("LoadPlugin",env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal)));
}
// Load audio with DSS if it exists
if (env->FunctionExists("DirectShowSource")) {
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
}
// Otherwise fail
else {
throw agi::AudioProviderOpenError("No suitable audio source filter found. Try placing DirectShowSource.dll in the Aegisub application directory.", 0);
}
}
LoadFromClip(script);
}
catch (AvisynthError &err) {
std::string errmsg(err.msg);
if (errmsg.find("filter graph manager won't talk to me") != errmsg.npos)
throw agi::AudioDataNotFoundError("Avisynth error: " + errmsg, 0);
else
throw agi::AudioProviderOpenError("Avisynth error: " + errmsg, 0);
}
} }
/// @brief Read from environment /// @brief Read from environment
@ -104,7 +114,7 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
// Check if it has audio // Check if it has audio
VideoInfo vi = _clip.AsClip()->GetVideoInfo(); VideoInfo vi = _clip.AsClip()->GetVideoInfo();
if (!vi.HasAudio()) throw AudioOpenError("No audio found."); if (!vi.HasAudio()) throw agi::AudioDataNotFoundError("No audio found.", 0);
// Convert to one channel // Convert to one channel
char buffer[1024]; char buffer[1024];

View file

@ -26,6 +26,7 @@
#include "audio_provider_convert.h" #include "audio_provider_convert.h"
#include "aegisub_endian.h" #include "aegisub_endian.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include <libaegisub/scoped_ptr.h> #include <libaegisub/scoped_ptr.h>
@ -54,7 +55,7 @@ class BitdepthConvertAudioProvider : public AudioProviderConverter {
public: public:
BitdepthConvertAudioProvider(AudioProvider *src) : AudioProviderConverter(src) { BitdepthConvertAudioProvider(AudioProvider *src) : AudioProviderConverter(src) {
if (bytes_per_sample > 8) if (bytes_per_sample > 8)
throw AudioOpenError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported"); throw agi::AudioProviderOpenError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported", 0);
src_is_native_endian = src->AreSamplesNativeEndian(); src_is_native_endian = src->AreSamplesNativeEndian();
src_bytes_per_sample = bytes_per_sample; src_bytes_per_sample = bytes_per_sample;

View file

@ -47,10 +47,11 @@
#endif #endif
#include "audio_provider_ffmpegsource.h" #include "audio_provider_ffmpegsource.h"
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
/// @brief Constructor /// @brief Constructor
/// @param filename /// @param filename
/// ///
@ -64,7 +65,7 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename)
if (SUCCEEDED(res)) if (SUCCEEDED(res))
COMInited = true; COMInited = true;
else if (res != RPC_E_CHANGED_MODE) else if (res != RPC_E_CHANGED_MODE)
throw AudioOpenError("COM initialization failure"); throw agi::AudioProviderOpenError("COM initialization failure", 0);
#endif #endif
// initialize ffmpegsource // initialize ffmpegsource
// FIXME: CPU detection? // FIXME: CPU detection?
@ -101,7 +102,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO); std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
if (TrackList.size() <= 0) if (TrackList.size() <= 0)
throw AudioOpenError("no audio tracks found"); throw agi::AudioDataNotFoundError("no audio tracks found", 0);
// initialize the track number to an invalid value so we can detect later on // initialize the track number to an invalid value so we can detect later on
// whether the user actually had to choose a track or not // whether the user actually had to choose a track or not
@ -110,7 +111,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
TrackNumber = AskForTrackSelection(TrackList, FFMS_TYPE_AUDIO); TrackNumber = AskForTrackSelection(TrackList, FFMS_TYPE_AUDIO);
// if it's still -1 here, user pressed cancel // if it's still -1 here, user pressed cancel
if (TrackNumber == -1) if (TrackNumber == -1)
throw agi::UserCancelException("audio loading cancelled by user"); throw agi::UserCancelException("audio loading canceled by user");
} }
// generate a name for the cache file // generate a name for the cache file
@ -137,7 +138,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
if (TrackNumber < 0) { if (TrackNumber < 0) {
FFMS_DestroyIndex(Index); FFMS_DestroyIndex(Index);
Index = NULL; Index = NULL;
throw AudioOpenError(std::string("Couldn't find any audio tracks: ") + ErrInfo.Buffer); throw agi::AudioDataNotFoundError(std::string("Couldn't find any audio tracks: ") + ErrInfo.Buffer, 0);
} }
// index is valid and track number is now set, // index is valid and track number is now set,
@ -166,7 +167,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
Index = DoIndexing(Indexer, CacheName, TrackMask, GetErrorHandlingMode()); Index = DoIndexing(Indexer, CacheName, TrackMask, GetErrorHandlingMode());
} }
catch (wxString const& err) { catch (wxString const& err) {
throw AudioOpenError(STD_STR(err)); throw agi::AudioProviderOpenError(STD_STR(err), 0);
} }
// if tracknumber still isn't set we need to set it now // if tracknumber still isn't set we need to set it now
@ -187,7 +188,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
FFMS_DestroyIndex(Index); FFMS_DestroyIndex(Index);
Index = NULL; Index = NULL;
if (!AudioSource) { if (!AudioSource) {
throw AudioOpenError(std::string("Failed to open audio track: %s") + ErrInfo.Buffer); throw agi::AudioProviderOpenError(std::string("Failed to open audio track: %s") + ErrInfo.Buffer, 0);
} }
const FFMS_AudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource); const FFMS_AudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource);
@ -196,7 +197,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
sample_rate = AudioInfo.SampleRate; sample_rate = AudioInfo.SampleRate;
num_samples = AudioInfo.NumSamples; num_samples = AudioInfo.NumSamples;
if (channels <= 0 || sample_rate <= 0 || num_samples <= 0) if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
throw AudioOpenError("sanity check failed, consult your local psychiatrist"); throw agi::AudioProviderOpenError("sanity check failed, consult your local psychiatrist", 0);
// FIXME: use the actual sample format too? // FIXME: use the actual sample format too?
// why not just bits_per_sample/8? maybe there's some oddball format with half bytes out there somewhere... // why not just bits_per_sample/8? maybe there's some oddball format with half bytes out there somewhere...
@ -206,7 +207,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
case 24: bytes_per_sample = 3; break; case 24: bytes_per_sample = 3; break;
case 32: bytes_per_sample = 4; break; case 32: bytes_per_sample = 4; break;
default: default:
throw AudioOpenError("unknown or unsupported sample format"); throw agi::AudioProviderOpenError("unknown or unsupported sample format", 0);
} }
} }

View file

@ -46,6 +46,7 @@
#include "audio_provider_hd.h" #include "audio_provider_hd.h"
#include "audio_controller.h"
#include "audio_provider_pcm.h" #include "audio_provider_pcm.h"
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
@ -103,7 +104,7 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
wxDiskspaceSize_t freespace; wxDiskspaceSize_t freespace;
if (wxGetDiskSpace(cache_dir(), 0, &freespace)) { if (wxGetDiskSpace(cache_dir(), 0, &freespace)) {
if (num_samples * channels * bytes_per_sample > freespace) if (num_samples * channels * bytes_per_sample > freespace)
throw AudioOpenError("Not enough free disk space in " + STD_STR(cache_dir()) + " to cache the audio"); throw agi::AudioCacheOpenError("Not enough free disk space in " + STD_STR(cache_dir()) + " to cache the audio", 0);
} }
bytes_per_sample = source->GetBytesPerSample(); bytes_per_sample = source->GetBytesPerSample();

View file

@ -54,6 +54,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include "aegisub_endian.h" #include "aegisub_endian.h"
#include "audio_controller.h"
#include "audio_provider_pcm.h" #include "audio_provider_pcm.h"
#include "compat.h" #include "compat.h"
#include "utils.h" #include "utils.h"
@ -81,7 +82,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
LARGE_INTEGER li_file_size = {0}; LARGE_INTEGER li_file_size = {0};
if (!GetFileSizeEx(file_handle, &li_file_size)) { if (!GetFileSizeEx(file_handle, &li_file_size)) {
CloseHandle(file_handle); CloseHandle(file_handle);
throw AudioOpenError("Failed getting file size"); throw agi::AudioProviderOpenError("Failed getting file size", 0);
} }
file_size = li_file_size.QuadPart; file_size = li_file_size.QuadPart;
@ -94,7 +95,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
if (file_mapping == 0) { if (file_mapping == 0) {
CloseHandle(file_handle); CloseHandle(file_handle);
throw AudioOpenError("Failed creating file mapping"); throw agi::AudioProviderOpenError("Failed creating file mapping", 0);
} }
current_mapping = 0; current_mapping = 0;
@ -111,7 +112,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
memset(&filestats, 0, sizeof(filestats)); memset(&filestats, 0, sizeof(filestats));
if (fstat(file_handle, &filestats)) { if (fstat(file_handle, &filestats)) {
close(file_handle); close(file_handle);
throw AudioOpenError("Could not stat file to get size"); throw agi::AudioProviderOpenError("Could not stat file to get size", 0);
} }
file_size = filestats.st_size; file_size = filestats.st_size;
@ -331,9 +332,9 @@ public:
// Check magic values // Check magic values
if (!CheckFourcc(header.ch.type, "RIFF")) if (!CheckFourcc(header.ch.type, "RIFF"))
throw AudioOpenError("File is not a RIFF file"); throw agi::AudioDataNotFoundError("File is not a RIFF file", 0);
if (!CheckFourcc(header.format, "WAVE")) if (!CheckFourcc(header.format, "WAVE"))
throw AudioOpenError("File is not a RIFF WAV file"); throw agi::AudioDataNotFoundError("File is not a RIFF WAV file", 0);
// Count how much more data we can have in the entire file // Count how much more data we can have in the entire file
// The first 4 bytes are already eaten by the header.format field // The first 4 bytes are already eaten by the header.format field
@ -356,13 +357,13 @@ public:
filepos += sizeof(ch); filepos += sizeof(ch);
if (CheckFourcc(ch.type, "fmt ")) { if (CheckFourcc(ch.type, "fmt ")) {
if (got_fmt_header) throw AudioOpenError("Invalid file, multiple 'fmt ' chunks"); if (got_fmt_header) throw agi::AudioProviderOpenError("Invalid file, multiple 'fmt ' chunks", 0);
got_fmt_header = true; got_fmt_header = true;
fmtChunk &fmt = *(fmtChunk*)EnsureRangeAccessible(filepos, sizeof(fmtChunk)); fmtChunk &fmt = *(fmtChunk*)EnsureRangeAccessible(filepos, sizeof(fmtChunk));
if (Endian::LittleToMachine(fmt.compression) != 1) if (Endian::LittleToMachine(fmt.compression) != 1)
throw AudioOpenError("Can't use file, not PCM encoding"); throw agi::AudioProviderOpenError("Can't use file, not PCM encoding", 0);
// Set stuff inherited from the AudioProvider class // Set stuff inherited from the AudioProvider class
sample_rate = Endian::LittleToMachine(fmt.samplerate); sample_rate = Endian::LittleToMachine(fmt.samplerate);
@ -374,7 +375,7 @@ public:
// This won't pick up 'data' chunks inside 'wavl' chunks // This won't pick up 'data' chunks inside 'wavl' chunks
// since the 'wavl' chunks wrap those. // since the 'wavl' chunks wrap those.
if (!got_fmt_header) throw AudioOpenError("Found 'data' chunk before 'fmt ' chunk, file is invalid."); if (!got_fmt_header) throw agi::AudioProviderOpenError("Found 'data' chunk before 'fmt ' chunk, file is invalid.", 0);
int64_t samples = Endian::LittleToMachine(ch.size) / bytes_per_sample; int64_t samples = Endian::LittleToMachine(ch.size) / bytes_per_sample;
int64_t frames = samples / channels; int64_t frames = samples / channels;
@ -518,7 +519,7 @@ public:
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk); int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
if (file_size < smallest_possible_file) if (file_size < smallest_possible_file)
throw AudioOpenError("File is too small to be a Wave64 file"); throw agi::AudioDataNotFoundError("File is too small to be a Wave64 file", 0);
// Read header // Read header
// This should throw an exception if the mapping fails // This should throw an exception if the mapping fails
@ -528,9 +529,9 @@ public:
// Check magic values // Check magic values
if (!CheckGuid(header.riff_guid, w64GuidRIFF)) if (!CheckGuid(header.riff_guid, w64GuidRIFF))
throw AudioOpenError("File is not a Wave64 RIFF file"); throw agi::AudioDataNotFoundError("File is not a Wave64 RIFF file", 0);
if (!CheckGuid(header.format_guid, w64GuidWAVE)) if (!CheckGuid(header.format_guid, w64GuidWAVE))
throw AudioOpenError("File is not a Wave64 WAVE file"); throw agi::AudioDataNotFoundError("File is not a Wave64 WAVE file", 0);
// Count how much more data we can have in the entire file // Count how much more data we can have in the entire file
uint64_t data_left = Endian::LittleToMachine(header.file_size) - sizeof(RiffChunk); uint64_t data_left = Endian::LittleToMachine(header.file_size) - sizeof(RiffChunk);
@ -550,15 +551,15 @@ public:
if (CheckGuid(chunk_guid, w64Guidfmt)) { if (CheckGuid(chunk_guid, w64Guidfmt)) {
if (got_fmt_header) if (got_fmt_header)
throw AudioOpenError("Bad file, found more than one 'fmt' chunk"); throw agi::AudioProviderOpenError("Bad file, found more than one 'fmt' chunk", 0);
FormatChunk &fmt = *(FormatChunk*)EnsureRangeAccessible(filepos, sizeof(FormatChunk)); FormatChunk &fmt = *(FormatChunk*)EnsureRangeAccessible(filepos, sizeof(FormatChunk));
got_fmt_header = true; got_fmt_header = true;
if (Endian::LittleToMachine(fmt.format.wFormatTag) == 3) if (Endian::LittleToMachine(fmt.format.wFormatTag) == 3)
throw AudioOpenError("File is IEEE 32 bit float format which isn't supported. Bug the developers if this matters."); throw agi::AudioProviderOpenError("File is IEEE 32 bit float format which isn't supported. Bug the developers if this matters.", 0);
if (Endian::LittleToMachine(fmt.format.wFormatTag) != 1) if (Endian::LittleToMachine(fmt.format.wFormatTag) != 1)
throw AudioOpenError("Can't use file, not PCM encoding"); throw agi::AudioProviderOpenError("Can't use file, not PCM encoding", 0);
// Set stuff inherited from the AudioProvider class // Set stuff inherited from the AudioProvider class
sample_rate = Endian::LittleToMachine(fmt.format.nSamplesPerSec); sample_rate = Endian::LittleToMachine(fmt.format.nSamplesPerSec);
@ -567,7 +568,7 @@ public:
} }
else if (CheckGuid(chunk_guid, w64Guiddata)) { else if (CheckGuid(chunk_guid, w64Guiddata)) {
if (!got_fmt_header) if (!got_fmt_header)
throw AudioOpenError("Found 'data' chunk before 'fmt ' chunk, file is invalid."); throw agi::AudioProviderOpenError("Found 'data' chunk before 'fmt ' chunk, file is invalid.", 0);
int64_t samples = chunk_size / bytes_per_sample; int64_t samples = chunk_size / bytes_per_sample;
int64_t frames = samples / channels; int64_t frames = samples / channels;
@ -601,23 +602,35 @@ public:
} }
}; };
/// @brief DOCME
/// @param filename
///
AudioProvider *CreatePCMAudioProvider(const wxString &filename) AudioProvider *CreatePCMAudioProvider(const wxString &filename)
{ {
bool wrong_file_type = true;
std::string msg; std::string msg;
try { try {
return new RiffWavPCMAudioProvider(filename); return new RiffWavPCMAudioProvider(filename);
} }
catch (AudioOpenError const& err) { catch (agi::AudioDataNotFoundError const& err) {
msg = "RIFF PCM WAV audio provider: " + err.GetMessage(); msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
} }
catch (agi::AudioProviderOpenError const& err) {
wrong_file_type = false;
msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
}
try { try {
return new Wave64AudioProvider(filename); return new Wave64AudioProvider(filename);
} }
catch (AudioOpenError const& err) { catch (agi::AudioDataNotFoundError const& err) {
msg += "\nWave64 audio provider: " + err.GetMessage(); msg += "\nWave64 audio provider: " + err.GetMessage();
throw AudioOpenError(msg);
} }
catch (agi::AudioProviderOpenError const& err) {
wrong_file_type = false;
msg += "\nWave64 audio provider: " + err.GetMessage();
}
if (wrong_file_type)
throw agi::AudioDataNotFoundError(msg, 0);
else
throw agi::AudioProviderOpenError(msg, 0);
} }

View file

@ -40,6 +40,7 @@
#include "audio_provider_ram.h" #include "audio_provider_ram.h"
#include "audio_controller.h"
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
#include "utils.h" #include "utils.h"
@ -67,7 +68,7 @@ RAMAudioProvider::RAMAudioProvider(AudioProvider *src, agi::BackgroundRunner *br
} }
catch (std::bad_alloc const&) { catch (std::bad_alloc const&) {
Clear(); Clear();
throw AudioOpenError("Couldn't open audio, not enough ram available."); throw agi::AudioCacheOpenError("Couldn't open audio, not enough ram available.", 0);
} }
// Copy parameters // Copy parameters

View file

@ -72,8 +72,6 @@ namespace agi { struct Context; }
namespace cmd { class Command; } namespace cmd { class Command; }
DECLARE_EVENT_TYPE(wxEVT_AUTOMATION_SCRIPT_COMPLETED, -1)
/// DOCME /// DOCME
namespace Automation4 { namespace Automation4 {
DEFINE_BASE_EXCEPTION_NOINNER(AutomationError, agi::Exception) DEFINE_BASE_EXCEPTION_NOINNER(AutomationError, agi::Exception)

View file

@ -119,7 +119,12 @@ struct audio_open_blank : public Command {
STR_HELP("Open a 150 minutes blank audio clip, for debugging.") STR_HELP("Open a 150 minutes blank audio clip, for debugging.")
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->audioController->OpenAudio("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000"); try {
c->audioController->OpenAudio("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000");
}
catch (agi::Exception const& e) {
wxMessageBox(lagi_wxString(e.GetChainedMessage()), "Error loading file", wxICON_ERROR | wxOK);
}
} }
}; };
@ -132,7 +137,12 @@ struct audio_open_noise : public Command {
STR_HELP("Open a 150 minutes noise-filled audio clip, for debugging.") STR_HELP("Open a 150 minutes noise-filled audio clip, for debugging.")
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
c->audioController->OpenAudio("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000"); try {
c->audioController->OpenAudio("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000");
}
catch (agi::Exception const& e) {
wxMessageBox(lagi_wxString(e.GetChainedMessage()), "Error loading file", wxICON_ERROR | wxOK);
}
} }
}; };
@ -153,6 +163,7 @@ struct audio_open_video : public Command {
try { try {
c->audioController->OpenAudio(c->videoController->GetVideoName()); c->audioController->OpenAudio(c->videoController->GetVideoName());
} }
catch (agi::UserCancelException const&) { }
catch (agi::Exception const& e) { catch (agi::Exception const& e) {
wxMessageBox(lagi_wxString(e.GetChainedMessage()), "Error loading file", wxICON_ERROR | wxOK); wxMessageBox(lagi_wxString(e.GetChainedMessage()), "Error loading file", wxICON_ERROR | wxOK);
} }

View file

@ -50,9 +50,7 @@ class AudioProvider;
/// @brief DOCME /// @brief DOCME
/// ///
/// DOCME /// DOCME
class AudioPlayer : public wxEvtHandler { class AudioPlayer {
void OnStopAudio(wxCommandEvent &event);
protected: protected:
/// DOCME /// DOCME
AudioProvider *provider; AudioProvider *provider;
@ -64,18 +62,11 @@ public:
AudioPlayer(); AudioPlayer();
virtual ~AudioPlayer(); virtual ~AudioPlayer();
/// @brief DOCME
///
virtual void OpenStream() {} virtual void OpenStream() {}
/// @brief DOCME
///
virtual void CloseStream() {} virtual void CloseStream() {}
virtual void Play(int64_t start,int64_t count)=0; // Play sample range virtual void Play(int64_t start,int64_t count)=0; // Play sample range
virtual void Stop(bool timerToo=true)=0; // Stop playing virtual void Stop(bool timerToo=true)=0; // Stop playing
virtual void RequestStop(); // Request it to stop playing in a thread-safe way
virtual bool IsPlaying()=0; virtual bool IsPlaying()=0;
virtual void SetVolume(double volume)=0; virtual void SetVolume(double volume)=0;
@ -87,14 +78,8 @@ public:
virtual void SetEndPosition(int64_t pos)=0; virtual void SetEndPosition(int64_t pos)=0;
virtual void SetCurrentPosition(int64_t pos)=0; virtual void SetCurrentPosition(int64_t pos)=0;
virtual wxMutex *GetMutex() { return NULL; }
virtual void SetProvider(AudioProvider *new_provider) { provider = new_provider; } virtual void SetProvider(AudioProvider *new_provider) { provider = new_provider; }
AudioProvider *GetProvider() const { return provider; } AudioProvider *GetProvider() const { return provider; }
void SetDisplayTimer(wxTimer *timer) { displayTimer = timer; }
DECLARE_EVENT_TABLE()
}; };
class AudioPlayerFactory : public Factory0<AudioPlayer> { class AudioPlayerFactory : public Factory0<AudioPlayer> {
@ -104,5 +89,3 @@ public:
static void RegisterProviders(); static void RegisterProviders();
static AudioPlayer *GetAudioPlayer(); static AudioPlayer *GetAudioPlayer();
}; };
DECLARE_EVENT_TYPE(wxEVT_STOP_AUDIO, -1)

View file

@ -98,7 +98,5 @@ public:
}; };
DEFINE_BASE_EXCEPTION_NOINNER(AudioProviderError, agi::Exception) DEFINE_BASE_EXCEPTION_NOINNER(AudioProviderError, agi::Exception)
DEFINE_SIMPLE_EXCEPTION_NOINNER(AudioOpenError, AudioProviderError, "audio/open/failed")
/// Error of some sort occurred while decoding a frame /// Error of some sort occurred while decoding a frame
DEFINE_SIMPLE_EXCEPTION_NOINNER(AudioDecodeError, AudioProviderError, "audio/error") DEFINE_SIMPLE_EXCEPTION_NOINNER(AudioDecodeError, AudioProviderError, "audio/error")