Redesign AudioProviderFactory

Register functions which create each type of provider rather than the
provider types themselves so that the concrete types don't need to be
publicly exposed, and use a static list of providers rather than
registering them at runtime.
This commit is contained in:
Thomas Goyne 2014-03-23 19:30:03 -07:00
parent 7dd764db6b
commit 938025acb1
20 changed files with 263 additions and 489 deletions

View File

@ -117,13 +117,6 @@
<ClInclude Include="$(SrcDir)audio_player_oss.h" />
<ClInclude Include="$(SrcDir)audio_player_portaudio.h" />
<ClInclude Include="$(SrcDir)audio_player_pulse.h" />
<ClInclude Include="$(SrcDir)audio_provider_avs.h" />
<ClInclude Include="$(SrcDir)audio_provider_convert.h" />
<ClInclude Include="$(SrcDir)audio_provider_dummy.h" />
<ClInclude Include="$(SrcDir)audio_provider_ffmpegsource.h" />
<ClInclude Include="$(SrcDir)audio_provider_hd.h" />
<ClInclude Include="$(SrcDir)audio_provider_lock.h" />
<ClInclude Include="$(SrcDir)audio_provider_ram.h" />
<ClInclude Include="$(SrcDir)audio_renderer.h" />
<ClInclude Include="$(SrcDir)audio_renderer_spectrum.h" />
<ClInclude Include="$(SrcDir)audio_renderer_waveform.h" />

View File

@ -177,24 +177,6 @@
<ClInclude Include="$(SrcDir)ass_style_storage.h">
<Filter>ASS</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_ram.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_avs.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_convert.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_dummy.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_ffmpegsource.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_hd.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_box.h">
<Filter>Audio\UI</Filter>
</ClInclude>
@ -645,9 +627,6 @@
<ClInclude Include="$(SrcDir)scintilla_text_selection_controller.h">
<Filter>Main UI\Edit box</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_provider_lock.h">
<Filter>Audio\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)dialog_manager.h">
<Filter>Utilities\UI utilities</Filter>
</ClInclude>

View File

@ -37,7 +37,6 @@
#include "audio_controller.h"
#include "ass_file.h"
#include "audio_provider_dummy.h"
#include "audio_timing.h"
#include "compat.h"
#include "include/aegisub/audio_player.h"

View File

@ -34,13 +34,7 @@
#include "config.h"
#include "audio_provider_avs.h"
#include "audio_provider_convert.h"
#include "audio_provider_dummy.h"
#include "audio_provider_ffmpegsource.h"
#include "audio_provider_hd.h"
#include "audio_provider_lock.h"
#include "audio_provider_ram.h"
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "dialog_progress.h"
@ -53,9 +47,6 @@
#include <libaegisub/log.h>
#include <libaegisub/util.h>
// Defined in audio_provider_pcm.cpp
std::unique_ptr<AudioProvider> CreatePCMAudioProvider(agi::fs::path const& filename);
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
GetAudio(buf, start, count);
@ -110,68 +101,96 @@ void AudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
}
}
std::unique_ptr<AudioProvider> CreateDummyAudioProvider(agi::fs::path const& filename);
std::unique_ptr<AudioProvider> CreatePCMAudioProvider(agi::fs::path const& filename);
std::unique_ptr<AudioProvider> CreateAvisynthAudioProvider(agi::fs::path const& filename);
std::unique_ptr<AudioProvider> CreateFFmpegSourceAudioProvider(agi::fs::path const& filename);
std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> source_provider);
std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> source_provider);
std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> source_provider, agi::BackgroundRunner *br);
std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> source_provider, agi::BackgroundRunner *br);
namespace {
struct provider_creator {
using factory_fn = std::unique_ptr<AudioProvider> (*)(agi::fs::path const&);
struct factory {
const char *name;
factory_fn create;
bool hidden;
};
const factory providers[] = {
{"Dummy", CreateDummyAudioProvider, true},
{"PCM", CreatePCMAudioProvider, true},
#ifdef WITH_FFMS2
{"FFmpegSource", CreateFFmpegSourceAudioProvider, false},
#endif
#ifdef WITH_AVISYNTH
{"Avisynth", CreateAvisynthAudioProvider, false},
#endif
};
}
std::vector<std::string> AudioProviderFactory::GetClasses() {
std::vector<std::string> list;
for (auto const& provider : providers) {
if (!provider.hidden)
list.push_back(provider.name);
}
return list;
}
std::unique_ptr<AudioProvider> AudioProviderFactory::GetProvider(agi::fs::path const& filename) {
auto preferred = OPT_GET("Audio/Provider")->GetString();
std::vector<const factory *> sorted;
auto preferred_insertion_point = sorted.end();
for (auto const& provider : providers) {
if (provider.hidden)
sorted.push_back(&provider);
else if (preferred_insertion_point == sorted.end()) {
sorted.push_back(&provider);
preferred_insertion_point = prev(sorted.end());
}
else if (preferred == provider.name)
sorted.insert(preferred_insertion_point, &provider);
else
sorted.push_back(&provider);
}
std::unique_ptr<AudioProvider> provider;
bool found_file = false;
bool found_audio = false;
std::string msg;
template<typename Factory>
std::unique_ptr<AudioProvider> try_create(std::string const& name, Factory&& create) {
for (auto const& factory : sorted) {
try {
std::unique_ptr<AudioProvider> provider = create();
if (provider)
LOG_I("audio_provider") << "Using audio provider: " << name;
return provider;
provider = factory->create(filename);
if (!provider) continue;
LOG_I("audio_provider") << "Using audio provider: " << factory->name;
break;
}
catch (agi::fs::FileNotFound const& err) {
LOG_D("audio_provider") << err.GetChainedMessage();
msg += name + ": " + err.GetMessage() + " not found.\n";
msg += std::string(factory->name) + ": " + err.GetMessage() + " not found.\n";
}
catch (agi::AudioDataNotFoundError const& err) {
LOG_D("audio_provider") << err.GetChainedMessage();
found_file = true;
msg += name + ": " + err.GetChainedMessage() + "\n";
msg += std::string(factory->name) + ": " + err.GetChainedMessage() + "\n";
}
catch (agi::AudioOpenError const& err) {
LOG_D("audio_provider") << err.GetChainedMessage();
found_audio = true;
found_file = true;
msg += name + ": " + err.GetChainedMessage() + "\n";
}
return nullptr;
}
};
}
std::unique_ptr<AudioProvider> AudioProviderFactory::GetProvider(agi::fs::path const& filename) {
provider_creator creator;
std::unique_ptr<AudioProvider> provider;
provider = creator.try_create("Dummy audio provider", [&]() {
return agi::util::make_unique<DummyAudioProvider>(filename);
});
// Try a PCM provider first
if (!provider)
provider = creator.try_create("PCM audio provider", [&]() { return CreatePCMAudioProvider(filename); });
if (!provider) {
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Provider")->GetString());
if (list.empty()) throw agi::NoAudioProvidersError("No audio providers are available.", nullptr);
for (auto const& name : list) {
provider = creator.try_create(name, [&]() { return Create(name, filename); });
if (provider) break;
msg += std::string(factory->name) + ": " + err.GetChainedMessage() + "\n";
}
}
if (!provider) {
if (creator.found_audio)
throw agi::AudioProviderOpenError(creator.msg, nullptr);
if (creator.found_file)
throw agi::AudioDataNotFoundError(creator.msg, nullptr);
if (found_audio)
throw agi::AudioProviderOpenError(msg, nullptr);
if (found_file)
throw agi::AudioDataNotFoundError(msg, nullptr);
throw agi::fs::FileNotFound(filename);
}
@ -184,24 +203,15 @@ std::unique_ptr<AudioProvider> AudioProviderFactory::GetProvider(agi::fs::path c
// Change provider to RAM/HD cache if needed
int cache = OPT_GET("Audio/Cache/Type")->GetInt();
if (!cache || !needsCache)
return agi::util::make_unique<LockAudioProvider>(std::move(provider));
return CreateLockAudioProvider(std::move(provider));
DialogProgress progress(wxGetApp().frame, _("Load audio"));
// Convert to RAM
if (cache == 1) return agi::util::make_unique<RAMAudioProvider>(std::move(provider), &progress);
if (cache == 1) return CreateRAMAudioProvider(std::move(provider), &progress);
// Convert to HD
if (cache == 2) return agi::util::make_unique<HDAudioProvider>(std::move(provider), &progress);
if (cache == 2) return CreateHDAudioProvider(std::move(provider), &progress);
throw agi::AudioCacheOpenError("Unknown caching method", nullptr);
}
void AudioProviderFactory::RegisterProviders() {
#ifdef WITH_AVISYNTH
Register<AvisynthAudioProvider>("Avisynth");
#endif
#ifdef WITH_FFMS2
Register<FFmpegSourceAudioProvider>("FFmpegSource");
#endif
}

View File

@ -35,8 +35,10 @@
#include "config.h"
#ifdef WITH_AVISYNTH
#include "audio_provider_avs.h"
#include "include/aegisub/audio_provider.h"
#include "avisynth.h"
#include "avisynth_wrap.h"
#include "audio_controller.h"
#include "options.h"
#include "utils.h"
@ -45,9 +47,24 @@
#include <libaegisub/charset_conv.h>
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/util.h>
#include <mutex>
namespace {
class AvisynthAudioProvider final : public AudioProvider {
AviSynthWrapper avs_wrapper;
PClip clip;
void LoadFromClip(AVSValue clip);
void FillBuffer(void *buf, int64_t start, int64_t count) const;
public:
AvisynthAudioProvider(agi::fs::path const& filename);
bool NeedsCache() const override { return true; }
};
AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) {
this->filename = filename;
@ -128,4 +145,9 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue clip) {
void AvisynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
clip->GetAudio(buf, start, count, avs_wrapper.GetEnv());
}
}
std::unique_ptr<AudioProvider> CreateAvisynthAudioProvider(agi::fs::path const& file) {
return agi::util::make_unique<AvisynthAudioProvider>(file);
}
#endif

View File

@ -1,53 +0,0 @@
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
// 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_provider_avs.h
/// @see audio_provider_avs.cpp
/// @ingroup audio_input
///
#ifdef WITH_AVISYNTH
#include "include/aegisub/audio_provider.h"
#include "avisynth.h"
#include "avisynth_wrap.h"
class AvisynthAudioProvider final : public AudioProvider {
AviSynthWrapper avs_wrapper;
PClip clip;
void LoadFromClip(AVSValue clip);
void FillBuffer(void *buf, int64_t start, int64_t count) const;
public:
AvisynthAudioProvider(agi::fs::path const& filename);
bool NeedsCache() const { return true; }
};
#endif

View File

@ -21,10 +21,9 @@
#include "config.h"
#include "audio_provider_convert.h"
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include <libaegisub/log.h>
#include <libaegisub/util.h>

View File

@ -1,27 +0,0 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file audio_provider_convert.h
/// @see audio_provider_convert.cpp
/// @ingroup audio_input
///
#include <memory>
class AudioProvider;
/// Get an audio provider which supplies audio in a format supported by Aegisub's players
std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> source_provider);

View File

@ -34,10 +34,10 @@
#include "config.h"
#include "audio_provider_dummy.h"
#include "utils.h"
#include "include/aegisub/audio_provider.h"
#include <libaegisub/fs.h>
#include <libaegisub/util.h>
#include <boost/algorithm/string/predicate.hpp>
@ -61,25 +61,35 @@
* in every channel even if one would be LFE.
* "ln", length of signal in samples. ln/sr gives signal length in seconds.
*/
DummyAudioProvider::DummyAudioProvider(agi::fs::path const& uri) {
if (!boost::starts_with(uri.string(), "dummy-audio:"))
throw agi::fs::FileNotFound(std::string("Not a dummy audio URI"));
noise = boost::contains(uri.string(), ":noise?");
channels = 1;
sample_rate = 44100;
bytes_per_sample = 2;
float_samples = false;
num_samples = (int64_t)5*30*60*1000 * sample_rate / 1000;
namespace {
class DummyAudioProvider final : public AudioProvider {
bool noise;
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
if (noise) {
auto workbuf = static_cast<uint16_t *>(buf);
while (count-- > 0)
*workbuf++ = (rand() - RAND_MAX/2) * 10000 / RAND_MAX;
}
else {
memset(buf, 0, count * bytes_per_sample);
}
}
public:
DummyAudioProvider(agi::fs::path const& uri) {
noise = boost::contains(uri.string(), ":noise?");
channels = 1;
sample_rate = 44100;
bytes_per_sample = 2;
float_samples = false;
num_samples = (int64_t)5*30*60*1000 * sample_rate / 1000;
}
};
}
void DummyAudioProvider::FillBuffer(void *buf, int64_t, int64_t count) const {
if (noise) {
short *workbuf = (short*)buf;
while (count-- > 0)
*workbuf++ = (rand() - RAND_MAX/2) * 10000 / RAND_MAX;
}
else {
memset(buf, 0, count * bytes_per_sample);
}
std::unique_ptr<AudioProvider> CreateDummyAudioProvider(agi::fs::path const& file) {
if (!boost::starts_with(file.string(), "dummy-audio:"))
return {};
return agi::util::make_unique<DummyAudioProvider>(file);
}

View File

@ -1,43 +0,0 @@
// Copyright (c) 2006, 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_provider_dummy.h
/// @see audio_provider_dummy.cpp
/// @ingroup audio_input
///
#include "include/aegisub/audio_provider.h"
class DummyAudioProvider final : public AudioProvider {
bool noise;
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
public:
DummyAudioProvider(agi::fs::path const& uri);
};

View File

@ -35,15 +35,37 @@
#include "config.h"
#ifdef WITH_FFMS2
#include "audio_provider_ffmpegsource.h"
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "ffmpegsource_common.h"
#include "options.h"
#include <libaegisub/fs.h>
#include <libaegisub/util.h>
#include <map>
namespace {
class FFmpegSourceAudioProvider final : public AudioProvider, FFmpegSourceProvider {
/// audio source object
agi::scoped_holder<FFMS_AudioSource*, void (FFMS_CC *)(FFMS_AudioSource*)> AudioSource;
mutable char FFMSErrMsg[1024]; ///< FFMS error message
mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
void LoadAudio(agi::fs::path const& filename);
void FillBuffer(void *Buf, int64_t Start, int64_t Count) const override {
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo))
throw AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer);
}
public:
FFmpegSourceAudioProvider(agi::fs::path const& filename);
bool NeedsCache() const override { return true; }
};
/// @brief Constructor
/// @param filename The filename to open
FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(agi::fs::path const& filename) try
@ -182,8 +204,10 @@ void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) {
#endif
}
void FFmpegSourceAudioProvider::FillBuffer(void *Buf, int64_t Start, int64_t Count) const {
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo))
throw AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer);
}
std::unique_ptr<AudioProvider> CreateFFmpegSourceAudioProvider(agi::fs::path const& file) {
return agi::util::make_unique<FFmpegSourceAudioProvider>(file);
}
#endif /* WITH_FFMS2 */

View File

@ -1,57 +0,0 @@
// Copyright (c) 2008-2009, Karl Blomster
// 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_provider_ffmpegsource.h
/// @see audio_provider_ffmpegsource.cpp
/// @ingroup audio_input ffms
///
#ifdef WITH_FFMS2
#include "include/aegisub/audio_provider.h"
#include "ffmpegsource_common.h"
/// @class FFmpegSourceAudioProvider
/// @brief Implements audio loading with the FFMS library.
class FFmpegSourceAudioProvider final : public AudioProvider, FFmpegSourceProvider {
/// audio source object
agi::scoped_holder<FFMS_AudioSource*, void (FFMS_CC *)(FFMS_AudioSource*)> AudioSource;
mutable char FFMSErrMsg[1024]; ///< FFMS error message
mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
void LoadAudio(agi::fs::path const& filename);
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
public:
FFmpegSourceAudioProvider(agi::fs::path const& filename);
bool NeedsCache() const override { return true; }
};
#endif

View File

@ -16,7 +16,7 @@
#include "config.h"
#include "audio_provider_hd.h"
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "compat.h"
@ -33,43 +33,53 @@
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/interprocess/detail/os_thread_functions.hpp>
#include <wx/intl.h>
HDAudioProvider::HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::BackgroundRunner *br)
: AudioProviderWrapper(std::move(src))
{
auto path = OPT_GET("Audio/Cache/HD/Location")->GetString();
if (path == "default")
path = "?temp";
auto cache_dir = config::path->MakeAbsolute(config::path->Decode(path), "?temp");
namespace {
class HDAudioProvider final : public AudioProviderWrapper {
std::unique_ptr<agi::temp_file_mapping> file;
auto bps = bytes_per_sample * channels;
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
start *= channels * bytes_per_sample;
count *= channels * bytes_per_sample;
memcpy(buf, file->read(start, count), count);
}
// Check free space
if ((uint64_t)num_samples * bps > agi::fs::FreeSpace(cache_dir))
throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir.string() + " to cache the audio", nullptr);
public:
HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::BackgroundRunner *br)
: AudioProviderWrapper(std::move(src))
{
auto path = OPT_GET("Audio/Cache/HD/Location")->GetString();
if (path == "default")
path = "?temp";
auto cache_dir = config::path->MakeAbsolute(config::path->Decode(path), "?temp");
auto filename = str(boost::format("audio-%lld-%lld")
% (long long)time(nullptr)
% (long long)boost::interprocess::ipcdetail::get_current_process_id());
auto bps = bytes_per_sample * channels;
file = agi::util::make_unique<agi::temp_file_mapping>(cache_dir / filename, num_samples * bps);
br->Run([&] (agi::ProgressSink *ps) {
ps->SetMessage(from_wx(_("Reading to Hard Disk cache")));
// Check free space
if ((uint64_t)num_samples * bps > agi::fs::FreeSpace(cache_dir))
throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir.string() + " to cache the audio", nullptr);
int64_t block = 65536;
for (int64_t i = 0; i < num_samples; i += block) {
block = std::min(block, num_samples - i);
source->GetAudio(file->write(i * bps, block * bps), i, block);
ps->SetProgress(i, num_samples);
if (ps->IsCancelled()) return;
}
});
auto filename = str(boost::format("audio-%lld-%lld")
% (long long)time(nullptr)
% (long long)boost::interprocess::ipcdetail::get_current_process_id());
file = agi::util::make_unique<agi::temp_file_mapping>(cache_dir / filename, num_samples * bps);
br->Run([&] (agi::ProgressSink *ps) {
ps->SetMessage(from_wx(_("Reading to Hard Disk cache")));
int64_t block = 65536;
for (int64_t i = 0; i < num_samples; i += block) {
block = std::min(block, num_samples - i);
source->GetAudio(file->write(i * bps, block * bps), i, block);
ps->SetProgress(i, num_samples);
if (ps->IsCancelled()) return;
}
});
}
};
}
HDAudioProvider::~HDAudioProvider() { }
void HDAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
start *= channels * bytes_per_sample;
count *= channels * bytes_per_sample;
memcpy(buf, file->read(start, count), count);
std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> src, agi::BackgroundRunner *br) {
return agi::util::make_unique<HDAudioProvider>(std::move(src), br);
}

View File

@ -1,32 +0,0 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "include/aegisub/audio_provider.h"
namespace agi {
class BackgroundRunner;
class temp_file_mapping;
}
class HDAudioProvider final : public AudioProviderWrapper {
std::unique_ptr<agi::temp_file_mapping> file;
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
public:
HDAudioProvider(std::unique_ptr<AudioProvider> source, agi::BackgroundRunner *br);
~HDAudioProvider();
};

View File

@ -18,14 +18,30 @@
#include "config.h"
#include "audio_provider_lock.h"
#include "include/aegisub/audio_provider.h"
LockAudioProvider::LockAudioProvider(std::unique_ptr<AudioProvider> src)
: AudioProviderWrapper(std::move(src))
{
#include <libaegisub/util.h>
#include <memory>
#include <mutex>
namespace {
class LockAudioProvider final : public AudioProviderWrapper {
mutable std::mutex mutex;
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
std::unique_lock<std::mutex> lock(mutex);
source->GetAudio(buf, start, count);
}
public:
LockAudioProvider(std::unique_ptr<AudioProvider> src)
: AudioProviderWrapper(std::move(src))
{
}
};
}
void LockAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
std::unique_lock<std::mutex> lock(mutex);
source->GetAudio(buf, start, count);
}
std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> src) {
return agi::util::make_unique<LockAudioProvider>(std::move(src));
}

View File

@ -1,30 +0,0 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// @file audio_provider_lock.h
/// @brief An audio provider adapter for un-threadsafe audio providers
/// @ingroup audio_input
#include "include/aegisub/audio_provider.h"
#include <memory>
#include <mutex>
class LockAudioProvider final : public AudioProviderWrapper {
mutable std::mutex mutex;
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
public:
LockAudioProvider(std::unique_ptr<AudioProvider> source);
};

View File

@ -34,44 +34,55 @@
#include "config.h"
#include "audio_provider_ram.h"
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "compat.h"
#include "options.h"
#include "utils.h"
#include <libaegisub/background_runner.h>
#include <libaegisub/util.h>
#include <array>
#include <boost/container/stable_vector.hpp>
#include <wx/intl.h>
namespace {
#define CacheBits 22
#define CacheBlockSize (1 << CacheBits)
RAMAudioProvider::RAMAudioProvider(std::unique_ptr<AudioProvider> src, agi::BackgroundRunner *br)
: AudioProviderWrapper(std::move(src))
{
try {
blockcache.resize((source->GetNumSamples() * source->GetBytesPerSample() + CacheBlockSize - 1) >> CacheBits);
}
catch (std::bad_alloc const&) {
throw agi::AudioCacheOpenError("Couldn't open audio, not enough ram available.", nullptr);
}
class RAMAudioProvider final : public AudioProviderWrapper {
#ifdef _MSC_VER
boost::container::stable_vector<char[1 << 22]> blockcache;
#else
boost::container::stable_vector<std::array<char, 1 << 22>> blockcache;
#endif
br->Run(std::bind(&RAMAudioProvider::FillCache, this, source.get(), std::placeholders::_1));
}
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
void RAMAudioProvider::FillCache(AudioProvider *source, agi::ProgressSink *ps) {
ps->SetMessage(from_wx(_("Reading into RAM")));
int64_t readsize = CacheBlockSize / source->GetBytesPerSample();
for (size_t i = 0; i < blockcache.size(); i++) {
ps->SetProgress(i + 1, blockcache.size());
source->GetAudio(&blockcache[i][0], i * readsize, std::min<int64_t>(readsize, num_samples - i * readsize));
if (ps->IsCancelled()) {
return;
public:
RAMAudioProvider(std::unique_ptr<AudioProvider> src, agi::BackgroundRunner *br)
: AudioProviderWrapper(std::move(src))
{
try {
blockcache.resize((source->GetNumSamples() * source->GetBytesPerSample() + CacheBlockSize - 1) >> CacheBits);
}
catch (std::bad_alloc const&) {
throw agi::AudioCacheOpenError("Couldn't open audio, not enough ram available.", nullptr);
}
br->Run([&](agi::ProgressSink *ps) {
ps->SetMessage(from_wx(_("Reading into RAM")));
int64_t readsize = CacheBlockSize / source->GetBytesPerSample();
for (size_t i = 0; i < blockcache.size(); i++) {
if (ps->IsCancelled()) return;
ps->SetProgress(i + 1, blockcache.size());
source->GetAudio(&blockcache[i][0], i * readsize, std::min<int64_t>(readsize, num_samples - i * readsize));
}
});
}
}
};
void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
char *charbuf = static_cast<char *>(buf);
@ -90,3 +101,8 @@ void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const
bytesremaining -= readsize;
}
}
}
std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> src, agi::BackgroundRunner *br) {
return agi::util::make_unique<RAMAudioProvider>(std::move(src), br);
}

View File

@ -1,57 +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_provider_ram.h
/// @see audio_provider_ram.cpp
/// @ingroup audio_input
///
#include "include/aegisub/audio_provider.h"
#include <array>
#include <boost/container/stable_vector.hpp>
namespace agi {
class BackgroundRunner;
class ProgressSink;
}
class RAMAudioProvider final : public AudioProviderWrapper {
#ifdef _MSC_VER
boost::container::stable_vector<char[1 << 22]> blockcache;
#else
boost::container::stable_vector<std::array<char, 1 << 22>> blockcache;
#endif
void FillCache(AudioProvider *source, agi::ProgressSink *ps);
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
public:
RAMAudioProvider(std::unique_ptr<AudioProvider> source, agi::BackgroundRunner *br);
};

View File

@ -34,8 +34,6 @@
#pragma once
#include "factory_manager.h"
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
@ -90,9 +88,8 @@ public:
}
};
class AudioProviderFactory final : public Factory<AudioProvider, agi::fs::path> {
public:
static void RegisterProviders();
struct AudioProviderFactory {
static std::vector<std::string> GetClasses();
/// Get a provider for the file
/// @param filename URI to open

View File

@ -35,7 +35,6 @@
#include "config.h"
#include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include "include/aegisub/spellchecker.h"
#include "include/aegisub/subtitles_provider.h"
#include "plugin_manager.h"
@ -46,7 +45,6 @@
void RegisterBuiltInPlugins() {
VideoProviderFactory::RegisterProviders();
AudioProviderFactory::RegisterProviders();
AudioPlayerFactory::RegisterProviders();
SubtitlesProviderFactory::RegisterProviders();
SpellCheckerFactory::RegisterProviders();