mirror of
https://github.com/odrling/Aegisub
synced 2025-04-11 22:56:02 +02:00
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_display.h" />
|
||||||
<ClInclude Include="$(SrcDir)audio_karaoke.h" />
|
<ClInclude Include="$(SrcDir)audio_karaoke.h" />
|
||||||
<ClInclude Include="$(SrcDir)audio_marker.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_portaudio.h" />
|
||||||
<ClInclude Include="$(SrcDir)audio_player_pulse.h" />
|
|
||||||
<ClInclude Include="$(SrcDir)audio_renderer.h" />
|
<ClInclude Include="$(SrcDir)audio_renderer.h" />
|
||||||
<ClInclude Include="$(SrcDir)audio_renderer_spectrum.h" />
|
<ClInclude Include="$(SrcDir)audio_renderer_spectrum.h" />
|
||||||
<ClInclude Include="$(SrcDir)audio_renderer_waveform.h" />
|
<ClInclude Include="$(SrcDir)audio_renderer_waveform.h" />
|
||||||
@ -197,7 +191,6 @@
|
|||||||
<ClInclude Include="$(SrcDir)pen.h" />
|
<ClInclude Include="$(SrcDir)pen.h" />
|
||||||
<ClInclude Include="$(SrcDir)persist_location.h" />
|
<ClInclude Include="$(SrcDir)persist_location.h" />
|
||||||
<ClInclude Include="$(SrcDir)placeholder_ctrl.h" />
|
<ClInclude Include="$(SrcDir)placeholder_ctrl.h" />
|
||||||
<ClInclude Include="$(SrcDir)plugin_manager.h" />
|
|
||||||
<ClInclude Include="$(SrcDir)preferences.h" />
|
<ClInclude Include="$(SrcDir)preferences.h" />
|
||||||
<ClInclude Include="$(SrcDir)preferences_base.h" />
|
<ClInclude Include="$(SrcDir)preferences_base.h" />
|
||||||
<ClInclude Include="$(SrcDir)resolution_resampler.h" />
|
<ClInclude Include="$(SrcDir)resolution_resampler.h" />
|
||||||
@ -391,7 +384,6 @@
|
|||||||
<ClCompile Include="$(SrcDir)mkv_wrap.cpp" />
|
<ClCompile Include="$(SrcDir)mkv_wrap.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)pen.cpp" />
|
<ClCompile Include="$(SrcDir)pen.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)persist_location.cpp" />
|
<ClCompile Include="$(SrcDir)persist_location.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)plugin_manager.cpp" />
|
|
||||||
<ClCompile Include="$(SrcDir)preferences.cpp" />
|
<ClCompile Include="$(SrcDir)preferences.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)preferences_base.cpp" />
|
<ClCompile Include="$(SrcDir)preferences_base.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)resolution_resampler.cpp" />
|
<ClCompile Include="$(SrcDir)resolution_resampler.cpp" />
|
||||||
|
@ -213,24 +213,6 @@
|
|||||||
<ClInclude Include="$(SrcDir)config.h">
|
<ClInclude Include="$(SrcDir)config.h">
|
||||||
<Filter>Config</Filter>
|
<Filter>Config</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="$(SrcDir)audio_player_portaudio.h">
|
||||||
<Filter>Audio\Players</Filter>
|
<Filter>Audio\Players</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -528,9 +510,6 @@
|
|||||||
<ClInclude Include="$(SrcDir)include\aegisub\video_provider.h">
|
<ClInclude Include="$(SrcDir)include\aegisub\video_provider.h">
|
||||||
<Filter>Video\Providers</Filter>
|
<Filter>Video\Providers</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="$(SrcDir)plugin_manager.h">
|
|
||||||
<Filter>Utilities</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="$(SrcDir)spline_curve.h">
|
<ClInclude Include="$(SrcDir)spline_curve.h">
|
||||||
<Filter>Video\Visual tools</Filter>
|
<Filter>Video\Visual tools</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -1055,9 +1034,6 @@
|
|||||||
<ClCompile Include="$(SrcDir)menu.cpp">
|
<ClCompile Include="$(SrcDir)menu.cpp">
|
||||||
<Filter>Main UI</Filter>
|
<Filter>Main UI</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)plugin_manager.cpp">
|
|
||||||
<Filter>Utilities</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="$(SrcDir)spline.cpp">
|
<ClCompile Include="$(SrcDir)spline.cpp">
|
||||||
<Filter>Video\Visual tools</Filter>
|
<Filter>Video\Visual tools</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -203,7 +203,6 @@ SRC += \
|
|||||||
mkv_wrap.cpp \
|
mkv_wrap.cpp \
|
||||||
pen.cpp \
|
pen.cpp \
|
||||||
persist_location.cpp \
|
persist_location.cpp \
|
||||||
plugin_manager.cpp \
|
|
||||||
preferences.cpp \
|
preferences.cpp \
|
||||||
preferences_base.cpp \
|
preferences_base.cpp \
|
||||||
resolution_resampler.cpp \
|
resolution_resampler.cpp \
|
||||||
|
@ -35,56 +35,75 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "include/aegisub/audio_player.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 "audio_controller.h"
|
||||||
|
#include "factory_manager.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
|
||||||
AudioPlayer::AudioPlayer(AudioProvider *provider)
|
AudioPlayer::AudioPlayer(AudioProvider *provider)
|
||||||
: provider(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::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(AudioProvider *provider) {
|
||||||
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Player")->GetString());
|
if (std::distance(std::begin(factories), std::end(factories)) == 0)
|
||||||
if (list.empty()) throw agi::NoAudioPlayersError("No audio players are available.", nullptr);
|
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;
|
std::string error;
|
||||||
for (auto const& factory_name : list) {
|
for (auto factory : sorted) {
|
||||||
try {
|
try {
|
||||||
return Create(factory_name, provider);
|
return factory->create(provider);
|
||||||
}
|
}
|
||||||
catch (agi::AudioPlayerOpenError const& err) {
|
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);
|
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"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_ALSA
|
#ifdef WITH_ALSA
|
||||||
|
#include "include/aegisub/audio_player.h"
|
||||||
#include "audio_player_alsa.h"
|
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
#include "include/aegisub/audio_provider.h"
|
#include "include/aegisub/audio_provider.h"
|
||||||
@ -48,15 +47,73 @@
|
|||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
#include <inttypes.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 {
|
class PthreadMutexLocker {
|
||||||
pthread_mutex_t &mutex;
|
pthread_mutex_t &mutex;
|
||||||
|
|
||||||
PthreadMutexLocker(const PthreadMutexLocker &); // uncopyable
|
PthreadMutexLocker(const PthreadMutexLocker &) = delete;
|
||||||
PthreadMutexLocker(); // no default
|
PthreadMutexLocker& operator=(PthreadMutexLocker const&) = delete;
|
||||||
PthreadMutexLocker& operator=(PthreadMutexLocker const&);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PthreadMutexLocker(pthread_mutex_t &mutex) : mutex(mutex)
|
explicit PthreadMutexLocker(pthread_mutex_t &mutex) : mutex(mutex)
|
||||||
@ -83,78 +140,22 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ScopedAliveFlag {
|
class ScopedAliveFlag {
|
||||||
bool &flag;
|
bool &flag;
|
||||||
|
|
||||||
ScopedAliveFlag(const ScopedAliveFlag &); // uncopyable
|
ScopedAliveFlag(const ScopedAliveFlag &) = delete;
|
||||||
ScopedAliveFlag(); // no default
|
ScopedAliveFlag& operator=(ScopedAliveFlag const&) = delete;
|
||||||
ScopedAliveFlag& operator=(ScopedAliveFlag const&);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ScopedAliveFlag(bool &var) : flag(var) { flag = true; }
|
explicit ScopedAliveFlag(bool &var) : flag(var) { flag = true; }
|
||||||
~ScopedAliveFlag() { flag = false; }
|
~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)
|
void *playback_thread(void *arg)
|
||||||
{
|
{
|
||||||
// This is exception-free territory!
|
// This is exception-free territory!
|
||||||
// Return a pointer to a static string constant describing the error, or 0 on no error
|
// 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);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
ScopedAliveFlag alive_flag(ps.alive);
|
ScopedAliveFlag alive_flag(ps.alive);
|
||||||
@ -363,73 +364,66 @@ do_setup:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AlsaPlayer::AlsaPlayer(AudioProvider *provider)
|
AlsaPlayer::AlsaPlayer(AudioProvider *provider)
|
||||||
: AudioPlayer(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);
|
throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AlsaPlayer::~AlsaPlayer()
|
AlsaPlayer::~AlsaPlayer()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
ps->signal_stop = true;
|
ps.signal_stop = true;
|
||||||
ps->signal_close = true;
|
ps.signal_close = true;
|
||||||
LOG_D("audio/player/alsa") << "close stream, stop+close signal";
|
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
|
pthread_join(thread, 0); // FIXME: check for errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayer::Play(int64_t start, int64_t count)
|
void AlsaPlayer::Play(int64_t start, int64_t count)
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
ps->signal_start = true;
|
ps.signal_start = true;
|
||||||
ps->signal_stop = true; // make sure to stop any ongoing playback first
|
ps.signal_stop = true; // make sure to stop any ongoing playback first
|
||||||
ps->start_position = start;
|
ps.start_position = start;
|
||||||
ps->end_position = start + count;
|
ps.end_position = start + count;
|
||||||
pthread_cond_signal(&ps->cond);
|
pthread_cond_signal(&ps.cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayer::Stop()
|
void AlsaPlayer::Stop()
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
ps->signal_stop = true;
|
ps.signal_stop = true;
|
||||||
LOG_D("audio/player/alsa") << "stop stream, stop signal";
|
LOG_D("audio/player/alsa") << "stop stream, stop signal";
|
||||||
pthread_cond_signal(&ps->cond);
|
pthread_cond_signal(&ps.cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlsaPlayer::IsPlaying()
|
bool AlsaPlayer::IsPlaying()
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
return ps->playing;
|
return ps.playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayer::SetEndPosition(int64_t pos)
|
void AlsaPlayer::SetEndPosition(int64_t pos)
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
ps->end_position = pos;
|
ps.end_position = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t AlsaPlayer::GetEndPosition()
|
int64_t AlsaPlayer::GetEndPosition()
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
return ps->end_position;
|
return ps.end_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int64_t AlsaPlayer::GetCurrentPosition()
|
int64_t AlsaPlayer::GetCurrentPosition()
|
||||||
{
|
{
|
||||||
int64_t lastpos;
|
int64_t lastpos;
|
||||||
@ -437,10 +431,10 @@ int64_t AlsaPlayer::GetCurrentPosition()
|
|||||||
int64_t samplerate;
|
int64_t samplerate;
|
||||||
|
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
lastpos = ps->last_position;
|
lastpos = ps.last_position;
|
||||||
lasttime = ps->last_position_time;
|
lasttime = ps.last_position_time;
|
||||||
samplerate = ps->provider->GetSampleRate();
|
samplerate = ps.provider->GetSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec now;
|
timespec now;
|
||||||
@ -460,10 +454,16 @@ int64_t AlsaPlayer::GetCurrentPosition()
|
|||||||
|
|
||||||
void AlsaPlayer::SetVolume(double vol)
|
void AlsaPlayer::SetVolume(double vol)
|
||||||
{
|
{
|
||||||
PthreadMutexLocker ml(ps->mutex);
|
PthreadMutexLocker ml(ps.mutex);
|
||||||
ps->volume = vol;
|
ps.volume = vol;
|
||||||
ps->signal_volume = true;
|
ps.signal_volume = true;
|
||||||
pthread_cond_signal(&ps->cond);
|
pthread_cond_signal(&ps.cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(AudioProvider *provider)
|
||||||
|
{
|
||||||
|
return agi::util::make_unique<AlsaPlayer>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_ALSA
|
#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"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_DIRECTSOUND
|
#ifdef WITH_DIRECTSOUND
|
||||||
|
#include "include/aegisub/audio_player.h"
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
#include "audio_player_dsound.h"
|
#include "include/aegisub/audio_provider.h"
|
||||||
#include "frame_main.h"
|
#include "frame_main.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "utils.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)
|
DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider)
|
||||||
: AudioPlayer(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
|
// Initialize the DirectSound object
|
||||||
HRESULT res;
|
HRESULT res;
|
||||||
@ -105,23 +149,13 @@ DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider)
|
|||||||
DirectSoundPlayer::~DirectSoundPlayer() {
|
DirectSoundPlayer::~DirectSoundPlayer() {
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
// Unref the DirectSound buffer
|
if (buffer)
|
||||||
if (buffer) {
|
|
||||||
buffer->Release();
|
buffer->Release();
|
||||||
buffer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unref the DirectSound object
|
if (directSound)
|
||||||
if (directSound) {
|
|
||||||
directSound->Release();
|
directSound->Release();
|
||||||
directSound = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Fill buffer
|
|
||||||
/// @param fill
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
bool DirectSoundPlayer::FillBuffer(bool fill) {
|
bool DirectSoundPlayer::FillBuffer(bool fill) {
|
||||||
if (playPos >= endPos) return false;
|
if (playPos >= endPos) return false;
|
||||||
|
|
||||||
@ -181,7 +215,6 @@ RetryLock:
|
|||||||
goto RetryLock;
|
goto RetryLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error
|
|
||||||
if (FAILED(res)) return false;
|
if (FAILED(res)) return false;
|
||||||
|
|
||||||
// Convert size to number of samples
|
// Convert size to number of samples
|
||||||
@ -197,19 +230,13 @@ RetryLock:
|
|||||||
if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
|
if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
|
||||||
playPos += count1+count2;
|
playPos += count1+count2;
|
||||||
|
|
||||||
// Unlock
|
|
||||||
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
|
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
|
||||||
|
|
||||||
// Update offset
|
|
||||||
offset = (offset + count1*bytesps + count2*bytesps) % bufSize;
|
offset = (offset + count1*bytesps + count2*bytesps) % bufSize;
|
||||||
|
|
||||||
return playPos < endPos;
|
return playPos < endPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Play
|
|
||||||
/// @param start
|
|
||||||
/// @param count
|
|
||||||
///
|
|
||||||
void DirectSoundPlayer::Play(int64_t start,int64_t count) {
|
void DirectSoundPlayer::Play(int64_t start,int64_t count) {
|
||||||
// Make sure that it's stopped
|
// Make sure that it's stopped
|
||||||
Stop();
|
Stop();
|
||||||
@ -245,9 +272,6 @@ void DirectSoundPlayer::Play(int64_t start,int64_t count) {
|
|||||||
startTime = GetTickCount();
|
startTime = GetTickCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Stop
|
|
||||||
/// @param timerToo
|
|
||||||
///
|
|
||||||
void DirectSoundPlayer::Stop() {
|
void DirectSoundPlayer::Stop() {
|
||||||
// Stop the thread
|
// Stop the thread
|
||||||
if (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
|
// 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
|
if (buffer) buffer->Stop(); // the thread should have done this already
|
||||||
|
|
||||||
// Reset variables
|
// Reset variables
|
||||||
@ -270,16 +293,10 @@ void DirectSoundPlayer::Stop() {
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Set end
|
|
||||||
/// @param pos
|
|
||||||
///
|
|
||||||
void DirectSoundPlayer::SetEndPosition(int64_t pos) {
|
void DirectSoundPlayer::SetEndPosition(int64_t pos) {
|
||||||
if (playing) endPos = pos;
|
if (playing) endPos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Get current position
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
int64_t DirectSoundPlayer::GetCurrentPosition() {
|
int64_t DirectSoundPlayer::GetCurrentPosition() {
|
||||||
// Check if buffer is loaded
|
// Check if buffer is loaded
|
||||||
if (!buffer || !playing) return 0;
|
if (!buffer || !playing) return 0;
|
||||||
@ -291,23 +308,15 @@ int64_t DirectSoundPlayer::GetCurrentPosition() {
|
|||||||
return startPos + tdiff * provider->GetSampleRate() / 1000;
|
return startPos + tdiff * provider->GetSampleRate() / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Thread constructor
|
|
||||||
/// @param par
|
|
||||||
///
|
|
||||||
DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
|
DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
|
||||||
parent = par;
|
parent = par;
|
||||||
stopnotify = CreateEvent(nullptr, true, false, nullptr);
|
stopnotify = CreateEvent(nullptr, true, false, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Thread destructor
|
|
||||||
///
|
|
||||||
DirectSoundPlayerThread::~DirectSoundPlayerThread() {
|
DirectSoundPlayerThread::~DirectSoundPlayerThread() {
|
||||||
CloseHandle(stopnotify);
|
CloseHandle(stopnotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Thread entry point
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
||||||
CoInitialize(0);
|
CoInitialize(0);
|
||||||
|
|
||||||
@ -355,12 +364,15 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Stop playback thread
|
|
||||||
///
|
|
||||||
void DirectSoundPlayerThread::Stop() {
|
void DirectSoundPlayerThread::Stop() {
|
||||||
// Increase the stopnotify by one, causing a wait for it to succeed
|
// Increase the stopnotify by one, causing a wait for it to succeed
|
||||||
SetEvent(stopnotify);
|
SetEvent(stopnotify);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *provider) {
|
||||||
|
return agi::util::make_unique<DirectSoundPlayer>(provider);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WITH_DIRECTSOUND
|
#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"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_DIRECTSOUND
|
#ifdef WITH_DIRECTSOUND
|
||||||
#include "audio_player_dsound2.h"
|
#include "include/aegisub/audio_player.h"
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
#include "include/aegisub/audio_provider.h"
|
#include "include/aegisub/audio_provider.h"
|
||||||
@ -52,6 +52,68 @@
|
|||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <dsound.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
|
/// @brief RAII support class to init and de-init the COM library
|
||||||
struct COMInitialization {
|
struct COMInitialization {
|
||||||
|
|
||||||
@ -157,7 +219,6 @@ class DirectSoundPlayer2Thread {
|
|||||||
/// @brief Check for error state and throw exception if one occurred
|
/// @brief Check for error state and throw exception if one occurred
|
||||||
void CheckError();
|
void CheckError();
|
||||||
|
|
||||||
|
|
||||||
/// Win32 handle to the thread
|
/// Win32 handle to the thread
|
||||||
Win32KernelHandle thread_handle;
|
Win32KernelHandle thread_handle;
|
||||||
|
|
||||||
@ -186,16 +247,16 @@ class DirectSoundPlayer2Thread {
|
|||||||
Win32KernelHandle error_happened;
|
Win32KernelHandle error_happened;
|
||||||
|
|
||||||
/// Statically allocated error message text describing reason for error_happened being set
|
/// 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"
|
/// Playback volume, 1.0 is "unchanged"
|
||||||
double volume;
|
double volume = 1.0;
|
||||||
|
|
||||||
/// Audio frame to start playback at
|
/// Audio frame to start playback at
|
||||||
int64_t start_frame;
|
int64_t start_frame = 0;
|
||||||
|
|
||||||
/// Audio frame to end playback at
|
/// 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
|
/// Desired length in milliseconds to write ahead of the playback cursor
|
||||||
@ -646,11 +707,6 @@ DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int
|
|||||||
, buffer_length(BufferLength)
|
, buffer_length(BufferLength)
|
||||||
, provider(provider)
|
, provider(provider)
|
||||||
{
|
{
|
||||||
error_message = 0;
|
|
||||||
volume = 1.0;
|
|
||||||
start_frame = 0;
|
|
||||||
end_frame = 0;
|
|
||||||
|
|
||||||
thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);
|
thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);
|
||||||
|
|
||||||
if (!thread_handle)
|
if (!thread_handle)
|
||||||
@ -904,5 +960,10 @@ void DirectSoundPlayer2::SetVolume(double vol)
|
|||||||
LOG_E("audio/player/dsound") << msg;
|
LOG_E("audio/player/dsound") << msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioPlayer> CreateDirectSound2Player(AudioProvider *provider) {
|
||||||
|
return agi::util::make_unique<DirectSoundPlayer2>(provider);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WITH_DIRECTSOUND
|
#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"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_OPENAL
|
#ifdef WITH_OPENAL
|
||||||
|
#include "include/aegisub/audio_player.h"
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "audio_player_openal.h"
|
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
|
#include "include/aegisub/audio_provider.h"
|
||||||
#include "utils.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
|
// Auto-link to OpenAL lib for MSVC
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma comment(lib, "openal32.lib")
|
#pragma comment(lib, "openal32.lib")
|
||||||
@ -50,6 +65,65 @@
|
|||||||
|
|
||||||
DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal")
|
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)
|
OpenALPlayer::OpenALPlayer(AudioProvider *provider)
|
||||||
: AudioPlayer(provider)
|
: AudioPlayer(provider)
|
||||||
, samplerate(provider->GetSampleRate())
|
, samplerate(provider->GetSampleRate())
|
||||||
@ -210,5 +284,11 @@ int64_t OpenALPlayer::GetCurrentPosition()
|
|||||||
long extra = playback_segment_timer.Time();
|
long extra = playback_segment_timer.Time();
|
||||||
return buffers_played * decode_buffer.size() / bpf + start_frame + extra * samplerate / 1000;
|
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
|
#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"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_OSS
|
#ifdef WITH_OSS
|
||||||
|
#include "include/aegisub/audio_player.h"
|
||||||
#include <sys/param.h>
|
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "audio_player_oss.h"
|
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
@ -46,19 +41,112 @@
|
|||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.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")
|
DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss")
|
||||||
|
class OSSPlayerThread;
|
||||||
|
|
||||||
OSSPlayer::OSSPlayer(AudioProvider *provider)
|
class OSSPlayer final : public AudioPlayer {
|
||||||
: AudioPlayer(provider)
|
friend class OSSPlayerThread;
|
||||||
{
|
|
||||||
OpenStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
OSSPlayer::~OSSPlayer()
|
/// sample rate of audio
|
||||||
{
|
unsigned int rate = 0;
|
||||||
Stop();
|
|
||||||
::close(dspdev);
|
/// 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()
|
void OSSPlayer::OpenStream()
|
||||||
{
|
{
|
||||||
@ -193,31 +281,10 @@ int64_t OSSPlayer::GetCurrentPosition()
|
|||||||
// Return the last written frame, timing will suffer
|
// Return the last written frame, timing will suffer
|
||||||
return cur_frame;
|
return cur_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSSPlayerThread::OSSPlayerThread(OSSPlayer *par)
|
|
||||||
: wxThread(wxTHREAD_JOINABLE)
|
|
||||||
, parent(par)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThread::ExitCode OSSPlayerThread::Entry() {
|
std::unique_ptr<AudioPlayer> CreateOSSPlayer(AudioProvider *provider) {
|
||||||
// Use small enough writes for good timing accuracy with all
|
return agi::util::make_unique<OSSPlayer>(provider);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_OSS
|
#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
|
/// @ingroup audio_output
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_PORTAUDIO
|
#ifdef WITH_PORTAUDIO
|
||||||
|
|
||||||
#include <libaegisub/log.h>
|
|
||||||
|
|
||||||
#include "audio_player_portaudio.h"
|
#include "audio_player_portaudio.h"
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
@ -47,6 +43,9 @@
|
|||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <libaegisub/log.h>
|
||||||
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
DEFINE_SIMPLE_EXCEPTION(PortAudioError, agi::AudioPlayerOpenError, "audio/player/open/portaudio")
|
DEFINE_SIMPLE_EXCEPTION(PortAudioError, agi::AudioPlayerOpenError, "audio/player/open/portaudio")
|
||||||
|
|
||||||
// Uncomment to enable extremely spammy debug logging
|
// Uncomment to enable extremely spammy debug logging
|
||||||
@ -284,4 +283,8 @@ bool PortAudioPlayer::IsPlaying() {
|
|||||||
return !!Pa_IsStreamActive(stream);
|
return !!Pa_IsStreamActive(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(AudioProvider *provider) {
|
||||||
|
return agi::util::make_unique<PortAudioPlayer>(provider);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WITH_PORTAUDIO
|
#endif // WITH_PORTAUDIO
|
||||||
|
@ -35,18 +35,74 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_LIBPULSE
|
#ifdef WITH_LIBPULSE
|
||||||
|
#include "include/aegisub/audio_player.h"
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#include <wx/thread.h>
|
|
||||||
|
|
||||||
#include "audio_player_pulse.h"
|
|
||||||
|
|
||||||
#include "audio_controller.h"
|
#include "audio_controller.h"
|
||||||
#include "include/aegisub/audio_provider.h"
|
#include "include/aegisub/audio_provider.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <libaegisub/log.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) {
|
PulseAudioPlayer::PulseAudioPlayer(AudioProvider *provider) : AudioPlayer(provider) {
|
||||||
// Initialise a mainloop
|
// 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->sstate = pa_stream_get_state(thread->stream);
|
||||||
thread->stream_notify.Post();
|
thread->stream_notify.Post();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(AudioProvider *provider) {
|
||||||
|
return agi::util::make_unique<PulseAudioPlayer>(provider);
|
||||||
|
}
|
||||||
#endif // WITH_LIBPULSE
|
#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/
|
// 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 <string>
|
||||||
#include <vector>
|
#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 {
|
namespace {
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
std::vector<std::string> GetClasses(Container const& c) {
|
std::vector<std::string> GetClasses(Container const& c) {
|
||||||
|
@ -34,7 +34,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "factory_manager.h"
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class AudioProvider;
|
class AudioProvider;
|
||||||
|
|
||||||
@ -57,8 +60,7 @@ public:
|
|||||||
virtual void SetEndPosition(int64_t pos)=0;
|
virtual void SetEndPosition(int64_t pos)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioPlayerFactory final : public Factory<AudioPlayer, AudioProvider*> {
|
struct AudioPlayerFactory {
|
||||||
public:
|
static std::vector<std::string> GetClasses();
|
||||||
static void RegisterProviders();
|
|
||||||
static std::unique_ptr<AudioPlayer> GetAudioPlayer(AudioProvider *provider);
|
static std::unique_ptr<AudioPlayer> GetAudioPlayer(AudioProvider *provider);
|
||||||
};
|
};
|
||||||
|
@ -19,12 +19,10 @@
|
|||||||
/// @ingroup main_headers spelling
|
/// @ingroup main_headers spelling
|
||||||
///
|
///
|
||||||
|
|
||||||
#include "factory_manager.h"
|
#include <memory>
|
||||||
|
|
||||||
namespace agi { class SpellChecker; }
|
namespace agi { class SpellChecker; }
|
||||||
|
|
||||||
class SpellCheckerFactory final : public Factory<agi::SpellChecker> {
|
struct SpellCheckerFactory {
|
||||||
public:
|
|
||||||
static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
|
static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
|
||||||
static void RegisterProviders();
|
|
||||||
};
|
};
|
||||||
|
@ -34,21 +34,21 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "factory_manager.h"
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
struct VideoFrame;
|
struct VideoFrame;
|
||||||
|
|
||||||
class SubtitlesProvider {
|
class SubtitlesProvider {
|
||||||
public:
|
public:
|
||||||
virtual ~SubtitlesProvider() { };
|
virtual ~SubtitlesProvider() { }
|
||||||
|
|
||||||
virtual void LoadSubtitles(AssFile *subs)=0;
|
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> {
|
struct SubtitlesProviderFactory {
|
||||||
public:
|
|
||||||
static std::unique_ptr<SubtitlesProvider> GetProvider();
|
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_dialogue.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "auto4_base.h"
|
#include "auto4_base.h"
|
||||||
|
#include "auto4_lua_factory.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "crash_writer.h"
|
#include "crash_writer.h"
|
||||||
#include "export_fixstyle.h"
|
#include "export_fixstyle.h"
|
||||||
@ -50,9 +51,9 @@
|
|||||||
#include "include/aegisub/context.h"
|
#include "include/aegisub/context.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "plugin_manager.h"
|
|
||||||
#include "subs_controller.h"
|
#include "subs_controller.h"
|
||||||
#include "subtitle_format.h"
|
#include "subtitle_format.h"
|
||||||
|
#include "subtitles_provider_libass.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "utils.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.");
|
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
|
// Load plugins
|
||||||
RegisterBuiltInPlugins();
|
Automation4::ScriptFactory::Register(agi::util::make_unique<Automation4::LuaScriptFactory>());
|
||||||
|
libass::CacheFonts();
|
||||||
|
|
||||||
// Load Automation scripts
|
// Load Automation scripts
|
||||||
StartupLog("Load global 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
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file spellchecker.cpp
|
|
||||||
/// @brief Implementatin of base-class for spell checkers
|
|
||||||
/// @ingroup spelling
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "include/aegisub/spellchecker.h"
|
#include "include/aegisub/spellchecker.h"
|
||||||
#include "spellchecker_hunspell.h"
|
#include "spellchecker_hunspell.h"
|
||||||
|
|
||||||
#include "options.h"
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <libaegisub/spellchecker.h>
|
|
||||||
|
|
||||||
std::unique_ptr<agi::SpellChecker> SpellCheckerFactory::GetSpellChecker() {
|
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
|
#ifdef WITH_HUNSPELL
|
||||||
Register<HunspellSpellChecker>("Hunspell");
|
return agi::util::make_unique<HunspellSpellChecker>();
|
||||||
|
#else
|
||||||
|
return {};
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1,71 +1,67 @@
|
|||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file subtitles_provider.cpp
|
|
||||||
/// @brief Base class for subtitle renderers
|
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "include/aegisub/subtitles_provider.h"
|
||||||
|
|
||||||
|
#include "factory_manager.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "subtitles_provider_csri.h"
|
#include "subtitles_provider_csri.h"
|
||||||
#include "subtitles_provider_libass.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::unique_ptr<SubtitlesProvider> SubtitlesProviderFactory::GetProvider() {
|
||||||
std::vector<std::string> list = GetClasses(OPT_GET("Subtitle/Provider")->GetString());
|
auto preferred = OPT_GET("Subtitle/Provider")->GetString();
|
||||||
if (list.empty()) throw std::string("No subtitle providers are available.");
|
auto sorted = GetSorted(factories(), preferred);
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
for (auto const& factory : list) {
|
for (auto factory : sorted) {
|
||||||
try {
|
try {
|
||||||
size_t pos = factory.find('/');
|
auto provider = factory->create(factory->subtype);
|
||||||
std::string subType = pos < factory.size() - 1 ? factory.substr(pos + 1) : "";
|
|
||||||
auto provider = Create(factory, subType);
|
|
||||||
if (provider) return provider;
|
if (provider) return provider;
|
||||||
}
|
}
|
||||||
catch (agi::UserCancelException const&) { throw; }
|
catch (agi::UserCancelException const&) { throw; }
|
||||||
catch (std::string const& err) { error += factory + " factory: " + err + "\n"; }
|
catch (std::string const& err) { error += factory->name + ": " + err + "\n"; }
|
||||||
catch (const char *err) { error += factory + " factory: " + std::string(err) + "\n"; }
|
catch (const char *err) { error += factory->name + ": " + std::string(err) + "\n"; }
|
||||||
catch (...) { error += factory + " factory: Unknown error\n"; }
|
catch (...) { error += factory->name + ": Unknown error\n"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
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
|
#ifdef WITH_CSRI
|
||||||
#include "subtitles_provider_csri.h"
|
#include "subtitles_provider_csri.h"
|
||||||
|
|
||||||
|
#include "include/aegisub/subtitles_provider.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "subtitle_format.h"
|
#include "subtitle_format.h"
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
|
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
#include <libaegisub/path.h>
|
#include <libaegisub/path.h>
|
||||||
|
#include <libaegisub/scoped_ptr.h>
|
||||||
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -53,9 +56,24 @@
|
|||||||
|
|
||||||
#include <csri/csri.h>
|
#include <csri/csri.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
// CSRI renderers are not required to be thread safe (and VSFilter very much
|
// CSRI renderers are not required to be thread safe (and VSFilter very much
|
||||||
// is not)
|
// 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)
|
CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type)
|
||||||
: instance(nullptr, csri_close)
|
: instance(nullptr, csri_close)
|
||||||
@ -106,8 +124,10 @@ void CSRISubtitlesProvider::DrawSubtitles(VideoFrame &dst,double time) {
|
|||||||
if (!csri_request_fmt(instance, &format))
|
if (!csri_request_fmt(instance, &format))
|
||||||
csri_render(instance, &frame, time);
|
csri_render(instance, &frame, time);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> CSRISubtitlesProvider::GetSubTypes() {
|
namespace csri {
|
||||||
|
std::vector<std::string> List() {
|
||||||
std::vector<std::string> final;
|
std::vector<std::string> final;
|
||||||
for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
|
for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
|
||||||
std::string name(csri_renderer_info(cur)->name);
|
std::string name(csri_renderer_info(cur)->name);
|
||||||
@ -119,4 +139,8 @@ std::vector<std::string> CSRISubtitlesProvider::GetSubTypes() {
|
|||||||
return final;
|
return final;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SubtitlesProvider> Create(std::string const& name) {
|
||||||
|
return agi::util::make_unique<CSRISubtitlesProvider>(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // WITH_CSRI
|
#endif // WITH_CSRI
|
||||||
|
@ -1,63 +1,26 @@
|
|||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file subtitles_provider_csri.h
|
#include <memory>
|
||||||
/// @see subtitles_provider_csri.cpp
|
#include <string>
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#ifdef WITH_CSRI
|
|
||||||
#include "include/aegisub/subtitles_provider.h"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <libaegisub/fs_fwd.h>
|
class SubtitlesProvider;
|
||||||
#include <libaegisub/scoped_ptr.h>
|
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
namespace csri {
|
||||||
|
std::vector<std::string> List();
|
||||||
typedef void csri_rend;
|
std::unique_ptr<SubtitlesProvider> Create(std::string const& subtype);
|
||||||
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
|
|
||||||
|
@ -36,17 +36,13 @@
|
|||||||
|
|
||||||
#include "subtitles_provider_libass.h"
|
#include "subtitles_provider_libass.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <libaegisub/util_osx.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ass_info.h"
|
#include "ass_info.h"
|
||||||
#include "ass_dialogue.h"
|
#include "ass_dialogue.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "ass_style.h"
|
#include "ass_style.h"
|
||||||
#include "dialog_progress.h"
|
#include "dialog_progress.h"
|
||||||
#include "frame_main.h"
|
#include "frame_main.h"
|
||||||
|
#include "include/aegisub/subtitles_provider.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
@ -60,6 +56,15 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <libaegisub/util_osx.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <ass/ass.h>
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::unique_ptr<agi::dispatch::Queue> cache_queue;
|
std::unique_ptr<agi::dispatch::Queue> cache_queue;
|
||||||
ASS_Library *library;
|
ASS_Library *library;
|
||||||
@ -84,9 +89,20 @@ void msg_callback(int level, const char *fmt, va_list args, void *) {
|
|||||||
#else
|
#else
|
||||||
#define CONFIG_PATH nullptr
|
#define CONFIG_PATH nullptr
|
||||||
#endif
|
#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 done = std::make_shared<bool>(false);
|
||||||
auto renderer = std::make_shared<ASS_Renderer*>(nullptr);
|
auto renderer = std::make_shared<ASS_Renderer*>(nullptr);
|
||||||
cache_queue->Async([=]{
|
cache_queue->Async([=]{
|
||||||
@ -115,23 +131,21 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
|
|||||||
if (ass_renderer) ass_renderer_done(ass_renderer);
|
if (ass_renderer) ass_renderer_done(ass_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
struct Writer {
|
||||||
struct Writer {
|
std::vector<char> data;
|
||||||
std::vector<char> data;
|
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
|
||||||
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void Write(T const& list) {
|
void Write(T const& list) {
|
||||||
for (auto const& line : list) {
|
for (auto const& line : list) {
|
||||||
if (group != line.Group()) {
|
if (group != line.Group()) {
|
||||||
group = line.Group();
|
group = line.Group();
|
||||||
boost::push_back(data, line.GroupHeader() + "\r\n");
|
boost::push_back(data, line.GroupHeader() + "\r\n");
|
||||||
}
|
|
||||||
boost::push_back(data, line.GetEntryData() + "\r\n");
|
|
||||||
}
|
}
|
||||||
|
boost::push_back(data, line.GetEntryData() + "\r\n");
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||||
Writer writer;
|
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
|
// Initialize the cache worker thread
|
||||||
cache_queue = agi::dispatch::Create();
|
cache_queue = agi::dispatch::Create();
|
||||||
|
|
||||||
@ -204,3 +224,4 @@ void LibassSubtitlesProvider::CacheFonts() {
|
|||||||
ass_renderer_done(ass_renderer);
|
ass_renderer_done(ass_renderer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,53 +1,25 @@
|
|||||||
// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file subtitles_provider_libass.h
|
#include <memory>
|
||||||
/// @see subtitles_provider_libass.cpp
|
#include <string>
|
||||||
/// @ingroup subtitle_rendering
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "include/aegisub/subtitles_provider.h"
|
class SubtitlesProvider;
|
||||||
|
|
||||||
extern "C" {
|
namespace libass {
|
||||||
#include <ass/ass.h>
|
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…
x
Reference in New Issue
Block a user