mirror of https://github.com/odrling/Aegisub
Refactor the rest of the factories
This commit is contained in:
parent
dbe9bcfdad
commit
e71270f0f0
|
@ -110,13 +110,7 @@
|
|||
<ClInclude Include="$(SrcDir)audio_display.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_karaoke.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_marker.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_alsa.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_dsound.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_dsound2.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_openal.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_oss.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_portaudio.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_player_pulse.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_renderer.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_renderer_spectrum.h" />
|
||||
<ClInclude Include="$(SrcDir)audio_renderer_waveform.h" />
|
||||
|
@ -197,7 +191,6 @@
|
|||
<ClInclude Include="$(SrcDir)pen.h" />
|
||||
<ClInclude Include="$(SrcDir)persist_location.h" />
|
||||
<ClInclude Include="$(SrcDir)placeholder_ctrl.h" />
|
||||
<ClInclude Include="$(SrcDir)plugin_manager.h" />
|
||||
<ClInclude Include="$(SrcDir)preferences.h" />
|
||||
<ClInclude Include="$(SrcDir)preferences_base.h" />
|
||||
<ClInclude Include="$(SrcDir)resolution_resampler.h" />
|
||||
|
@ -391,7 +384,6 @@
|
|||
<ClCompile Include="$(SrcDir)mkv_wrap.cpp" />
|
||||
<ClCompile Include="$(SrcDir)pen.cpp" />
|
||||
<ClCompile Include="$(SrcDir)persist_location.cpp" />
|
||||
<ClCompile Include="$(SrcDir)plugin_manager.cpp" />
|
||||
<ClCompile Include="$(SrcDir)preferences.cpp" />
|
||||
<ClCompile Include="$(SrcDir)preferences_base.cpp" />
|
||||
<ClCompile Include="$(SrcDir)resolution_resampler.cpp" />
|
||||
|
|
|
@ -213,24 +213,6 @@
|
|||
<ClInclude Include="$(SrcDir)config.h">
|
||||
<Filter>Config</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_pulse.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_alsa.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_dsound.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_dsound2.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_openal.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_oss.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)audio_player_portaudio.h">
|
||||
<Filter>Audio\Players</Filter>
|
||||
</ClInclude>
|
||||
|
@ -528,9 +510,6 @@
|
|||
<ClInclude Include="$(SrcDir)include\aegisub\video_provider.h">
|
||||
<Filter>Video\Providers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)plugin_manager.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)spline_curve.h">
|
||||
<Filter>Video\Visual tools</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1055,9 +1034,6 @@
|
|||
<ClCompile Include="$(SrcDir)menu.cpp">
|
||||
<Filter>Main UI</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)plugin_manager.cpp">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)spline.cpp">
|
||||
<Filter>Video\Visual tools</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -203,7 +203,6 @@ SRC += \
|
|||
mkv_wrap.cpp \
|
||||
pen.cpp \
|
||||
persist_location.cpp \
|
||||
plugin_manager.cpp \
|
||||
preferences.cpp \
|
||||
preferences_base.cpp \
|
||||
resolution_resampler.cpp \
|
||||
|
|
|
@ -35,56 +35,75 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "audio_player_alsa.h"
|
||||
#include "audio_player_dsound.h"
|
||||
#include "audio_player_dsound2.h"
|
||||
#include "audio_player_openal.h"
|
||||
#include "audio_player_oss.h"
|
||||
#include "audio_player_portaudio.h"
|
||||
#include "audio_player_pulse.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "factory_manager.h"
|
||||
#include "options.h"
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
AudioPlayer::AudioPlayer(AudioProvider *provider)
|
||||
: provider(provider)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(AudioProvider *providers);
|
||||
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *providers);
|
||||
std::unique_ptr<AudioPlayer> CreateDirectSound2Player(AudioProvider *providers);
|
||||
std::unique_ptr<AudioPlayer> CreateOpenALPlayer(AudioProvider *providers);
|
||||
std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(AudioProvider *providers);
|
||||
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(AudioProvider *providers);
|
||||
std::unique_ptr<AudioPlayer> CreateOSSPlayer(AudioProvider *providers);
|
||||
|
||||
namespace {
|
||||
struct factory {
|
||||
const char *name;
|
||||
std::unique_ptr<AudioPlayer> (*create)(AudioProvider *);
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
const factory factories[] = {
|
||||
#ifdef WITH_ALSA
|
||||
{"ALSA", CreateAlsaPlayer, false},
|
||||
#endif
|
||||
#ifdef WITH_DIRECTSOUND
|
||||
{"DirectSound-old", CreateDirectSoundPlayer, false},
|
||||
{"DirectSound", CreateDirectSound2Player, false},
|
||||
#endif
|
||||
#ifdef WITH_OPENAL
|
||||
{"OpenAL", CreateOpenALPlayer, false},
|
||||
#endif
|
||||
#ifdef WITH_PORTAUDIO
|
||||
{"PortAudio", CreatePortAudioPlayer, false},
|
||||
#endif
|
||||
#ifdef WITH_LIBPULSE
|
||||
{"PulseAudio", CreatePulseAudioPlayer, false},
|
||||
#endif
|
||||
#ifdef WITH_OSS
|
||||
{"OSS", CreateOSSPlayer, false},
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<std::string> AudioPlayerFactory::GetClasses() {
|
||||
return ::GetClasses(boost::make_iterator_range(std::begin(factories), std::end(factories)));
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(AudioProvider *provider) {
|
||||
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Player")->GetString());
|
||||
if (list.empty()) throw agi::NoAudioPlayersError("No audio players are available.", nullptr);
|
||||
if (std::distance(std::begin(factories), std::end(factories)) == 0)
|
||||
throw agi::NoAudioPlayersError("No audio players are available.", nullptr);
|
||||
|
||||
auto preferred = OPT_GET("Audio/Player")->GetString();
|
||||
auto sorted = GetSorted(boost::make_iterator_range(std::begin(factories), std::end(factories)), preferred);
|
||||
|
||||
std::string error;
|
||||
for (auto const& factory_name : list) {
|
||||
for (auto factory : sorted) {
|
||||
try {
|
||||
return Create(factory_name, provider);
|
||||
return factory->create(provider);
|
||||
}
|
||||
catch (agi::AudioPlayerOpenError const& err) {
|
||||
error += factory_name + " factory: " + err.GetChainedMessage() + "\n";
|
||||
error += std::string(factory->name) + " factory: " + err.GetChainedMessage() + "\n";
|
||||
}
|
||||
}
|
||||
throw agi::AudioPlayerOpenError(error, nullptr);
|
||||
}
|
||||
|
||||
void AudioPlayerFactory::RegisterProviders() {
|
||||
#ifdef WITH_ALSA
|
||||
Register<AlsaPlayer>("ALSA");
|
||||
#endif
|
||||
#ifdef WITH_DIRECTSOUND
|
||||
Register<DirectSoundPlayer>("DirectSound-old");
|
||||
Register<DirectSoundPlayer2>("DirectSound");
|
||||
#endif
|
||||
#ifdef WITH_OPENAL
|
||||
Register<OpenALPlayer>("OpenAL");
|
||||
#endif
|
||||
#ifdef WITH_PORTAUDIO
|
||||
Register<PortAudioPlayer>("PortAudio");
|
||||
#endif
|
||||
#ifdef WITH_LIBPULSE
|
||||
Register<PulseAudioPlayer>("PulseAudio");
|
||||
#endif
|
||||
#ifdef WITH_OSS
|
||||
Register<OSSPlayer>("OSS");
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -35,8 +35,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef WITH_ALSA
|
||||
|
||||
#include "audio_player_alsa.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
@ -48,15 +47,73 @@
|
|||
#include <libaegisub/util.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
struct PlaybackState {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
bool playing = false;
|
||||
bool alive = false;
|
||||
|
||||
bool signal_start = false;
|
||||
bool signal_stop = false;
|
||||
bool signal_close = false;
|
||||
bool signal_volume = false;
|
||||
|
||||
double volume = 1.0;
|
||||
int64_t start_position = 0;
|
||||
int64_t end_position = 0;
|
||||
|
||||
AudioProvider *provider = nullptr;
|
||||
std::string device_name;
|
||||
|
||||
int64_t last_position = 0;
|
||||
timespec last_position_time;
|
||||
|
||||
PlaybackState()
|
||||
{
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
pthread_cond_init(&cond, 0);
|
||||
memset(&last_position_time, 0, sizeof last_position_time);
|
||||
}
|
||||
|
||||
~PlaybackState()
|
||||
{
|
||||
pthread_cond_destroy(&cond);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
class AlsaPlayer final : public AudioPlayer {
|
||||
PlaybackState ps;
|
||||
pthread_t thread;
|
||||
|
||||
public:
|
||||
AlsaPlayer(AudioProvider *provider);
|
||||
~AlsaPlayer();
|
||||
|
||||
void Play(int64_t start, int64_t count);
|
||||
void Stop();
|
||||
bool IsPlaying();
|
||||
|
||||
int64_t GetStartPosition();
|
||||
int64_t GetEndPosition();
|
||||
int64_t GetCurrentPosition();
|
||||
void SetEndPosition(int64_t pos);
|
||||
void SetCurrentPosition(int64_t pos);
|
||||
|
||||
void SetVolume(double vol);
|
||||
};
|
||||
|
||||
class PthreadMutexLocker {
|
||||
pthread_mutex_t &mutex;
|
||||
|
||||
PthreadMutexLocker(const PthreadMutexLocker &); // uncopyable
|
||||
PthreadMutexLocker(); // no default
|
||||
PthreadMutexLocker& operator=(PthreadMutexLocker const&);
|
||||
PthreadMutexLocker(const PthreadMutexLocker &) = delete;
|
||||
PthreadMutexLocker& operator=(PthreadMutexLocker const&) = delete;
|
||||
|
||||
public:
|
||||
explicit PthreadMutexLocker(pthread_mutex_t &mutex) : mutex(mutex)
|
||||
|
@ -83,78 +140,22 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
class ScopedAliveFlag {
|
||||
bool &flag;
|
||||
|
||||
ScopedAliveFlag(const ScopedAliveFlag &); // uncopyable
|
||||
ScopedAliveFlag(); // no default
|
||||
ScopedAliveFlag& operator=(ScopedAliveFlag const&);
|
||||
ScopedAliveFlag(const ScopedAliveFlag &) = delete;
|
||||
ScopedAliveFlag& operator=(ScopedAliveFlag const&) = delete;
|
||||
|
||||
public:
|
||||
explicit ScopedAliveFlag(bool &var) : flag(var) { flag = true; }
|
||||
~ScopedAliveFlag() { flag = false; }
|
||||
};
|
||||
|
||||
|
||||
struct PlaybackState {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
bool playing;
|
||||
bool alive;
|
||||
|
||||
bool signal_start;
|
||||
bool signal_stop;
|
||||
bool signal_close;
|
||||
bool signal_volume;
|
||||
|
||||
double volume;
|
||||
int64_t start_position;
|
||||
int64_t end_position;
|
||||
|
||||
AudioProvider *provider;
|
||||
std::string device_name;
|
||||
|
||||
int64_t last_position;
|
||||
timespec last_position_time;
|
||||
|
||||
PlaybackState()
|
||||
{
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
pthread_cond_init(&cond, 0);
|
||||
|
||||
Reset();
|
||||
volume = 1.0;
|
||||
}
|
||||
|
||||
~PlaybackState()
|
||||
{
|
||||
pthread_cond_destroy(&cond);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
playing = false;
|
||||
alive = false;
|
||||
signal_start = false;
|
||||
signal_stop = false;
|
||||
signal_close = false;
|
||||
signal_volume = false;
|
||||
start_position = 0;
|
||||
end_position = 0;
|
||||
last_position = 0;
|
||||
memset(&last_position_time, 0, sizeof last_position_time);
|
||||
provider = 0;
|
||||
}
|
||||
};
|
||||
|
||||
void *playback_thread(void *arg)
|
||||
{
|
||||
// This is exception-free territory!
|
||||
// Return a pointer to a static string constant describing the error, or 0 on no error
|
||||
PlaybackState &ps = *(PlaybackState*)arg;
|
||||
auto &ps = *static_cast<PlaybackState *>(arg);
|
||||
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
ScopedAliveFlag alive_flag(ps.alive);
|
||||
|
@ -363,73 +364,66 @@ do_setup:
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
AlsaPlayer::AlsaPlayer(AudioProvider *provider)
|
||||
: AudioPlayer(provider)
|
||||
, ps(agi::util::make_unique<PlaybackState>())
|
||||
{
|
||||
ps->provider = provider;
|
||||
ps.provider = provider;
|
||||
|
||||
ps->device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString();
|
||||
ps.device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString();
|
||||
|
||||
if (pthread_create(&thread, 0, &playback_thread, ps.get()) != 0)
|
||||
if (pthread_create(&thread, 0, &playback_thread, &ps) != 0)
|
||||
throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0);
|
||||
}
|
||||
|
||||
|
||||
AlsaPlayer::~AlsaPlayer()
|
||||
{
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
ps->signal_stop = true;
|
||||
ps->signal_close = true;
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
ps.signal_stop = true;
|
||||
ps.signal_close = true;
|
||||
LOG_D("audio/player/alsa") << "close stream, stop+close signal";
|
||||
pthread_cond_signal(&ps->cond);
|
||||
pthread_cond_signal(&ps.cond);
|
||||
}
|
||||
|
||||
pthread_join(thread, 0); // FIXME: check for errors
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::Play(int64_t start, int64_t count)
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
ps->signal_start = true;
|
||||
ps->signal_stop = true; // make sure to stop any ongoing playback first
|
||||
ps->start_position = start;
|
||||
ps->end_position = start + count;
|
||||
pthread_cond_signal(&ps->cond);
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
ps.signal_start = true;
|
||||
ps.signal_stop = true; // make sure to stop any ongoing playback first
|
||||
ps.start_position = start;
|
||||
ps.end_position = start + count;
|
||||
pthread_cond_signal(&ps.cond);
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::Stop()
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
ps->signal_stop = true;
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
ps.signal_stop = true;
|
||||
LOG_D("audio/player/alsa") << "stop stream, stop signal";
|
||||
pthread_cond_signal(&ps->cond);
|
||||
pthread_cond_signal(&ps.cond);
|
||||
}
|
||||
|
||||
bool AlsaPlayer::IsPlaying()
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
return ps->playing;
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
return ps.playing;
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::SetEndPosition(int64_t pos)
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
ps->end_position = pos;
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
ps.end_position = pos;
|
||||
}
|
||||
|
||||
int64_t AlsaPlayer::GetEndPosition()
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
return ps->end_position;
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
return ps.end_position;
|
||||
}
|
||||
|
||||
|
||||
int64_t AlsaPlayer::GetCurrentPosition()
|
||||
{
|
||||
int64_t lastpos;
|
||||
|
@ -437,10 +431,10 @@ int64_t AlsaPlayer::GetCurrentPosition()
|
|||
int64_t samplerate;
|
||||
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
lastpos = ps->last_position;
|
||||
lasttime = ps->last_position_time;
|
||||
samplerate = ps->provider->GetSampleRate();
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
lastpos = ps.last_position;
|
||||
lasttime = ps.last_position_time;
|
||||
samplerate = ps.provider->GetSampleRate();
|
||||
}
|
||||
|
||||
timespec now;
|
||||
|
@ -460,10 +454,16 @@ int64_t AlsaPlayer::GetCurrentPosition()
|
|||
|
||||
void AlsaPlayer::SetVolume(double vol)
|
||||
{
|
||||
PthreadMutexLocker ml(ps->mutex);
|
||||
ps->volume = vol;
|
||||
ps->signal_volume = true;
|
||||
pthread_cond_signal(&ps->cond);
|
||||
PthreadMutexLocker ml(ps.mutex);
|
||||
ps.volume = vol;
|
||||
ps.signal_volume = true;
|
||||
pthread_cond_signal(&ps.cond);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(AudioProvider *provider)
|
||||
{
|
||||
return agi::util::make_unique<AlsaPlayer>(provider);
|
||||
}
|
||||
|
||||
#endif // WITH_ALSA
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright (c) 2011, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file audio_player_alsa.h
|
||||
/// @see audio_player_alsa.cpp
|
||||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
#ifdef WITH_ALSA
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <memory>
|
||||
|
||||
struct PlaybackState;
|
||||
|
||||
class AlsaPlayer final : public AudioPlayer {
|
||||
std::unique_ptr<PlaybackState> ps;
|
||||
pthread_t thread;
|
||||
|
||||
public:
|
||||
AlsaPlayer(AudioProvider *provider);
|
||||
~AlsaPlayer();
|
||||
|
||||
void Play(int64_t start, int64_t count);
|
||||
void Stop();
|
||||
bool IsPlaying();
|
||||
|
||||
int64_t GetStartPosition();
|
||||
int64_t GetEndPosition();
|
||||
int64_t GetCurrentPosition();
|
||||
void SetEndPosition(int64_t pos);
|
||||
void SetCurrentPosition(int64_t pos);
|
||||
|
||||
void SetVolume(double vol);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -35,28 +35,72 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef WITH_DIRECTSOUND
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_player_dsound.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <mmsystem.h>
|
||||
#include <dsound.h>
|
||||
|
||||
namespace {
|
||||
class DirectSoundPlayer;
|
||||
|
||||
class DirectSoundPlayerThread final : public wxThread {
|
||||
DirectSoundPlayer *parent;
|
||||
HANDLE stopnotify;
|
||||
|
||||
public:
|
||||
void Stop(); // Notify thread to stop audio playback. Thread safe.
|
||||
DirectSoundPlayerThread(DirectSoundPlayer *parent);
|
||||
~DirectSoundPlayerThread();
|
||||
|
||||
wxThread::ExitCode Entry();
|
||||
};
|
||||
|
||||
class DirectSoundPlayer final : public AudioPlayer {
|
||||
friend class DirectSoundPlayerThread;
|
||||
|
||||
volatile bool playing = false;
|
||||
float volume = 1.0f;
|
||||
int offset = 0;
|
||||
|
||||
DWORD bufSize = 0;
|
||||
volatile int64_t playPos = 0;
|
||||
int64_t startPos = 0;
|
||||
volatile int64_t endPos = 0;
|
||||
DWORD startTime = 0;
|
||||
|
||||
IDirectSound8 *directSound = nullptr;
|
||||
IDirectSoundBuffer8 *buffer = nullptr;
|
||||
|
||||
bool FillBuffer(bool fill);
|
||||
DirectSoundPlayerThread *thread = nullptr;
|
||||
|
||||
public:
|
||||
DirectSoundPlayer(AudioProvider *provider);
|
||||
~DirectSoundPlayer();
|
||||
|
||||
void Play(int64_t start,int64_t count);
|
||||
void Stop();
|
||||
|
||||
bool IsPlaying() { return playing; }
|
||||
|
||||
int64_t GetEndPosition() { return endPos; }
|
||||
int64_t GetCurrentPosition();
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
void SetVolume(double vol) { volume = vol; }
|
||||
};
|
||||
|
||||
DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider)
|
||||
: AudioPlayer(provider)
|
||||
, playing(false)
|
||||
, volume(1.0f)
|
||||
, offset(0)
|
||||
, bufSize(0)
|
||||
, playPos(0)
|
||||
, startPos(0)
|
||||
, endPos(0)
|
||||
, startTime(0)
|
||||
, directSound(0)
|
||||
, buffer(0)
|
||||
, thread(0)
|
||||
{
|
||||
// Initialize the DirectSound object
|
||||
HRESULT res;
|
||||
|
@ -105,23 +149,13 @@ DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider)
|
|||
DirectSoundPlayer::~DirectSoundPlayer() {
|
||||
Stop();
|
||||
|
||||
// Unref the DirectSound buffer
|
||||
if (buffer) {
|
||||
if (buffer)
|
||||
buffer->Release();
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
// Unref the DirectSound object
|
||||
if (directSound) {
|
||||
if (directSound)
|
||||
directSound->Release();
|
||||
directSound = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Fill buffer
|
||||
/// @param fill
|
||||
/// @return
|
||||
///
|
||||
bool DirectSoundPlayer::FillBuffer(bool fill) {
|
||||
if (playPos >= endPos) return false;
|
||||
|
||||
|
@ -181,7 +215,6 @@ RetryLock:
|
|||
goto RetryLock;
|
||||
}
|
||||
|
||||
// Error
|
||||
if (FAILED(res)) return false;
|
||||
|
||||
// Convert size to number of samples
|
||||
|
@ -197,19 +230,13 @@ RetryLock:
|
|||
if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
|
||||
playPos += count1+count2;
|
||||
|
||||
// Unlock
|
||||
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
|
||||
|
||||
// Update offset
|
||||
offset = (offset + count1*bytesps + count2*bytesps) % bufSize;
|
||||
|
||||
return playPos < endPos;
|
||||
}
|
||||
|
||||
/// @brief Play
|
||||
/// @param start
|
||||
/// @param count
|
||||
///
|
||||
void DirectSoundPlayer::Play(int64_t start,int64_t count) {
|
||||
// Make sure that it's stopped
|
||||
Stop();
|
||||
|
@ -245,9 +272,6 @@ void DirectSoundPlayer::Play(int64_t start,int64_t count) {
|
|||
startTime = GetTickCount();
|
||||
}
|
||||
|
||||
/// @brief Stop
|
||||
/// @param timerToo
|
||||
///
|
||||
void DirectSoundPlayer::Stop() {
|
||||
// Stop the thread
|
||||
if (thread) {
|
||||
|
@ -259,7 +283,6 @@ void DirectSoundPlayer::Stop() {
|
|||
}
|
||||
// The thread is now guaranteed dead and there are no concurrency problems to worry about
|
||||
|
||||
// Stop
|
||||
if (buffer) buffer->Stop(); // the thread should have done this already
|
||||
|
||||
// Reset variables
|
||||
|
@ -270,16 +293,10 @@ void DirectSoundPlayer::Stop() {
|
|||
offset = 0;
|
||||
}
|
||||
|
||||
/// @brief Set end
|
||||
/// @param pos
|
||||
///
|
||||
void DirectSoundPlayer::SetEndPosition(int64_t pos) {
|
||||
if (playing) endPos = pos;
|
||||
}
|
||||
|
||||
/// @brief Get current position
|
||||
/// @return
|
||||
///
|
||||
int64_t DirectSoundPlayer::GetCurrentPosition() {
|
||||
// Check if buffer is loaded
|
||||
if (!buffer || !playing) return 0;
|
||||
|
@ -291,23 +308,15 @@ int64_t DirectSoundPlayer::GetCurrentPosition() {
|
|||
return startPos + tdiff * provider->GetSampleRate() / 1000;
|
||||
}
|
||||
|
||||
/// @brief Thread constructor
|
||||
/// @param par
|
||||
///
|
||||
DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
|
||||
parent = par;
|
||||
stopnotify = CreateEvent(nullptr, true, false, nullptr);
|
||||
}
|
||||
|
||||
/// @brief Thread destructor
|
||||
///
|
||||
DirectSoundPlayerThread::~DirectSoundPlayerThread() {
|
||||
CloseHandle(stopnotify);
|
||||
}
|
||||
|
||||
/// @brief Thread entry point
|
||||
/// @return
|
||||
///
|
||||
wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
||||
CoInitialize(0);
|
||||
|
||||
|
@ -355,12 +364,15 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// @brief Stop playback thread
|
||||
///
|
||||
void DirectSoundPlayerThread::Stop() {
|
||||
// Increase the stopnotify by one, causing a wait for it to succeed
|
||||
SetEvent(stopnotify);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *provider) {
|
||||
return agi::util::make_unique<DirectSoundPlayer>(provider);
|
||||
}
|
||||
|
||||
#endif // WITH_DIRECTSOUND
|
||||
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file audio_player_dsound.h
|
||||
/// @see audio_player_dsound.cpp
|
||||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
#ifdef WITH_DIRECTSOUND
|
||||
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include <dsound.h>
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
||||
class DirectSoundPlayer;
|
||||
|
||||
class DirectSoundPlayerThread final : public wxThread {
|
||||
DirectSoundPlayer *parent;
|
||||
HANDLE stopnotify;
|
||||
|
||||
public:
|
||||
void Stop(); // Notify thread to stop audio playback. Thread safe.
|
||||
DirectSoundPlayerThread(DirectSoundPlayer *parent);
|
||||
~DirectSoundPlayerThread();
|
||||
|
||||
wxThread::ExitCode Entry();
|
||||
};
|
||||
|
||||
class DirectSoundPlayer final : public AudioPlayer {
|
||||
friend class DirectSoundPlayerThread;
|
||||
|
||||
volatile bool playing;
|
||||
float volume;
|
||||
int offset;
|
||||
|
||||
DWORD bufSize;
|
||||
volatile int64_t playPos;
|
||||
int64_t startPos;
|
||||
volatile int64_t endPos;
|
||||
DWORD startTime;
|
||||
|
||||
IDirectSound8 *directSound;
|
||||
IDirectSoundBuffer8 *buffer;
|
||||
|
||||
bool FillBuffer(bool fill);
|
||||
DirectSoundPlayerThread *thread;
|
||||
|
||||
public:
|
||||
DirectSoundPlayer(AudioProvider *provider);
|
||||
~DirectSoundPlayer();
|
||||
|
||||
void Play(int64_t start,int64_t count);
|
||||
void Stop();
|
||||
|
||||
bool IsPlaying() { return playing; }
|
||||
|
||||
int64_t GetEndPosition() { return endPos; }
|
||||
int64_t GetCurrentPosition();
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
void SetVolume(double vol) { volume = vol; }
|
||||
};
|
||||
#endif
|
|
@ -35,7 +35,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef WITH_DIRECTSOUND
|
||||
#include "audio_player_dsound2.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
@ -52,6 +52,68 @@
|
|||
#include <process.h>
|
||||
#include <dsound.h>
|
||||
|
||||
namespace {
|
||||
class DirectSoundPlayer2Thread;
|
||||
|
||||
/// @class DirectSoundPlayer2
|
||||
/// @brief New implementation of DirectSound-based audio player
|
||||
///
|
||||
/// The core design idea is to have a playback thread that owns the DirectSound COM objects
|
||||
/// and performs all playback operations, and use the player object as a proxy to
|
||||
/// send commands to the playback thread.
|
||||
class DirectSoundPlayer2 final : public AudioPlayer {
|
||||
/// The playback thread
|
||||
std::unique_ptr<DirectSoundPlayer2Thread> thread;
|
||||
|
||||
/// Desired length in milliseconds to write ahead of the playback cursor
|
||||
int WantedLatency;
|
||||
|
||||
/// Multiplier for WantedLatency to get total buffer length
|
||||
int BufferLength;
|
||||
|
||||
/// @brief Tell whether playback thread is alive
|
||||
/// @return True if there is a playback thread and it's ready
|
||||
bool IsThreadAlive();
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
DirectSoundPlayer2(AudioProvider *provider);
|
||||
/// @brief Destructor
|
||||
~DirectSoundPlayer2();
|
||||
|
||||
/// @brief Start playback
|
||||
/// @param start First audio frame to play
|
||||
/// @param count Number of audio frames to play
|
||||
void Play(int64_t start,int64_t count);
|
||||
|
||||
/// @brief Stop audio playback
|
||||
/// @param timerToo Whether to also stop the playback update timer
|
||||
void Stop();
|
||||
|
||||
/// @brief Tell whether playback is active
|
||||
/// @return True if audio is playing back
|
||||
bool IsPlaying();
|
||||
|
||||
/// @brief Get playback end position
|
||||
/// @return Audio frame index
|
||||
///
|
||||
/// Returns 0 if playback is stopped or there is no playback thread
|
||||
int64_t GetEndPosition();
|
||||
/// @brief Get approximate playback position
|
||||
/// @return Index of audio frame user is currently hearing
|
||||
///
|
||||
/// Returns 0 if playback is stopped or there is no playback thread
|
||||
int64_t GetCurrentPosition();
|
||||
|
||||
/// @brief Change playback end position
|
||||
/// @param pos New end position
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
/// @brief Change playback volume
|
||||
/// @param vol Amplification factor
|
||||
void SetVolume(double vol);
|
||||
};
|
||||
|
||||
/// @brief RAII support class to init and de-init the COM library
|
||||
struct COMInitialization {
|
||||
|
||||
|
@ -157,7 +219,6 @@ class DirectSoundPlayer2Thread {
|
|||
/// @brief Check for error state and throw exception if one occurred
|
||||
void CheckError();
|
||||
|
||||
|
||||
/// Win32 handle to the thread
|
||||
Win32KernelHandle thread_handle;
|
||||
|
||||
|
@ -186,16 +247,16 @@ class DirectSoundPlayer2Thread {
|
|||
Win32KernelHandle error_happened;
|
||||
|
||||
/// Statically allocated error message text describing reason for error_happened being set
|
||||
const char *error_message;
|
||||
const char *error_message = nullptr;
|
||||
|
||||
/// Playback volume, 1.0 is "unchanged"
|
||||
double volume;
|
||||
double volume = 1.0;
|
||||
|
||||
/// Audio frame to start playback at
|
||||
int64_t start_frame;
|
||||
int64_t start_frame = 0;
|
||||
|
||||
/// Audio frame to end playback at
|
||||
int64_t end_frame;
|
||||
int64_t end_frame = 0;
|
||||
|
||||
|
||||
/// Desired length in milliseconds to write ahead of the playback cursor
|
||||
|
@ -646,11 +707,6 @@ DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int
|
|||
, buffer_length(BufferLength)
|
||||
, provider(provider)
|
||||
{
|
||||
error_message = 0;
|
||||
volume = 1.0;
|
||||
start_frame = 0;
|
||||
end_frame = 0;
|
||||
|
||||
thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);
|
||||
|
||||
if (!thread_handle)
|
||||
|
@ -904,5 +960,10 @@ void DirectSoundPlayer2::SetVolume(double vol)
|
|||
LOG_E("audio/player/dsound") << msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreateDirectSound2Player(AudioProvider *provider) {
|
||||
return agi::util::make_unique<DirectSoundPlayer2>(provider);
|
||||
}
|
||||
|
||||
#endif // WITH_DIRECTSOUND
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
// Copyright (c) 2008, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file audio_player_dsound2.h
|
||||
/// @see audio_player_dsound2.cpp
|
||||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
#ifdef WITH_DIRECTSOUND
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
class DirectSoundPlayer2Thread;
|
||||
|
||||
/// @class DirectSoundPlayer2
|
||||
/// @brief New implementation of DirectSound-based audio player
|
||||
///
|
||||
/// The core design idea is to have a playback thread that owns the DirectSound COM objects
|
||||
/// and performs all playback operations, and use the player object as a proxy to
|
||||
/// send commands to the playback thread.
|
||||
class DirectSoundPlayer2 final : public AudioPlayer {
|
||||
/// The playback thread
|
||||
std::unique_ptr<DirectSoundPlayer2Thread> thread;
|
||||
|
||||
/// Desired length in milliseconds to write ahead of the playback cursor
|
||||
int WantedLatency;
|
||||
|
||||
/// Multiplier for WantedLatency to get total buffer length
|
||||
int BufferLength;
|
||||
|
||||
/// @brief Tell whether playback thread is alive
|
||||
/// @return True if there is a playback thread and it's ready
|
||||
bool IsThreadAlive();
|
||||
|
||||
public:
|
||||
/// @brief Constructor
|
||||
DirectSoundPlayer2(AudioProvider *provider);
|
||||
/// @brief Destructor
|
||||
~DirectSoundPlayer2();
|
||||
|
||||
/// @brief Start playback
|
||||
/// @param start First audio frame to play
|
||||
/// @param count Number of audio frames to play
|
||||
void Play(int64_t start,int64_t count);
|
||||
|
||||
/// @brief Stop audio playback
|
||||
/// @param timerToo Whether to also stop the playback update timer
|
||||
void Stop();
|
||||
|
||||
/// @brief Tell whether playback is active
|
||||
/// @return True if audio is playing back
|
||||
bool IsPlaying();
|
||||
|
||||
/// @brief Get playback end position
|
||||
/// @return Audio frame index
|
||||
///
|
||||
/// Returns 0 if playback is stopped or there is no playback thread
|
||||
int64_t GetEndPosition();
|
||||
/// @brief Get approximate playback position
|
||||
/// @return Index of audio frame user is currently hearing
|
||||
///
|
||||
/// Returns 0 if playback is stopped or there is no playback thread
|
||||
int64_t GetCurrentPosition();
|
||||
|
||||
/// @brief Change playback end position
|
||||
/// @param pos New end position
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
/// @brief Change playback volume
|
||||
/// @param vol Amplification factor
|
||||
void SetVolume(double vol);
|
||||
};
|
||||
#endif
|
|
@ -35,14 +35,29 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef WITH_OPENAL
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_player_openal.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <wx/timer.h>
|
||||
|
||||
// Auto-link to OpenAL lib for MSVC
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "openal32.lib")
|
||||
|
@ -50,6 +65,65 @@
|
|||
|
||||
DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal")
|
||||
|
||||
namespace {
|
||||
class OpenALPlayer final : public AudioPlayer, wxTimer {
|
||||
/// Number of OpenAL buffers to use
|
||||
static const ALsizei num_buffers = 8;
|
||||
|
||||
bool playing = false; ///< Is audio currently playing?
|
||||
|
||||
float volume = 1.f; ///< Current audio volume
|
||||
ALsizei samplerate; ///< Sample rate of the audio
|
||||
int bpf; ///< Bytes per frame
|
||||
|
||||
int64_t start_frame = 0; ///< First frame of playbacka
|
||||
int64_t cur_frame = 0; ///< Next frame to write to playback buffers
|
||||
int64_t end_frame = 0; ///< Last frame to play
|
||||
|
||||
ALCdevice *device = nullptr; ///< OpenAL device handle
|
||||
ALCcontext *context = nullptr; ///< OpenAL sound context
|
||||
ALuint buffers[num_buffers]; ///< OpenAL sound buffers
|
||||
ALuint source = 0; ///< OpenAL playback source
|
||||
|
||||
/// Index into buffers, first free (unqueued) buffer to be filled
|
||||
ALsizei buf_first_free = 0;
|
||||
|
||||
/// Index into buffers, first queued (non-free) buffer
|
||||
ALsizei buf_first_queued = 0;
|
||||
|
||||
/// Number of free buffers
|
||||
ALsizei buffers_free = 0;
|
||||
|
||||
/// Number of buffers which have been fully played since playback was last started
|
||||
ALsizei buffers_played = 0;
|
||||
|
||||
wxStopWatch playback_segment_timer;
|
||||
|
||||
/// Buffer to decode audio into
|
||||
std::vector<char> decode_buffer;
|
||||
|
||||
/// Fill count OpenAL buffers
|
||||
void FillBuffers(ALsizei count);
|
||||
|
||||
protected:
|
||||
/// wxTimer override to periodically fill available buffers
|
||||
void Notify() override;
|
||||
|
||||
public:
|
||||
OpenALPlayer(AudioProvider *provider);
|
||||
~OpenALPlayer();
|
||||
|
||||
void Play(int64_t start,int64_t count) override;
|
||||
void Stop() override;
|
||||
bool IsPlaying() override { return playing; }
|
||||
|
||||
int64_t GetEndPosition() override { return end_frame; }
|
||||
int64_t GetCurrentPosition() override;
|
||||
void SetEndPosition(int64_t pos) override;
|
||||
|
||||
void SetVolume(double vol) override { volume = vol; }
|
||||
};
|
||||
|
||||
OpenALPlayer::OpenALPlayer(AudioProvider *provider)
|
||||
: AudioPlayer(provider)
|
||||
, samplerate(provider->GetSampleRate())
|
||||
|
@ -210,5 +284,11 @@ int64_t OpenALPlayer::GetCurrentPosition()
|
|||
long extra = playback_segment_timer.Time();
|
||||
return buffers_played * decode_buffer.size() / bpf + start_frame + extra * samplerate / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreateOpenALPlayer(AudioProvider *provider)
|
||||
{
|
||||
return agi::util::make_unique<OpenALPlayer>(provider);
|
||||
}
|
||||
|
||||
#endif // WITH_OPENAL
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright (c) 2007, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file audio_player_openal.h
|
||||
/// @see audio_player_openal.cpp
|
||||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
#ifdef WITH_OPENAL
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wx/timer.h>
|
||||
|
||||
class OpenALPlayer final : public AudioPlayer, wxTimer {
|
||||
/// Number of OpenAL buffers to use
|
||||
static const ALsizei num_buffers = 8;
|
||||
|
||||
bool playing = false; ///< Is audio currently playing?
|
||||
|
||||
float volume = 1.f; ///< Current audio volume
|
||||
ALsizei samplerate; ///< Sample rate of the audio
|
||||
int bpf; ///< Bytes per frame
|
||||
|
||||
int64_t start_frame = 0; ///< First frame of playbacka
|
||||
int64_t cur_frame = 0; ///< Next frame to write to playback buffers
|
||||
int64_t end_frame = 0; ///< Last frame to play
|
||||
|
||||
ALCdevice *device = nullptr; ///< OpenAL device handle
|
||||
ALCcontext *context = nullptr; ///< OpenAL sound context
|
||||
ALuint buffers[num_buffers]; ///< OpenAL sound buffers
|
||||
ALuint source = 0; ///< OpenAL playback source
|
||||
|
||||
/// Index into buffers, first free (unqueued) buffer to be filled
|
||||
ALsizei buf_first_free = 0;
|
||||
|
||||
/// Index into buffers, first queued (non-free) buffer
|
||||
ALsizei buf_first_queued = 0;
|
||||
|
||||
/// Number of free buffers
|
||||
ALsizei buffers_free = 0;
|
||||
|
||||
/// Number of buffers which have been fully played since playback was last started
|
||||
ALsizei buffers_played = 0;
|
||||
|
||||
wxStopWatch playback_segment_timer;
|
||||
|
||||
/// Buffer to decode audio into
|
||||
std::vector<char> decode_buffer;
|
||||
|
||||
/// Fill count OpenAL buffers
|
||||
void FillBuffers(ALsizei count);
|
||||
|
||||
protected:
|
||||
/// wxTimer override to periodically fill available buffers
|
||||
void Notify() override;
|
||||
|
||||
public:
|
||||
OpenALPlayer(AudioProvider *provider);
|
||||
~OpenALPlayer();
|
||||
|
||||
void Play(int64_t start,int64_t count) override;
|
||||
void Stop() override;
|
||||
bool IsPlaying() override { return playing; }
|
||||
|
||||
int64_t GetEndPosition() override { return end_frame; }
|
||||
int64_t GetCurrentPosition() override;
|
||||
void SetEndPosition(int64_t pos) override;
|
||||
|
||||
void SetVolume(double vol) override { volume = vol; }
|
||||
};
|
||||
#endif
|
|
@ -33,12 +33,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef WITH_OSS
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_player_oss.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
|
@ -46,19 +41,112 @@
|
|||
#include "options.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
#ifdef HAVE_SOUNDCARD_H
|
||||
# include <soundcard.h>
|
||||
#elif defined(HAVE_SYS_SOUNDCARD_H)
|
||||
# include <sys/soundcard.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss")
|
||||
class OSSPlayerThread;
|
||||
|
||||
OSSPlayer::OSSPlayer(AudioProvider *provider)
|
||||
: AudioPlayer(provider)
|
||||
{
|
||||
OpenStream();
|
||||
}
|
||||
class OSSPlayer final : public AudioPlayer {
|
||||
friend class OSSPlayerThread;
|
||||
|
||||
OSSPlayer::~OSSPlayer()
|
||||
{
|
||||
Stop();
|
||||
::close(dspdev);
|
||||
}
|
||||
/// sample rate of audio
|
||||
unsigned int rate = 0;
|
||||
|
||||
/// Worker thread that does the actual writing
|
||||
OSSPlayerThread *thread = nullptr;
|
||||
|
||||
/// Is the player currently playing?
|
||||
volatile bool playing = false;
|
||||
|
||||
/// Current volume level
|
||||
volatile float volume = 1.f;
|
||||
|
||||
/// first frame of playback
|
||||
volatile unsigned long start_frame = 0;
|
||||
|
||||
/// last written frame + 1
|
||||
volatile unsigned long cur_frame = 0;
|
||||
|
||||
/// last frame to play
|
||||
volatile unsigned long end_frame = 0;
|
||||
|
||||
/// bytes per frame
|
||||
unsigned long bpf = 0;
|
||||
|
||||
/// OSS audio device handle
|
||||
volatile int dspdev = 0;
|
||||
|
||||
void OpenStream();
|
||||
|
||||
public:
|
||||
OSSPlayer(AudioProvider *provider)
|
||||
: AudioPlayer(provider)
|
||||
{
|
||||
OpenStream();
|
||||
}
|
||||
|
||||
~OSSPlayer() {
|
||||
Stop();
|
||||
::close(dspdev);
|
||||
}
|
||||
|
||||
|
||||
void Play(int64_t start, int64_t count);
|
||||
void Stop();
|
||||
bool IsPlaying() { return playing; }
|
||||
|
||||
int64_t GetEndPosition() { return end_frame; }
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
int64_t GetCurrentPosition();
|
||||
|
||||
void SetVolume(double vol) { volume = vol; }
|
||||
};
|
||||
|
||||
/// Worker thread to asynchronously write audio data to the output device
|
||||
class OSSPlayerThread final : public wxThread {
|
||||
/// Parent player
|
||||
OSSPlayer *parent;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param parent Player to get audio data and playback state from
|
||||
OSSPlayerThread(OSSPlayer *parent) : wxThread(wxTHREAD_JOINABLE) , parent(parent) { }
|
||||
|
||||
/// Main thread entry point
|
||||
wxThread::ExitCode Entry() {
|
||||
// Use small enough writes for good timing accuracy with all
|
||||
// timing methods.
|
||||
const unsigned long wsize = parent->rate / 25;
|
||||
void *buf = malloc(wsize * parent->bpf);
|
||||
|
||||
while (!TestDestroy() && parent->cur_frame < parent->end_frame) {
|
||||
int rsize = std::min(wsize, parent->end_frame - parent->cur_frame);
|
||||
parent->provider->GetAudioWithVolume(buf, parent->cur_frame,
|
||||
rsize, parent->volume);
|
||||
int written = ::write(parent->dspdev, buf, rsize * parent->bpf);
|
||||
parent->cur_frame += written / parent->bpf;
|
||||
}
|
||||
free(buf);
|
||||
parent->cur_frame = parent->end_frame;
|
||||
|
||||
LOG_D("player/audio/oss") << "Thread dead";
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
void OSSPlayer::OpenStream()
|
||||
{
|
||||
|
@ -193,31 +281,10 @@ int64_t OSSPlayer::GetCurrentPosition()
|
|||
// Return the last written frame, timing will suffer
|
||||
return cur_frame;
|
||||
}
|
||||
|
||||
OSSPlayerThread::OSSPlayerThread(OSSPlayer *par)
|
||||
: wxThread(wxTHREAD_JOINABLE)
|
||||
, parent(par)
|
||||
{
|
||||
}
|
||||
|
||||
wxThread::ExitCode OSSPlayerThread::Entry() {
|
||||
// Use small enough writes for good timing accuracy with all
|
||||
// timing methods.
|
||||
const unsigned long wsize = parent->rate / 25;
|
||||
void *buf = malloc(wsize * parent->bpf);
|
||||
|
||||
while (!TestDestroy() && parent->cur_frame < parent->end_frame) {
|
||||
int rsize = std::min(wsize, parent->end_frame - parent->cur_frame);
|
||||
parent->provider->GetAudioWithVolume(buf, parent->cur_frame,
|
||||
rsize, parent->volume);
|
||||
int written = ::write(parent->dspdev, buf, rsize * parent->bpf);
|
||||
parent->cur_frame += written / parent->bpf;
|
||||
}
|
||||
free(buf);
|
||||
parent->cur_frame = parent->end_frame;
|
||||
|
||||
LOG_D("player/audio/oss") << "Thread dead";
|
||||
return 0;
|
||||
std::unique_ptr<AudioPlayer> CreateOSSPlayer(AudioProvider *provider) {
|
||||
return agi::util::make_unique<OSSPlayer>(provider);
|
||||
}
|
||||
|
||||
#endif // WITH_OSS
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright (c) 2009, Grigori Goronzy
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file audio_player_oss.h
|
||||
/// @see audio_player_oss.cpp
|
||||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
#ifdef WITH_OSS
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <wx/thread.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_SOUNDCARD_H
|
||||
# include <soundcard.h>
|
||||
#else
|
||||
# ifdef HAVE_SYS_SOUNDCARD_H
|
||||
# include <sys/soundcard.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
class AudioProvider;
|
||||
class OSSPlayer;
|
||||
|
||||
/// Worker thread to asynchronously write audio data to the output device
|
||||
class OSSPlayerThread final : public wxThread {
|
||||
/// Parent player
|
||||
OSSPlayer *parent;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param parent Player to get audio data and playback state from
|
||||
OSSPlayerThread(OSSPlayer *parent);
|
||||
|
||||
/// Main thread entry point
|
||||
wxThread::ExitCode Entry();
|
||||
};
|
||||
|
||||
class OSSPlayer final : public AudioPlayer {
|
||||
friend class OSSPlayerThread;
|
||||
|
||||
/// sample rate of audio
|
||||
unsigned int rate = 0;
|
||||
|
||||
/// Worker thread that does the actual writing
|
||||
OSSPlayerThread *thread = nullptr;
|
||||
|
||||
/// Is the player currently playing?
|
||||
volatile bool playing = false;
|
||||
|
||||
/// Current volume level
|
||||
volatile float volume = 1.f;
|
||||
|
||||
/// first frame of playback
|
||||
volatile unsigned long start_frame = 0;
|
||||
|
||||
/// last written frame + 1
|
||||
volatile unsigned long cur_frame = 0;
|
||||
|
||||
/// last frame to play
|
||||
volatile unsigned long end_frame = 0;
|
||||
|
||||
/// bytes per frame
|
||||
unsigned long bpf = 0;
|
||||
|
||||
/// OSS audio device handle
|
||||
volatile int dspdev = 0;
|
||||
|
||||
void OpenStream();
|
||||
|
||||
public:
|
||||
OSSPlayer(AudioProvider *provider);
|
||||
~OSSPlayer();
|
||||
|
||||
void Play(int64_t start, int64_t count);
|
||||
void Stop();
|
||||
bool IsPlaying() { return playing; }
|
||||
|
||||
int64_t GetEndPosition() { return end_frame; }
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
int64_t GetCurrentPosition();
|
||||
|
||||
void SetVolume(double vol) { volume = vol; }
|
||||
};
|
||||
#endif
|
|
@ -32,13 +32,9 @@
|
|||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_PORTAUDIO
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "audio_player_portaudio.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
|
@ -47,6 +43,9 @@
|
|||
#include "options.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
DEFINE_SIMPLE_EXCEPTION(PortAudioError, agi::AudioPlayerOpenError, "audio/player/open/portaudio")
|
||||
|
||||
// Uncomment to enable extremely spammy debug logging
|
||||
|
@ -284,4 +283,8 @@ bool PortAudioPlayer::IsPlaying() {
|
|||
return !!Pa_IsStreamActive(stream);
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(AudioProvider *provider) {
|
||||
return agi::util::make_unique<PortAudioPlayer>(provider);
|
||||
}
|
||||
|
||||
#endif // WITH_PORTAUDIO
|
||||
|
|
|
@ -35,18 +35,74 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef WITH_LIBPULSE
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <wx/thread.h>
|
||||
|
||||
#include "audio_player_pulse.h"
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
namespace {
|
||||
class PulseAudioPlayer final : public AudioPlayer {
|
||||
float volume = 1.f;
|
||||
bool is_playing = false;
|
||||
|
||||
volatile unsigned long start_frame = 0;
|
||||
volatile unsigned long cur_frame = 0;
|
||||
volatile unsigned long end_frame = 0;
|
||||
|
||||
unsigned long bpf = 0; // bytes per frame
|
||||
|
||||
wxSemaphore context_notify{0, 1};
|
||||
wxSemaphore context_success{0, 1};
|
||||
volatile int context_success_val;
|
||||
|
||||
wxSemaphore stream_notify{0, 1};
|
||||
wxSemaphore stream_success{0, 1};
|
||||
volatile int stream_success_val;
|
||||
|
||||
pa_threaded_mainloop *mainloop = nullptr; // pulseaudio mainloop handle
|
||||
pa_context *context = nullptr; // connection context
|
||||
volatile pa_context_state_t cstate;
|
||||
|
||||
pa_stream *stream = nullptr;
|
||||
volatile pa_stream_state_t sstate;
|
||||
|
||||
volatile pa_usec_t play_start_time; // timestamp when playback was started
|
||||
|
||||
int paerror = 0;
|
||||
|
||||
/// Called by PA to notify about contetxt operation completion
|
||||
static void pa_context_success(pa_context *c, int success, PulseAudioPlayer *thread);
|
||||
/// Called by PA to notify about other context-related stuff
|
||||
static void pa_context_notify(pa_context *c, PulseAudioPlayer *thread);
|
||||
/// Called by PA when a stream operation completes
|
||||
static void pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread);
|
||||
/// Called by PA to request more data written to stream
|
||||
static void pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread);
|
||||
/// Called by PA to notify about other stream-related stuff
|
||||
static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread);
|
||||
|
||||
public:
|
||||
PulseAudioPlayer(AudioProvider *provider);
|
||||
~PulseAudioPlayer();
|
||||
|
||||
void Play(int64_t start,int64_t count);
|
||||
void Stop();
|
||||
bool IsPlaying() { return is_playing; }
|
||||
|
||||
int64_t GetEndPosition() { return end_frame; }
|
||||
int64_t GetCurrentPosition();
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
void SetVolume(double vol) { volume = vol; }
|
||||
};
|
||||
|
||||
PulseAudioPlayer::PulseAudioPlayer(AudioProvider *provider) : AudioPlayer(provider) {
|
||||
// Initialise a mainloop
|
||||
|
@ -277,5 +333,9 @@ void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread)
|
|||
thread->sstate = pa_stream_get_state(thread->stream);
|
||||
thread->stream_notify.Post();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(AudioProvider *provider) {
|
||||
return agi::util::make_unique<PulseAudioPlayer>(provider);
|
||||
}
|
||||
#endif // WITH_LIBPULSE
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
// Copyright (c) 2007, Niels Martin Hansen
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file audio_player_pulse.h
|
||||
/// @see audio_player_pulse.cpp
|
||||
/// @ingroup audio_output
|
||||
///
|
||||
|
||||
#ifdef WITH_LIBPULSE
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
|
||||
class PulseAudioPlayer;
|
||||
|
||||
class PulseAudioPlayer final : public AudioPlayer {
|
||||
float volume = 1.f;
|
||||
bool is_playing = false;
|
||||
|
||||
volatile unsigned long start_frame = 0;
|
||||
volatile unsigned long cur_frame = 0;
|
||||
volatile unsigned long end_frame = 0;
|
||||
|
||||
unsigned long bpf = 0; // bytes per frame
|
||||
|
||||
|
||||
wxSemaphore context_notify{0, 1};
|
||||
wxSemaphore context_success{0, 1};
|
||||
volatile int context_success_val;
|
||||
|
||||
wxSemaphore stream_notify{0, 1};
|
||||
wxSemaphore stream_success{0, 1};
|
||||
volatile int stream_success_val;
|
||||
|
||||
pa_threaded_mainloop *mainloop = nullptr; // pulseaudio mainloop handle
|
||||
pa_context *context = nullptr; // connection context
|
||||
volatile pa_context_state_t cstate;
|
||||
|
||||
pa_stream *stream = nullptr;
|
||||
volatile pa_stream_state_t sstate;
|
||||
|
||||
volatile pa_usec_t play_start_time; // timestamp when playback was started
|
||||
|
||||
int paerror = 0;
|
||||
|
||||
/// Called by PA to notify about contetxt operation completion
|
||||
static void pa_context_success(pa_context *c, int success, PulseAudioPlayer *thread);
|
||||
/// Called by PA to notify about other context-related stuff
|
||||
static void pa_context_notify(pa_context *c, PulseAudioPlayer *thread);
|
||||
/// Called by PA when a stream operation completes
|
||||
static void pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread);
|
||||
/// Called by PA to request more data written to stream
|
||||
static void pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread);
|
||||
/// Called by PA to notify about other stream-related stuff
|
||||
static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread);
|
||||
|
||||
public:
|
||||
PulseAudioPlayer(AudioProvider *provider);
|
||||
~PulseAudioPlayer();
|
||||
|
||||
void Play(int64_t start,int64_t count);
|
||||
void Stop();
|
||||
bool IsPlaying() { return is_playing; }
|
||||
|
||||
int64_t GetEndPosition() { return end_frame; }
|
||||
int64_t GetCurrentPosition();
|
||||
void SetEndPosition(int64_t pos);
|
||||
|
||||
void SetVolume(double vol) { volume = vol; }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,65 +14,9 @@
|
|||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file factory_manager.h
|
||||
/// @brief Template/base-class for factory classes
|
||||
/// @ingroup utility
|
||||
///
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace agi { class ProgressSink; }
|
||||
|
||||
template<typename Base, typename... Args>
|
||||
class Factory {
|
||||
typedef Base *(*func)(Args const&...);
|
||||
typedef std::map<std::string, std::pair<bool, func>> map;
|
||||
|
||||
static map& classes() {
|
||||
static map classes;
|
||||
return classes;
|
||||
}
|
||||
|
||||
public:
|
||||
static std::vector<std::string> GetClasses(std::string favorite="") {
|
||||
std::vector<std::string> list;
|
||||
std::string cmp;
|
||||
for (auto& c : favorite) c = ::tolower(c);
|
||||
for (auto const& cls : classes()) {
|
||||
cmp.clear();
|
||||
std::transform(cls.first.begin(), cls.first.end(), std::back_inserter(cmp), ::tolower);
|
||||
if (cmp == favorite)
|
||||
list.insert(list.begin(), cls.first);
|
||||
else if (!cls.second.first)
|
||||
list.push_back(cls.first);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Base> Create(std::string const& name, Args const&... args) {
|
||||
auto factory = classes().find(name);
|
||||
if (factory == classes().end()) return nullptr;
|
||||
return std::unique_ptr<Base>(factory->second.second(args...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void Register(std::string name, bool hide = false, std::vector<std::string> subtypes = {}) {
|
||||
func factory = [](Args const&... args) -> Base * { return new T(args...); };
|
||||
if (subtypes.empty())
|
||||
classes().insert(std::make_pair(name, std::make_pair(hide, factory)));
|
||||
else {
|
||||
for (auto const& subtype : subtypes)
|
||||
classes().insert(std::make_pair(name + '/' + subtype, std::make_pair(hide, factory)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
template<typename Container>
|
||||
std::vector<std::string> GetClasses(Container const& c) {
|
||||
|
|
|
@ -34,7 +34,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "factory_manager.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AudioProvider;
|
||||
|
||||
|
@ -57,8 +60,7 @@ public:
|
|||
virtual void SetEndPosition(int64_t pos)=0;
|
||||
};
|
||||
|
||||
class AudioPlayerFactory final : public Factory<AudioPlayer, AudioProvider*> {
|
||||
public:
|
||||
static void RegisterProviders();
|
||||
struct AudioPlayerFactory {
|
||||
static std::vector<std::string> GetClasses();
|
||||
static std::unique_ptr<AudioPlayer> GetAudioPlayer(AudioProvider *provider);
|
||||
};
|
||||
|
|
|
@ -19,12 +19,10 @@
|
|||
/// @ingroup main_headers spelling
|
||||
///
|
||||
|
||||
#include "factory_manager.h"
|
||||
#include <memory>
|
||||
|
||||
namespace agi { class SpellChecker; }
|
||||
|
||||
class SpellCheckerFactory final : public Factory<agi::SpellChecker> {
|
||||
public:
|
||||
struct SpellCheckerFactory {
|
||||
static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
|
||||
static void RegisterProviders();
|
||||
};
|
||||
|
|
|
@ -34,21 +34,21 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "factory_manager.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AssFile;
|
||||
struct VideoFrame;
|
||||
|
||||
class SubtitlesProvider {
|
||||
public:
|
||||
virtual ~SubtitlesProvider() { };
|
||||
|
||||
virtual ~SubtitlesProvider() { }
|
||||
virtual void LoadSubtitles(AssFile *subs)=0;
|
||||
virtual void DrawSubtitles(VideoFrame &dst,double time)=0;
|
||||
virtual void DrawSubtitles(VideoFrame &dst, double time)=0;
|
||||
};
|
||||
|
||||
class SubtitlesProviderFactory final : public Factory<SubtitlesProvider, std::string> {
|
||||
public:
|
||||
struct SubtitlesProviderFactory {
|
||||
static std::unique_ptr<SubtitlesProvider> GetProvider();
|
||||
static void RegisterProviders();
|
||||
static std::vector<std::string> GetClasses();
|
||||
};
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "auto4_base.h"
|
||||
#include "auto4_lua_factory.h"
|
||||
#include "compat.h"
|
||||
#include "crash_writer.h"
|
||||
#include "export_fixstyle.h"
|
||||
|
@ -50,9 +51,9 @@
|
|||
#include "include/aegisub/context.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "options.h"
|
||||
#include "plugin_manager.h"
|
||||
#include "subs_controller.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "subtitles_provider_libass.h"
|
||||
#include "video_context.h"
|
||||
#include "version.h"
|
||||
#include "utils.h"
|
||||
|
@ -238,7 +239,8 @@ bool AegisubApp::OnInit() {
|
|||
exception_message = _("Oops, Aegisub has crashed!\n\nAn attempt has been made to save a copy of your file to:\n\n%s\n\nAegisub will now close.");
|
||||
|
||||
// Load plugins
|
||||
RegisterBuiltInPlugins();
|
||||
Automation4::ScriptFactory::Register(agi::util::make_unique<Automation4::LuaScriptFactory>());
|
||||
libass::CacheFonts();
|
||||
|
||||
// Load Automation scripts
|
||||
StartupLog("Load global Automation scripts");
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2008, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file plugin_manager.cpp
|
||||
/// @brief Keep track of and set up variable parts of the application
|
||||
/// @ingroup main
|
||||
///
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "include/aegisub/audio_player.h"
|
||||
#include "include/aegisub/spellchecker.h"
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
#include "plugin_manager.h"
|
||||
#include "auto4_lua_factory.h"
|
||||
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
void RegisterBuiltInPlugins() {
|
||||
AudioPlayerFactory::RegisterProviders();
|
||||
SubtitlesProviderFactory::RegisterProviders();
|
||||
SpellCheckerFactory::RegisterProviders();
|
||||
Automation4::ScriptFactory::Register(agi::util::make_unique<Automation4::LuaScriptFactory>());
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) 2008, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
void RegisterBuiltInPlugins();
|
|
@ -1,64 +1,30 @@
|
|||
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file spellchecker.cpp
|
||||
/// @brief Implementatin of base-class for spell checkers
|
||||
/// @ingroup spelling
|
||||
///
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "include/aegisub/spellchecker.h"
|
||||
#include "spellchecker_hunspell.h"
|
||||
|
||||
#include "options.h"
|
||||
|
||||
#include <libaegisub/spellchecker.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
std::unique_ptr<agi::SpellChecker> SpellCheckerFactory::GetSpellChecker() {
|
||||
std::vector<std::string> list = GetClasses(OPT_GET("Tool/Spell Checker/Backend")->GetString());
|
||||
if (list.empty()) return nullptr;
|
||||
|
||||
std::string error;
|
||||
for (auto const& name : list) {
|
||||
try {
|
||||
auto checker = Create(name);
|
||||
if (checker) return checker;
|
||||
}
|
||||
catch (...) { error += name + " factory: Unknown error\n"; }
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
void SpellCheckerFactory::RegisterProviders() {
|
||||
#ifdef WITH_HUNSPELL
|
||||
Register<HunspellSpellChecker>("Hunspell");
|
||||
return agi::util::make_unique<HunspellSpellChecker>();
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,71 +1,67 @@
|
|||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file subtitles_provider.cpp
|
||||
/// @brief Base class for subtitle renderers
|
||||
/// @ingroup subtitle_rendering
|
||||
///
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
|
||||
#include "factory_manager.h"
|
||||
#include "options.h"
|
||||
#include "subtitles_provider_csri.h"
|
||||
#include "subtitles_provider_libass.h"
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
|
||||
namespace {
|
||||
struct factory {
|
||||
std::string name;
|
||||
std::string subtype;
|
||||
std::unique_ptr<SubtitlesProvider> (*create)(std::string const& subtype);
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
std::vector<factory> const& factories() {
|
||||
static std::vector<factory> factories;
|
||||
if (factories.size()) return factories;
|
||||
#ifdef WITH_CSRI
|
||||
for (auto const& subtype : csri::List())
|
||||
factories.push_back(factory{"CSRI/" + subtype, subtype, csri::Create, false});
|
||||
#endif
|
||||
factories.push_back(factory{"libass", "", libass::Create, false});
|
||||
return factories;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> SubtitlesProviderFactory::GetClasses() {
|
||||
return ::GetClasses(factories());
|
||||
}
|
||||
|
||||
std::unique_ptr<SubtitlesProvider> SubtitlesProviderFactory::GetProvider() {
|
||||
std::vector<std::string> list = GetClasses(OPT_GET("Subtitle/Provider")->GetString());
|
||||
if (list.empty()) throw std::string("No subtitle providers are available.");
|
||||
auto preferred = OPT_GET("Subtitle/Provider")->GetString();
|
||||
auto sorted = GetSorted(factories(), preferred);
|
||||
|
||||
std::string error;
|
||||
for (auto const& factory : list) {
|
||||
for (auto factory : sorted) {
|
||||
try {
|
||||
size_t pos = factory.find('/');
|
||||
std::string subType = pos < factory.size() - 1 ? factory.substr(pos + 1) : "";
|
||||
auto provider = Create(factory, subType);
|
||||
auto provider = factory->create(factory->subtype);
|
||||
if (provider) return provider;
|
||||
}
|
||||
catch (agi::UserCancelException const&) { throw; }
|
||||
catch (std::string const& err) { error += factory + " factory: " + err + "\n"; }
|
||||
catch (const char *err) { error += factory + " factory: " + std::string(err) + "\n"; }
|
||||
catch (...) { error += factory + " factory: Unknown error\n"; }
|
||||
catch (std::string const& err) { error += factory->name + ": " + err + "\n"; }
|
||||
catch (const char *err) { error += factory->name + ": " + std::string(err) + "\n"; }
|
||||
catch (...) { error += factory->name + ": Unknown error\n"; }
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
void SubtitlesProviderFactory::RegisterProviders() {
|
||||
#ifdef WITH_CSRI
|
||||
std::vector<std::string> csri_providers(CSRISubtitlesProvider::GetSubTypes());
|
||||
if (!csri_providers.empty())
|
||||
Register<CSRISubtitlesProvider>("CSRI", false, csri_providers);
|
||||
#endif
|
||||
Register<LibassSubtitlesProvider>("libass");
|
||||
LibassSubtitlesProvider::CacheFonts();
|
||||
}
|
||||
|
|
|
@ -37,12 +37,15 @@
|
|||
#ifdef WITH_CSRI
|
||||
#include "subtitles_provider_csri.h"
|
||||
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
#include "options.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "video_frame.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/path.h>
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <mutex>
|
||||
|
@ -53,9 +56,24 @@
|
|||
|
||||
#include <csri/csri.h>
|
||||
|
||||
namespace {
|
||||
// CSRI renderers are not required to be thread safe (and VSFilter very much
|
||||
// is not)
|
||||
static std::mutex csri_mutex;
|
||||
std::mutex csri_mutex;
|
||||
|
||||
class CSRISubtitlesProvider final : public SubtitlesProvider {
|
||||
agi::scoped_holder<csri_inst*> instance;
|
||||
csri_rend *renderer;
|
||||
|
||||
/// Name of the file passed to renderers with can_open_mem false
|
||||
agi::fs::path tempfile;
|
||||
public:
|
||||
CSRISubtitlesProvider(std::string subType);
|
||||
~CSRISubtitlesProvider();
|
||||
|
||||
void LoadSubtitles(AssFile *subs);
|
||||
void DrawSubtitles(VideoFrame &dst, double time);
|
||||
};
|
||||
|
||||
CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type)
|
||||
: instance(nullptr, csri_close)
|
||||
|
@ -106,8 +124,10 @@ void CSRISubtitlesProvider::DrawSubtitles(VideoFrame &dst,double time) {
|
|||
if (!csri_request_fmt(instance, &format))
|
||||
csri_render(instance, &frame, time);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> CSRISubtitlesProvider::GetSubTypes() {
|
||||
namespace csri {
|
||||
std::vector<std::string> List() {
|
||||
std::vector<std::string> final;
|
||||
for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
|
||||
std::string name(csri_renderer_info(cur)->name);
|
||||
|
@ -119,4 +139,8 @@ std::vector<std::string> CSRISubtitlesProvider::GetSubTypes() {
|
|||
return final;
|
||||
}
|
||||
|
||||
std::unique_ptr<SubtitlesProvider> Create(std::string const& name) {
|
||||
return agi::util::make_unique<CSRISubtitlesProvider>(name);
|
||||
}
|
||||
}
|
||||
#endif // WITH_CSRI
|
||||
|
|
|
@ -1,63 +1,26 @@
|
|||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file subtitles_provider_csri.h
|
||||
/// @see subtitles_provider_csri.cpp
|
||||
/// @ingroup subtitle_rendering
|
||||
///
|
||||
|
||||
#ifdef WITH_CSRI
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
class SubtitlesProvider;
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
typedef void csri_rend;
|
||||
typedef void csri_inst;
|
||||
|
||||
class CSRISubtitlesProvider final : public SubtitlesProvider {
|
||||
agi::scoped_holder<csri_inst*> instance;
|
||||
csri_rend *renderer;
|
||||
|
||||
/// Name of the file passed to renderers with can_open_mem false
|
||||
agi::fs::path tempfile;
|
||||
public:
|
||||
CSRISubtitlesProvider(std::string subType);
|
||||
~CSRISubtitlesProvider();
|
||||
|
||||
void LoadSubtitles(AssFile *subs);
|
||||
void DrawSubtitles(VideoFrame &dst, double time);
|
||||
|
||||
static std::vector<std::string> GetSubTypes();
|
||||
};
|
||||
#endif
|
||||
namespace csri {
|
||||
std::vector<std::string> List();
|
||||
std::unique_ptr<SubtitlesProvider> Create(std::string const& subtype);
|
||||
}
|
||||
|
|
|
@ -36,17 +36,13 @@
|
|||
|
||||
#include "subtitles_provider_libass.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h>
|
||||
#include <libaegisub/util_osx.h>
|
||||
#endif
|
||||
|
||||
#include "ass_info.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_style.h"
|
||||
#include "dialog_progress.h"
|
||||
#include "frame_main.h"
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
#include "main.h"
|
||||
#include "utils.h"
|
||||
#include "video_frame.h"
|
||||
|
@ -60,6 +56,15 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h>
|
||||
#include <libaegisub/util_osx.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <ass/ass.h>
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::unique_ptr<agi::dispatch::Queue> cache_queue;
|
||||
ASS_Library *library;
|
||||
|
@ -84,9 +89,20 @@ void msg_callback(int level, const char *fmt, va_list args, void *) {
|
|||
#else
|
||||
#define CONFIG_PATH nullptr
|
||||
#endif
|
||||
}
|
||||
|
||||
LibassSubtitlesProvider::LibassSubtitlesProvider(std::string) {
|
||||
class LibassSubtitlesProvider final : public SubtitlesProvider {
|
||||
ASS_Renderer* ass_renderer = nullptr;
|
||||
ASS_Track* ass_track = nullptr;
|
||||
|
||||
public:
|
||||
LibassSubtitlesProvider();
|
||||
~LibassSubtitlesProvider();
|
||||
|
||||
void LoadSubtitles(AssFile *subs) override;
|
||||
void DrawSubtitles(VideoFrame &dst, double time) override;
|
||||
};
|
||||
|
||||
LibassSubtitlesProvider::LibassSubtitlesProvider() {
|
||||
auto done = std::make_shared<bool>(false);
|
||||
auto renderer = std::make_shared<ASS_Renderer*>(nullptr);
|
||||
cache_queue->Async([=]{
|
||||
|
@ -115,23 +131,21 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
|
|||
if (ass_renderer) ass_renderer_done(ass_renderer);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct Writer {
|
||||
std::vector<char> data;
|
||||
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
|
||||
struct Writer {
|
||||
std::vector<char> data;
|
||||
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
|
||||
|
||||
template<typename T>
|
||||
void Write(T const& list) {
|
||||
for (auto const& line : list) {
|
||||
if (group != line.Group()) {
|
||||
group = line.Group();
|
||||
boost::push_back(data, line.GroupHeader() + "\r\n");
|
||||
}
|
||||
boost::push_back(data, line.GetEntryData() + "\r\n");
|
||||
template<typename T>
|
||||
void Write(T const& list) {
|
||||
for (auto const& line : list) {
|
||||
if (group != line.Group()) {
|
||||
group = line.Group();
|
||||
boost::push_back(data, line.GroupHeader() + "\r\n");
|
||||
}
|
||||
boost::push_back(data, line.GetEntryData() + "\r\n");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||
Writer writer;
|
||||
|
@ -188,8 +202,14 @@ void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame,double time) {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibassSubtitlesProvider::CacheFonts() {
|
||||
namespace libass {
|
||||
std::unique_ptr<SubtitlesProvider> Create(std::string const&) {
|
||||
return agi::util::make_unique<LibassSubtitlesProvider>();
|
||||
}
|
||||
|
||||
void CacheFonts() {
|
||||
// Initialize the cache worker thread
|
||||
cache_queue = agi::dispatch::Create();
|
||||
|
||||
|
@ -204,3 +224,4 @@ void LibassSubtitlesProvider::CacheFonts() {
|
|||
ass_renderer_done(ass_renderer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +1,25 @@
|
|||
// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file subtitles_provider_libass.h
|
||||
/// @see subtitles_provider_libass.cpp
|
||||
/// @ingroup subtitle_rendering
|
||||
///
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "include/aegisub/subtitles_provider.h"
|
||||
class SubtitlesProvider;
|
||||
|
||||
extern "C" {
|
||||
#include <ass/ass.h>
|
||||
namespace libass {
|
||||
std::unique_ptr<SubtitlesProvider> Create(std::string const&);
|
||||
void CacheFonts();
|
||||
}
|
||||
|
||||
class LibassSubtitlesProvider final : public SubtitlesProvider {
|
||||
ASS_Renderer* ass_renderer = nullptr;
|
||||
ASS_Track* ass_track = nullptr;
|
||||
|
||||
public:
|
||||
LibassSubtitlesProvider(std::string);
|
||||
~LibassSubtitlesProvider();
|
||||
|
||||
void LoadSubtitles(AssFile *subs) override;
|
||||
void DrawSubtitles(VideoFrame &dst, double time) override;
|
||||
|
||||
static void CacheFonts();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue