mirror of https://github.com/odrling/Aegisub
Add support for floating-point audio. Closes #1490.
Originally committed to SVN as r6904.
This commit is contained in:
parent
5eb14a1f7a
commit
b8c6a41ac4
|
@ -146,6 +146,7 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
|
|||
num_samples = vi.num_audio_samples;
|
||||
sample_rate = vi.SamplesPerSecond();
|
||||
bytes_per_sample = vi.BytesPerAudioSample();
|
||||
float_samples = false;
|
||||
|
||||
clip = tempclip;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <limits>
|
||||
#endif
|
||||
|
||||
/// Base class for all wrapping converters
|
||||
class AudioProviderConverter : public AudioProvider {
|
||||
protected:
|
||||
|
@ -41,13 +45,14 @@ public:
|
|||
num_samples = source->GetNumSamples();
|
||||
sample_rate = source->GetSampleRate();
|
||||
bytes_per_sample = source->GetBytesPerSample();
|
||||
float_samples = source->AreSamplesFloat();
|
||||
}
|
||||
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
wxString GetFilename() const { return source->GetFilename(); }
|
||||
};
|
||||
|
||||
/// Anything -> 16 bit signed machine-endian audio converter
|
||||
/// Anything integral -> 16 bit signed machine-endian audio converter
|
||||
template<class Target>
|
||||
class BitdepthConvertAudioProvider : public AudioProviderConverter {
|
||||
int src_bytes_per_sample;
|
||||
|
@ -104,6 +109,40 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/// Floating point -> 16 bit signed machine-endian audio converter
|
||||
template<class Source, class Target>
|
||||
class FloatConvertAudioProvider : public AudioProviderConverter {
|
||||
public:
|
||||
FloatConvertAudioProvider(AudioProvider *src) : AudioProviderConverter(src) {
|
||||
if (!src->AreSamplesNativeEndian())
|
||||
throw agi::AudioProviderOpenError("Audio format converter: Float audio with non-native endianness is currently unsupported.", 0);
|
||||
bytes_per_sample = sizeof(Target);
|
||||
float_samples = false;
|
||||
}
|
||||
|
||||
void GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||
std::vector<Source> src_buf(count * channels);
|
||||
source->GetAudio(&src_buf[0], start, count);
|
||||
|
||||
Target *dest = reinterpret_cast<Target*>(buf);
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(count * channels); ++i) {
|
||||
Source expanded;
|
||||
if (src_buf[i] < 0)
|
||||
expanded = static_cast<Target>(-src_buf[i] * std::numeric_limits<Target>::min());
|
||||
else
|
||||
expanded = static_cast<Target>(src_buf[i] * std::numeric_limits<Target>::max());
|
||||
|
||||
if (expanded < std::numeric_limits<Target>::min())
|
||||
dest[i] = std::numeric_limits<Target>::min();
|
||||
else if (expanded > std::numeric_limits<Target>::max())
|
||||
dest[i] = std::numeric_limits<Target>::max();
|
||||
else
|
||||
dest[i] = static_cast<Target>(expanded);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Non-mono 16-bit signed machine-endian -> mono 16-bit signed machine endian converter
|
||||
class DownmixAudioProvider : public AudioProviderConverter {
|
||||
int src_channels;
|
||||
|
@ -178,6 +217,12 @@ AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider) {
|
|||
AudioProvider *provider = source_provider;
|
||||
|
||||
// Ensure 16-bit audio with proper endianness
|
||||
if (provider->AreSamplesFloat()) {
|
||||
if (provider->GetBytesPerSample() == sizeof(float))
|
||||
provider = new FloatConvertAudioProvider<float, int16_t>(provider);
|
||||
else
|
||||
provider = new FloatConvertAudioProvider<double, int16_t>(provider);
|
||||
}
|
||||
if (provider->GetBytesPerSample() != 2 || !provider->AreSamplesNativeEndian())
|
||||
provider = new BitdepthConvertAudioProvider<int16_t>(provider);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ DummyAudioProvider::DummyAudioProvider(unsigned long dur_ms, bool _noise) {
|
|||
channels = 1;
|
||||
sample_rate = 44100;
|
||||
bytes_per_sample = 2;
|
||||
float_samples = false;
|
||||
num_samples = (int64_t)dur_ms * sample_rate / 1000;
|
||||
}
|
||||
|
||||
|
|
|
@ -161,13 +161,12 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
|
|||
if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
|
||||
throw agi::AudioProviderOpenError("sanity check failed, consult your local psychiatrist", 0);
|
||||
|
||||
// FIXME: use the actual sample format too?
|
||||
// why not just bits_per_sample/8? maybe there's some oddball format with half bytes out there somewhere...
|
||||
switch (AudioInfo.BitsPerSample) {
|
||||
case 8: bytes_per_sample = 1; break;
|
||||
case 16: bytes_per_sample = 2; break;
|
||||
case 24: bytes_per_sample = 3; break;
|
||||
case 32: bytes_per_sample = 4; break;
|
||||
switch (AudioInfo.SampleFormat) {
|
||||
case FFMS_FMT_U8: bytes_per_sample = 1; float_samples = false; break;
|
||||
case FFMS_FMT_S16: bytes_per_sample = 2; float_samples = false; break;
|
||||
case FFMS_FMT_S32: bytes_per_sample = 4; float_samples = false; break;
|
||||
case FFMS_FMT_FLT: bytes_per_sample = 4; float_samples = true; break;
|
||||
case FFMS_FMT_DBL: bytes_per_sample = 8; float_samples = true; break;
|
||||
default:
|
||||
throw agi::AudioProviderOpenError("unknown or unsupported sample format", 0);
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ public:
|
|||
channels = src->GetChannels();
|
||||
sample_rate = src->GetSampleRate();
|
||||
filename = src->GetFilename();
|
||||
float_samples = src->AreSamplesFloat();
|
||||
|
||||
IndexPoint p = { 0, 0, num_samples };
|
||||
index_points.push_back(p);
|
||||
|
@ -112,6 +113,7 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
|
|||
channels = source->GetChannels();
|
||||
sample_rate = source->GetSampleRate();
|
||||
filename = source->GetFilename();
|
||||
float_samples = source->AreSamplesFloat();
|
||||
|
||||
diskCacheFilename = cache_path();
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
|
|||
|
||||
current_mapping = 0;
|
||||
#endif
|
||||
float_samples = false;
|
||||
}
|
||||
|
||||
/// @brief DOCME
|
||||
|
|
|
@ -78,6 +78,7 @@ RAMAudioProvider::RAMAudioProvider(AudioProvider *src, agi::BackgroundRunner *br
|
|||
channels = source->GetChannels();
|
||||
sample_rate = source->GetSampleRate();
|
||||
filename = source->GetFilename();
|
||||
float_samples = source->AreSamplesFloat();
|
||||
|
||||
br->Run(std::tr1::bind(&RAMAudioProvider::FillCache, this, src, std::tr1::placeholders::_1));
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ protected:
|
|||
/// DOCME
|
||||
int channels;
|
||||
|
||||
/// DOCME
|
||||
int64_t num_samples; // for one channel, ie. number of PCM frames
|
||||
/// for one channel, ie. number of PCM frames
|
||||
int64_t num_samples;
|
||||
|
||||
/// DOCME
|
||||
int sample_rate;
|
||||
|
@ -61,6 +61,8 @@ protected:
|
|||
/// DOCME
|
||||
int bytes_per_sample;
|
||||
|
||||
bool float_samples;
|
||||
|
||||
|
||||
/// DOCME
|
||||
wxString filename;
|
||||
|
@ -76,6 +78,7 @@ public:
|
|||
int GetSampleRate() const { return sample_rate; }
|
||||
int GetBytesPerSample() const { return bytes_per_sample; }
|
||||
int GetChannels() const { return channels; }
|
||||
bool AreSamplesFloat() const { return float_samples; }
|
||||
virtual bool AreSamplesNativeEndian() const = 0;
|
||||
|
||||
/// @brief Does this provider benefit from external caching?
|
||||
|
|
Loading…
Reference in New Issue