Add support for floating-point audio. Closes #1490.

Originally committed to SVN as r6904.
This commit is contained in:
Thomas Goyne 2012-06-13 15:58:28 +00:00
parent 5eb14a1f7a
commit b8c6a41ac4
8 changed files with 63 additions and 10 deletions

View File

@ -146,6 +146,7 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue _clip) {
num_samples = vi.num_audio_samples; num_samples = vi.num_audio_samples;
sample_rate = vi.SamplesPerSecond(); sample_rate = vi.SamplesPerSecond();
bytes_per_sample = vi.BytesPerAudioSample(); bytes_per_sample = vi.BytesPerAudioSample();
float_samples = false;
clip = tempclip; clip = tempclip;
} }

View File

@ -31,6 +31,10 @@
#include <libaegisub/scoped_ptr.h> #include <libaegisub/scoped_ptr.h>
#ifndef AGI_PRE
#include <limits>
#endif
/// Base class for all wrapping converters /// Base class for all wrapping converters
class AudioProviderConverter : public AudioProvider { class AudioProviderConverter : public AudioProvider {
protected: protected:
@ -41,13 +45,14 @@ public:
num_samples = source->GetNumSamples(); num_samples = source->GetNumSamples();
sample_rate = source->GetSampleRate(); sample_rate = source->GetSampleRate();
bytes_per_sample = source->GetBytesPerSample(); bytes_per_sample = source->GetBytesPerSample();
float_samples = source->AreSamplesFloat();
} }
bool AreSamplesNativeEndian() const { return true; } bool AreSamplesNativeEndian() const { return true; }
wxString GetFilename() const { return source->GetFilename(); } 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> template<class Target>
class BitdepthConvertAudioProvider : public AudioProviderConverter { class BitdepthConvertAudioProvider : public AudioProviderConverter {
int src_bytes_per_sample; 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 /// Non-mono 16-bit signed machine-endian -> mono 16-bit signed machine endian converter
class DownmixAudioProvider : public AudioProviderConverter { class DownmixAudioProvider : public AudioProviderConverter {
int src_channels; int src_channels;
@ -178,6 +217,12 @@ AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider) {
AudioProvider *provider = source_provider; AudioProvider *provider = source_provider;
// Ensure 16-bit audio with proper endianness // 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()) if (provider->GetBytesPerSample() != 2 || !provider->AreSamplesNativeEndian())
provider = new BitdepthConvertAudioProvider<int16_t>(provider); provider = new BitdepthConvertAudioProvider<int16_t>(provider);

View File

@ -44,6 +44,7 @@ DummyAudioProvider::DummyAudioProvider(unsigned long dur_ms, bool _noise) {
channels = 1; channels = 1;
sample_rate = 44100; sample_rate = 44100;
bytes_per_sample = 2; bytes_per_sample = 2;
float_samples = false;
num_samples = (int64_t)dur_ms * sample_rate / 1000; num_samples = (int64_t)dur_ms * sample_rate / 1000;
} }

View File

@ -161,13 +161,12 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
if (channels <= 0 || sample_rate <= 0 || num_samples <= 0) if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
throw agi::AudioProviderOpenError("sanity check failed, consult your local psychiatrist", 0); throw agi::AudioProviderOpenError("sanity check failed, consult your local psychiatrist", 0);
// FIXME: use the actual sample format too? switch (AudioInfo.SampleFormat) {
// why not just bits_per_sample/8? maybe there's some oddball format with half bytes out there somewhere... case FFMS_FMT_U8: bytes_per_sample = 1; float_samples = false; break;
switch (AudioInfo.BitsPerSample) { case FFMS_FMT_S16: bytes_per_sample = 2; float_samples = false; break;
case 8: bytes_per_sample = 1; break; case FFMS_FMT_S32: bytes_per_sample = 4; float_samples = false; break;
case 16: bytes_per_sample = 2; break; case FFMS_FMT_FLT: bytes_per_sample = 4; float_samples = true; break;
case 24: bytes_per_sample = 3; break; case FFMS_FMT_DBL: bytes_per_sample = 8; float_samples = true; break;
case 32: bytes_per_sample = 4; break;
default: default:
throw agi::AudioProviderOpenError("unknown or unsupported sample format", 0); throw agi::AudioProviderOpenError("unknown or unsupported sample format", 0);
} }

View File

@ -86,6 +86,7 @@ public:
channels = src->GetChannels(); channels = src->GetChannels();
sample_rate = src->GetSampleRate(); sample_rate = src->GetSampleRate();
filename = src->GetFilename(); filename = src->GetFilename();
float_samples = src->AreSamplesFloat();
IndexPoint p = { 0, 0, num_samples }; IndexPoint p = { 0, 0, num_samples };
index_points.push_back(p); index_points.push_back(p);
@ -112,6 +113,7 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
channels = source->GetChannels(); channels = source->GetChannels();
sample_rate = source->GetSampleRate(); sample_rate = source->GetSampleRate();
filename = source->GetFilename(); filename = source->GetFilename();
float_samples = source->AreSamplesFloat();
diskCacheFilename = cache_path(); diskCacheFilename = cache_path();

View File

@ -114,6 +114,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
current_mapping = 0; current_mapping = 0;
#endif #endif
float_samples = false;
} }
/// @brief DOCME /// @brief DOCME

View File

@ -78,6 +78,7 @@ RAMAudioProvider::RAMAudioProvider(AudioProvider *src, agi::BackgroundRunner *br
channels = source->GetChannels(); channels = source->GetChannels();
sample_rate = source->GetSampleRate(); sample_rate = source->GetSampleRate();
filename = source->GetFilename(); filename = source->GetFilename();
float_samples = source->AreSamplesFloat();
br->Run(std::tr1::bind(&RAMAudioProvider::FillCache, this, src, std::tr1::placeholders::_1)); br->Run(std::tr1::bind(&RAMAudioProvider::FillCache, this, src, std::tr1::placeholders::_1));
} }

View File

@ -52,8 +52,8 @@ protected:
/// DOCME /// DOCME
int channels; int channels;
/// DOCME /// for one channel, ie. number of PCM frames
int64_t num_samples; // for one channel, ie. number of PCM frames int64_t num_samples;
/// DOCME /// DOCME
int sample_rate; int sample_rate;
@ -61,6 +61,8 @@ protected:
/// DOCME /// DOCME
int bytes_per_sample; int bytes_per_sample;
bool float_samples;
/// DOCME /// DOCME
wxString filename; wxString filename;
@ -76,6 +78,7 @@ public:
int GetSampleRate() const { return sample_rate; } int GetSampleRate() const { return sample_rate; }
int GetBytesPerSample() const { return bytes_per_sample; } int GetBytesPerSample() const { return bytes_per_sample; }
int GetChannels() const { return channels; } int GetChannels() const { return channels; }
bool AreSamplesFloat() const { return float_samples; }
virtual bool AreSamplesNativeEndian() const = 0; virtual bool AreSamplesNativeEndian() const = 0;
/// @brief Does this provider benefit from external caching? /// @brief Does this provider benefit from external caching?