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.
*/
wxFileName fn(url);
if (!fn.FileExists())
{
config::mru->Remove("Audio", STD_STR(url));
agi::FileNotFoundError fnf(STD_STR(url));
throw agi::AudioOpenError(
"Failed opening audio file (parsing as plain filename)",
&fnf);
try {
provider = AudioProviderFactory::GetProvider(url);
StandardPaths::SetPathValue("?audio", wxFileName(url).GetPath());
}
catch (...) {
config::mru->Remove("Audio", STD_STR(url));
throw;
}
provider = AudioProviderFactory::GetProvider(url);
StandardPaths::SetPathValue("?audio", fn.GetPath());
}
try

View File

@ -445,6 +445,27 @@ public:
};
namespace agi {
DEFINE_BASE_EXCEPTION(AudioControllerError, Exception)
DEFINE_SIMPLE_EXCEPTION(AudioOpenError, AudioControllerError, "audio_controller/open_failed")
/// Base class for all audio-related errors
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
#include "audio_player_pulse.h"
#endif
#include "audio_controller.h"
#include "compat.h"
#include "main.h"
@ -70,38 +72,20 @@ AudioPlayer::~AudioPlayer() {
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() {
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;
for (unsigned int i=0;i<list.size();i++) {
std::string error;
for (size_t i = 0; i < list.size(); ++i) {
try {
AudioPlayer *player = Create(list[i]);
if (player) return player;
return Create(list[i]);
}
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() {

View File

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

View File

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

View File

@ -40,6 +40,7 @@
#include <libaegisub/log.h>
#include "audio_controller.h"
#include "audio_player_dsound.h"
#include "frame_main.h"
#include "main.h"
@ -75,7 +76,7 @@ void DirectSoundPlayer::OpenStream() {
// Initialize the DirectSound object
HRESULT res;
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
directSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(),DSSCL_PRIORITY);
@ -106,11 +107,11 @@ void DirectSoundPlayer::OpenStream() {
// Create the buffer
IDirectSoundBuffer *buf;
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
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
offset = 0;

View File

@ -48,6 +48,7 @@
#include "audio_player_dsound2.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include "frame_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);
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 };
switch (WaitForMultipleObjects(2, running_or_error, FALSE, INFINITE))
@ -680,10 +681,10 @@ DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int
case WAIT_OBJECT_0 + 1:
// error happened, we fail
throw error_message;
throw agi::AudioPlayerOpenError(error_message, 0);
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
throw error_message;
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_controller.h"
#include "utils.h"
// Auto-link to OpenAL lib for MSVC
@ -49,6 +50,8 @@
#pragma comment(lib, "openal32.lib")
#endif
DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal")
OpenALPlayer::OpenALPlayer()
: open(false)
, playing(false)
@ -76,25 +79,25 @@ void OpenALPlayer::OpenStream()
try {
// Open device
device = alcOpenDevice(0);
if (!device) throw OpenALException("Failed opening default OpenAL device");
if (!device) throw OpenALException("Failed opening default OpenAL device", 0);
// Create context
context = alcCreateContext(device, 0);
if (!context) throw OpenALException("Failed creating OpenAL context");
if (!alcMakeContextCurrent(context)) throw OpenALException("Failed selecting OpenAL context");
if (!context) throw OpenALException("Failed creating OpenAL context", 0);
if (!alcMakeContextCurrent(context)) throw OpenALException("Failed selecting OpenAL context", 0);
// Clear error code
alGetError();
// Generate 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
alGenSources(1, &source);
if (alGetError() != AL_NO_ERROR) {
alDeleteBuffers(num_buffers, buffers);
throw OpenALException("Error generating OpenAL source");
throw OpenALException("Error generating OpenAL source", 0);
}
}
catch (...)

View File

@ -53,10 +53,6 @@
#include <vector>
#endif
#include <libaegisub/exception.h>
DEFINE_SIMPLE_EXCEPTION_NOINNER(OpenALException, agi::Exception, "audio/player/openal/generic")
/// DOCME
/// @class OpenALPlayer
/// @brief DOCME

View File

@ -43,11 +43,16 @@
#include <libaegisub/log.h>
#include "audio_player_oss.h"
#include "frame_main.h"
#include "audio_controller.h"
#include "compat.h"
#include "include/aegisub/audio_provider.h"
#include "main.h"
#include "utils.h"
DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss")
/// @brief Constructor
///
OSSPlayer::OSSPlayer()
@ -81,7 +86,7 @@ void OSSPlayer::OpenStream()
wxString device = lagi_wxString(OPT_GET("Audio/OSS/Device")->GetString());
dspdev = ::open(device.mb_str(wxConvUTF8), O_WRONLY, 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)
@ -93,7 +98,7 @@ void OSSPlayer::OpenStream()
// Set number of channels
int channels = provider->GetChannels();
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
@ -106,17 +111,17 @@ void OSSPlayer::OpenStream()
sample_format = AFMT_S16_LE;
break;
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) {
throw OSSError("OSS player: setting sample format failed");
throw OSSError("OSS player: setting sample format failed", 0);
}
// Set sample rate
rate = provider->GetSampleRate();
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

View File

@ -49,12 +49,8 @@
#endif
#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;
/// DOCME

View File

@ -42,11 +42,15 @@
#include <libaegisub/log.h>
#include "audio_player_portaudio.h"
#include "charset_conv.h"
#include "audio_controller.h"
#include "compat.h"
#include "include/aegisub/audio_provider.h"
#include "main.h"
#include "utils.h"
DEFINE_SIMPLE_EXCEPTION(PortAudioError, agi::AudioPlayerOpenError, "audio/player/open/portaudio")
// Uncomment to enable extremely spammy debug logging
//#define PORTAUDIO_DEBUG
@ -54,7 +58,7 @@ PortAudioPlayer::PortAudioPlayer() {
PaError err = Pa_Initialize();
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;
pa_start = 0.0;
@ -109,7 +113,7 @@ void PortAudioPlayer::OpenStream() {
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("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
#include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include <libaegisub/exception.h>
extern "C" {
#include <portaudio.h>
}
DEFINE_SIMPLE_EXCEPTION_NOINNER(PortAudioError, agi::Exception, "audio/player/portaudio")
#ifndef AGI_PRE
#include <map>
#include <string>
#endif
/// @class PortAudioPlayer
/// @brief PortAudio Player

View File

@ -43,11 +43,11 @@
#endif
#include "audio_player_pulse.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include "utils.h"
/// @brief Constructor
///
PulseAudioPlayer::PulseAudioPlayer()
: context_notify(0, 1)
, context_success(0, 1)
@ -60,32 +60,20 @@ PulseAudioPlayer::PulseAudioPlayer()
is_playing = false;
}
/// @brief Destructor
///
PulseAudioPlayer::~PulseAudioPlayer()
{
if (open) CloseStream();
CloseStream();
}
/// @brief Open stream
///
void PulseAudioPlayer::OpenStream()
{
//printf("Opening PulseAudio stream\n");
if (open) CloseStream();
// Get provider
AudioProvider *provider = GetProvider();
// Initialise a mainloop
//printf("Initialising threaded main loop\n");
mainloop = pa_threaded_mainloop_new();
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");
pa_threaded_mainloop_start(mainloop);
@ -95,7 +83,7 @@ void PulseAudioPlayer::OpenStream()
context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub");
if (!context) {
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);
@ -113,9 +101,7 @@ void PulseAudioPlayer::OpenStream()
pa_context_unref(context);
pa_threaded_mainloop_stop(mainloop);
pa_threaded_mainloop_free(mainloop);
wxString s(pa_strerror(paerror), wxConvUTF8);
s.Prepend("PulseAudio reported error: ");
throw s.c_str();
throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
}
// otherwise loop once more
}
@ -137,7 +123,7 @@ void PulseAudioPlayer::OpenStream()
pa_context_unref(context);
pa_threaded_mainloop_stop(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_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);
if (paerror) {
printf("PulseAudio reported error: %s (%d)\n", pa_strerror(paerror), paerror);
wxString s(pa_strerror(paerror), wxConvUTF8);
s.Prepend("PulseAudio reported error: ");
throw s.c_str();
throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
}
while (true) {
stream_notify.Wait();
@ -158,7 +142,7 @@ void PulseAudioPlayer::OpenStream()
} else if (sstate == PA_STREAM_FAILED) {
paerror = pa_context_errno(context);
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");
@ -168,11 +152,6 @@ void PulseAudioPlayer::OpenStream()
open = true;
}
/// @brief Close stream
/// @return
///
void PulseAudioPlayer::CloseStream()
{
if (!open) return;
@ -192,12 +171,6 @@ void PulseAudioPlayer::CloseStream()
open = false;
}
/// @brief Play
/// @param start
/// @param count
///
void PulseAudioPlayer::Play(int64_t start,int64_t count)
{
//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);
}
/// @brief Stop
/// @param timerToo
/// @return
///
void PulseAudioPlayer::Stop(bool timerToo)
{
if (!is_playing) return;
@ -286,61 +253,31 @@ void PulseAudioPlayer::Stop(bool timerToo)
}
}
/// @brief DOCME
/// @return
///
bool PulseAudioPlayer::IsPlaying()
{
return is_playing;
}
/// @brief Set end
/// @param pos
///
void PulseAudioPlayer::SetEndPosition(int64_t pos)
{
end_frame = pos;
}
/// @brief Set current position
/// @param pos
///
void PulseAudioPlayer::SetCurrentPosition(int64_t pos)
{
cur_frame = pos;
}
/// @brief DOCME
/// @return
///
int64_t PulseAudioPlayer::GetStartPosition()
{
return start_frame;
}
/// @brief DOCME
/// @return
///
int64_t PulseAudioPlayer::GetEndPosition()
{
return end_frame;
}
/// @brief Get current position
/// @return
///
int64_t PulseAudioPlayer::GetCurrentPosition()
{
if (!is_playing) return 0;
@ -356,52 +293,28 @@ int64_t PulseAudioPlayer::GetCurrentPosition()
return start_frame + playtime * provider->GetSampleRate() / (1000*1000);
}
/// @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)
{
thread->context_success_val = success;
thread->context_success.Post();
}
/// @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)
{
thread->cstate = pa_context_get_state(thread->context);
thread->context_notify.Post();
}
/// @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)
{
thread->stream_success_val = success;
thread->stream_success.Post();
}
/// @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)
{
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;
}
/// @brief Called by PA to notify about other stuff
/// @param p
/// @param thread
///
void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread)
{
thread->sstate = pa_stream_get_state(thread->stream);

View File

@ -38,7 +38,6 @@
#include <pulse/pulseaudio.h>
#include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
class PulseAudioPlayer;
@ -141,16 +140,7 @@ public:
void SetEndPosition(int64_t pos);
void SetCurrentPosition(int64_t pos);
/// @brief DOCME
/// @param vol
/// @return
///
void SetVolume(double vol) { volume = vol; }
/// @brief DOCME
/// @return
///
double GetVolume() { return volume; }
};

View File

@ -41,6 +41,7 @@
#include <wx/thread.h>
#endif
#include "audio_controller.h"
#ifdef WITH_AVISYNTH
#include "audio_provider_avs.h"
#endif
@ -56,13 +57,6 @@
#include "frame_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 {
try {
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 *provider = NULL;
bool found = false;
AudioProvider *provider = 0;
bool found_file = false;
bool found_audio = false;
std::string msg;
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) {
msg = "PCM audio provider: " + err.GetMessage() + " not found.\n";
}
catch (AudioOpenError const& err) {
found = true;
msg += err.GetMessage() + "\n";
catch (agi::AudioOpenError const& err) {
found_file = true;
msg += err.GetChainedMessage() + "\n";
}
}
if (!provider) {
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 {
provider = Create(list[i], filename);
if (provider) break;
@ -121,20 +116,25 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int c
catch (agi::FileNotFoundError const& err) {
msg += list[i] + ": " + err.GetMessage() + " not found.\n";
}
catch (AudioOpenError const& err) {
found = true;
msg += list[i] + ": " + err.GetMessage() + "\n";
catch (agi::AudioDataNotFoundError const& err) {
found_file = true;
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 (found) {
throw AudioOpenError(msg);
}
else {
throw agi::FileNotFoundError(STD_STR(filename));
}
if (found_audio)
throw agi::AudioProviderOpenError(msg, 0);
if (found_file)
throw agi::AudioDataNotFoundError(msg, 0);
throw agi::FileNotFoundError(STD_STR(filename));
}
bool needsCache = provider->NeedsCache();
// Give it a converter if needed
@ -155,7 +155,7 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename, int c
// Convert to HD
if (cache == 2) return new HDAudioProvider(provider, &progress);
throw AudioOpenError("Unknown caching method");
throw agi::AudioCacheOpenError("Unknown caching method", 0);
}
/// @brief Register all providers

View File

@ -46,6 +46,8 @@
#endif
#include "audio_provider_avs.h"
#include "audio_controller.h"
#include "charset_conv.h"
#include "compat.h"
#include "main.h"
@ -55,56 +57,64 @@
/// @brief Constructor
/// @param _filename
///
AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) try : filename(filename) {
AVSValue script;
wxMutexLocker lock(AviSynthMutex);
AvisynthAudioProvider::AvisynthAudioProvider(wxString filename)
: filename(filename)
{
try {
AVSValue script;
wxMutexLocker lock(AviSynthMutex);
wxFileName fn(filename);
if (!fn.FileExists())
throw agi::FileNotFoundError(STD_STR(filename));
wxFileName fn(filename);
if (!fn.FileExists())
throw agi::FileNotFoundError(STD_STR(filename));
// Include
if (filename.EndsWith(".avs")) {
char *fname = env->SaveString(fn.GetShortPath().mb_str(csConvLocal));
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)));
// Include
if (filename.EndsWith(".avs")) {
char *fname = env->SaveString(fn.GetShortPath().mb_str(csConvLocal));
script = env->Invoke("Import", fname);
}
// Load audio with DSS if it exists
if (env->FunctionExists("DirectShowSource")) {
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
}
// Otherwise fail
// Use DirectShowSource
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);
}
catch (AvisynthError &err) {
throw AudioOpenError("Avisynth error: " + std::string(err.msg));
// 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
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
/// @param _clip
///
void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
AVSValue script;
// Check if it has audio
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
char buffer[1024];

View File

@ -26,6 +26,7 @@
#include "audio_provider_convert.h"
#include "aegisub_endian.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include <libaegisub/scoped_ptr.h>
@ -54,7 +55,7 @@ class BitdepthConvertAudioProvider : public AudioProviderConverter {
public:
BitdepthConvertAudioProvider(AudioProvider *src) : AudioProviderConverter(src) {
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_bytes_per_sample = bytes_per_sample;

View File

@ -47,10 +47,11 @@
#endif
#include "audio_provider_ffmpegsource.h"
#include "audio_controller.h"
#include "compat.h"
#include "main.h"
/// @brief Constructor
/// @param filename
///
@ -64,7 +65,7 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename)
if (SUCCEEDED(res))
COMInited = true;
else if (res != RPC_E_CHANGED_MODE)
throw AudioOpenError("COM initialization failure");
throw agi::AudioProviderOpenError("COM initialization failure", 0);
#endif
// initialize ffmpegsource
// FIXME: CPU detection?
@ -101,7 +102,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
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
// 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);
// if it's still -1 here, user pressed cancel
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
@ -137,7 +138,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
if (TrackNumber < 0) {
FFMS_DestroyIndex(Index);
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,
@ -166,7 +167,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
Index = DoIndexing(Indexer, CacheName, TrackMask, GetErrorHandlingMode());
}
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
@ -187,7 +188,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
FFMS_DestroyIndex(Index);
Index = NULL;
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);
@ -196,7 +197,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
sample_rate = AudioInfo.SampleRate;
num_samples = AudioInfo.NumSamples;
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?
// 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 32: bytes_per_sample = 4; break;
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_controller.h"
#include "audio_provider_pcm.h"
#include "compat.h"
#include "main.h"
@ -103,7 +104,7 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
wxDiskspaceSize_t freespace;
if (wxGetDiskSpace(cache_dir(), 0, &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();

View File

@ -54,6 +54,7 @@
#include <libaegisub/log.h>
#include "aegisub_endian.h"
#include "audio_controller.h"
#include "audio_provider_pcm.h"
#include "compat.h"
#include "utils.h"
@ -81,7 +82,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
LARGE_INTEGER li_file_size = {0};
if (!GetFileSizeEx(file_handle, &li_file_size)) {
CloseHandle(file_handle);
throw AudioOpenError("Failed getting file size");
throw agi::AudioProviderOpenError("Failed getting file size", 0);
}
file_size = li_file_size.QuadPart;
@ -94,7 +95,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
if (file_mapping == 0) {
CloseHandle(file_handle);
throw AudioOpenError("Failed creating file mapping");
throw agi::AudioProviderOpenError("Failed creating file mapping", 0);
}
current_mapping = 0;
@ -111,7 +112,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
memset(&filestats, 0, sizeof(filestats));
if (fstat(file_handle, &filestats)) {
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;
@ -331,9 +332,9 @@ public:
// Check magic values
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"))
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
// The first 4 bytes are already eaten by the header.format field
@ -356,13 +357,13 @@ public:
filepos += sizeof(ch);
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;
fmtChunk &fmt = *(fmtChunk*)EnsureRangeAccessible(filepos, sizeof(fmtChunk));
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
sample_rate = Endian::LittleToMachine(fmt.samplerate);
@ -374,7 +375,7 @@ public:
// This won't pick up 'data' chunks inside 'wavl' chunks
// 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 frames = samples / channels;
@ -518,7 +519,7 @@ public:
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
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
// This should throw an exception if the mapping fails
@ -528,9 +529,9 @@ public:
// Check magic values
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))
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
uint64_t data_left = Endian::LittleToMachine(header.file_size) - sizeof(RiffChunk);
@ -550,15 +551,15 @@ public:
if (CheckGuid(chunk_guid, w64Guidfmt)) {
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));
got_fmt_header = true;
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)
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
sample_rate = Endian::LittleToMachine(fmt.format.nSamplesPerSec);
@ -567,7 +568,7 @@ public:
}
else if (CheckGuid(chunk_guid, w64Guiddata)) {
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 frames = samples / channels;
@ -601,23 +602,35 @@ public:
}
};
/// @brief DOCME
/// @param filename
///
AudioProvider *CreatePCMAudioProvider(const wxString &filename)
{
bool wrong_file_type = true;
std::string msg;
try {
return new RiffWavPCMAudioProvider(filename);
}
catch (AudioOpenError const& err) {
catch (agi::AudioDataNotFoundError const& err) {
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 {
return new Wave64AudioProvider(filename);
}
catch (AudioOpenError const& err) {
catch (agi::AudioDataNotFoundError const& err) {
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_controller.h"
#include "compat.h"
#include "main.h"
#include "utils.h"
@ -67,7 +68,7 @@ RAMAudioProvider::RAMAudioProvider(AudioProvider *src, agi::BackgroundRunner *br
}
catch (std::bad_alloc const&) {
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

View File

@ -72,8 +72,6 @@ namespace agi { struct Context; }
namespace cmd { class Command; }
DECLARE_EVENT_TYPE(wxEVT_AUTOMATION_SCRIPT_COMPLETED, -1)
/// DOCME
namespace Automation4 {
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.")
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.")
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 {
c->audioController->OpenAudio(c->videoController->GetVideoName());
}
catch (agi::UserCancelException const&) { }
catch (agi::Exception const& e) {
wxMessageBox(lagi_wxString(e.GetChainedMessage()), "Error loading file", wxICON_ERROR | wxOK);
}

View File

@ -50,9 +50,7 @@ class AudioProvider;
/// @brief DOCME
///
/// DOCME
class AudioPlayer : public wxEvtHandler {
void OnStopAudio(wxCommandEvent &event);
class AudioPlayer {
protected:
/// DOCME
AudioProvider *provider;
@ -64,18 +62,11 @@ public:
AudioPlayer();
virtual ~AudioPlayer();
/// @brief DOCME
///
virtual void OpenStream() {}
/// @brief DOCME
///
virtual void CloseStream() {}
virtual void Play(int64_t start,int64_t count)=0; // Play sample range
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 void SetVolume(double volume)=0;
@ -87,14 +78,8 @@ public:
virtual void SetEndPosition(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; }
AudioProvider *GetProvider() const { return provider; }
void SetDisplayTimer(wxTimer *timer) { displayTimer = timer; }
DECLARE_EVENT_TABLE()
};
class AudioPlayerFactory : public Factory0<AudioPlayer> {
@ -104,5 +89,3 @@ public:
static void RegisterProviders();
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_SIMPLE_EXCEPTION_NOINNER(AudioOpenError, AudioProviderError, "audio/open/failed")
/// Error of some sort occurred while decoding a frame
DEFINE_SIMPLE_EXCEPTION_NOINNER(AudioDecodeError, AudioProviderError, "audio/error")