From ce015fc820359168f79d097da2780e854547faf7 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Thu, 4 Jun 2009 23:02:29 +0000 Subject: [PATCH] Add sample endianness info to all audio providers (except one, intentionally; see the stream provider changeset), and make the converting audio provider convert to native endian when required. Updates #725 but needs some testing. Might break compilation in some places, but shouldn't. ("Works for me.") Originally committed to SVN as r3016. --- aegisub/src/audio_provider_avs.h | 3 ++ aegisub/src/audio_provider_convert.cpp | 56 ++++++++++++++++---- aegisub/src/audio_provider_convert.h | 7 ++- aegisub/src/audio_provider_downmix.cpp | 2 + aegisub/src/audio_provider_downmix.h | 3 ++ aegisub/src/audio_provider_dummy.h | 2 + aegisub/src/audio_provider_ffmpegsource.h | 3 ++ aegisub/src/audio_provider_hd.cpp | 1 + aegisub/src/audio_provider_hd.h | 3 ++ aegisub/src/audio_provider_lavc.h | 4 ++ aegisub/src/audio_provider_pcm.cpp | 20 +++++++ aegisub/src/audio_provider_ram.cpp | 1 + aegisub/src/audio_provider_ram.h | 3 ++ aegisub/src/audio_provider_stream.h | 6 +++ aegisub/src/include/aegisub/audio_provider.h | 1 + 15 files changed, 104 insertions(+), 11 deletions(-) diff --git a/aegisub/src/audio_provider_avs.h b/aegisub/src/audio_provider_avs.h index eb406dc59..cdcccf699 100644 --- a/aegisub/src/audio_provider_avs.h +++ b/aegisub/src/audio_provider_avs.h @@ -61,6 +61,9 @@ public: wxString GetFilename(); + // Only exists for x86 Windows, always delivers machine (little) endian + bool AreSamplesNativeEndian() { return true; } + void GetAudio(void *buf, int64_t start, int64_t count); void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale); }; diff --git a/aegisub/src/audio_provider_convert.cpp b/aegisub/src/audio_provider_convert.cpp index 9f3bd244b..cc5ae870e 100644 --- a/aegisub/src/audio_provider_convert.cpp +++ b/aegisub/src/audio_provider_convert.cpp @@ -40,6 +40,7 @@ #include "audio_provider_convert.h" #include "audio_provider_downmix.h" +#include "aegisub_endian.h" /////////////// @@ -70,7 +71,7 @@ ConvertAudioProvider::~ConvertAudioProvider() { // Convert to 16-bit void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count) { for (int64_t i=0;i +void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter) { // Upsample by 2 if (sampleMult == 2) { int64_t size = count/2; @@ -86,7 +89,7 @@ void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_ short next = 0; for (int64_t i=0;i 0) { + *dst++ = converter(*src++); + } + } } +// Do-nothing sample converter for ChangeSampleRate +struct NullSampleConverter { + inline short operator()(const short val) const { + return val; + } +}; + +// Endian-swapping sample converter for ChangeSampleRate +struct EndianSwapSampleConverter { + inline short operator()(const short val) const { + return (short)Endian::Reverse((uint16_t)val); + }; +}; + + ///////////// // Get audio void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t count) { @@ -154,8 +176,11 @@ void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t co else if (srcBps == 2) last = buffer1; // Convert sample rate - if (sampleMult != 1) { - ChangeSampleRate(last,(short*)destination,count * channels); + if (sampleMult != 1 && source->AreSamplesNativeEndian()) { + ChangeSampleRate(last,(short*)destination,count * channels, NullSampleConverter()); + } + else if (!source->AreSamplesNativeEndian()) { + ChangeSampleRate(last,(short*)destination,count * channels, EndianSwapSampleConverter()); } delete [] buffer1; @@ -166,11 +191,22 @@ void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t co // See if we need to downmix the number of channels AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider) { AudioProvider *provider = source_provider; - if (provider->GetBytesPerSample() != 2 || provider->GetSampleRate() < 32000) - provider = new ConvertAudioProvider(provider); + // Aegisub requires 16 bit samples, + // some audio players break with low samplerates, + // everything breaks with wrong-ended samples. + if (provider->GetBytesPerSample() != 2 || + provider->GetSampleRate() < 32000 || + !provider->AreSamplesNativeEndian()) + { + provider = new ConvertAudioProvider(provider); + } + + // We also require mono audio for historical reasons if (provider->GetChannels() != 1) + { provider = new DownmixingAudioProvider(provider); + } return provider; } diff --git a/aegisub/src/audio_provider_convert.h b/aegisub/src/audio_provider_convert.h index 51d29455f..ebd11f508 100644 --- a/aegisub/src/audio_provider_convert.h +++ b/aegisub/src/audio_provider_convert.h @@ -50,12 +50,17 @@ private: AudioProvider *source; void Make16Bit(const char *src, short *dst, int64_t count); - void ChangeSampleRate(const short *src, short *dst, int64_t count); + template + void ChangeSampleRate(const short *src, short *dst, int64_t count, const SampleConverter &converter); public: ConvertAudioProvider(AudioProvider *source); ~ConvertAudioProvider(); + // By its nature, the ConvertAudioProvider always delivers machine endian: + // That's one of the points of it! + bool AreSamplesNativeEndian() { return true; } + void GetAudio(void *buf, int64_t start, int64_t count); wxString GetFilename() { return source->GetFilename(); } }; diff --git a/aegisub/src/audio_provider_downmix.cpp b/aegisub/src/audio_provider_downmix.cpp index b57057d40..9827a0fe9 100644 --- a/aegisub/src/audio_provider_downmix.cpp +++ b/aegisub/src/audio_provider_downmix.cpp @@ -55,6 +55,8 @@ DownmixingAudioProvider::DownmixingAudioProvider(AudioProvider *source) { if (!(bytes_per_sample == 1 || bytes_per_sample == 2)) throw _T("Downmixing Audio Provider: Can only downmix 8 and 16 bit audio"); + if (!source->AreSamplesNativeEndian()) + throw _T("Downmixing Audio Provider: Source must have machine endian samples"); } ///////////////// diff --git a/aegisub/src/audio_provider_downmix.h b/aegisub/src/audio_provider_downmix.h index 9b0986259..5c090723a 100644 --- a/aegisub/src/audio_provider_downmix.h +++ b/aegisub/src/audio_provider_downmix.h @@ -44,6 +44,9 @@ public: DownmixingAudioProvider(AudioProvider *source); ~DownmixingAudioProvider(); + // Downmixing requires samples to be native endian beforehand + bool AreSamplesNativeEndian() { return true; } + void GetAudio(void *buf, int64_t start, int64_t count); }; diff --git a/aegisub/src/audio_provider_dummy.h b/aegisub/src/audio_provider_dummy.h index b9c1866fb..6157b926e 100644 --- a/aegisub/src/audio_provider_dummy.h +++ b/aegisub/src/audio_provider_dummy.h @@ -52,5 +52,7 @@ public: DummyAudioProvider(unsigned long dur_ms, bool _noise); ~DummyAudioProvider(); + bool AreSamplesNativeEndian() { return true; } + void GetAudio(void *buf, int64_t start, int64_t count); }; diff --git a/aegisub/src/audio_provider_ffmpegsource.h b/aegisub/src/audio_provider_ffmpegsource.h index 6806dbb74..4d8fe59c0 100644 --- a/aegisub/src/audio_provider_ffmpegsource.h +++ b/aegisub/src/audio_provider_ffmpegsource.h @@ -60,6 +60,9 @@ public: FFmpegSourceAudioProvider(Aegisub::String filename); virtual ~FFmpegSourceAudioProvider(); + // FFMS always delivers samples in machine endian + bool AreSamplesNativeEndian() { return true; } + virtual void GetAudio(void *buf, int64_t start, int64_t count); }; diff --git a/aegisub/src/audio_provider_hd.cpp b/aegisub/src/audio_provider_hd.cpp index 27df73308..dc5925ce8 100644 --- a/aegisub/src/audio_provider_hd.cpp +++ b/aegisub/src/audio_provider_hd.cpp @@ -59,6 +59,7 @@ HDAudioProvider::HDAudioProvider(AudioProvider *source) { channels = source->GetChannels(); sample_rate = source->GetSampleRate(); filename = source->GetFilename(); + samples_native_endian = source->AreSamplesNativeEndian(); // Check free space wxLongLong freespace; diff --git a/aegisub/src/audio_provider_hd.h b/aegisub/src/audio_provider_hd.h index ab75af3a7..db34afb10 100644 --- a/aegisub/src/audio_provider_hd.h +++ b/aegisub/src/audio_provider_hd.h @@ -50,6 +50,7 @@ private: wxMutex diskmutex; wxFile file_cache; wxString diskCacheFilename; + bool samples_native_endian; static wxString DiskCachePath(); static wxString DiskCacheName(); @@ -58,5 +59,7 @@ public: HDAudioProvider(AudioProvider *source); ~HDAudioProvider(); + bool AreSamplesNativeEndian() { return samples_native_endian; } + void GetAudio(void *buf, int64_t start, int64_t count); }; diff --git a/aegisub/src/audio_provider_lavc.h b/aegisub/src/audio_provider_lavc.h index bb9099075..4ea3c924b 100644 --- a/aegisub/src/audio_provider_lavc.h +++ b/aegisub/src/audio_provider_lavc.h @@ -89,6 +89,10 @@ private: public: LAVCAudioProvider(Aegisub::String _filename); virtual ~LAVCAudioProvider(); + + // Supposedly lavc always returns machine endian samples + bool AreSamplesNativeEndian() { return true; } + virtual void GetAudio(void *buf, int64_t start, int64_t count); }; diff --git a/aegisub/src/audio_provider_pcm.cpp b/aegisub/src/audio_provider_pcm.cpp index d4161f41b..401bf77b1 100644 --- a/aegisub/src/audio_provider_pcm.cpp +++ b/aegisub/src/audio_provider_pcm.cpp @@ -365,6 +365,16 @@ public: filepos += (Endian::LittleToMachine(ch.size) + 1) & ~1; } } + + + bool AreSamplesNativeEndian() + { + // 8 bit samples don't consider endianness + if (bytes_per_sample < 2) return true; + // Otherwise test whether we're little endian + uint32_t testvalue = 0x008800ff; + return testvalue == Endian::LittleToMachine(testvalue); + } }; @@ -508,6 +518,16 @@ public: filepos += (chunk_size + 7) & ~7; } } + + + bool AreSamplesNativeEndian() + { + // 8 bit samples don't consider endianness + if (bytes_per_sample < 2) return true; + // Otherwise test whether we're little endian + uint32_t testvalue = 0x008800ff; + return testvalue == Endian::LittleToMachine(testvalue); + } }; diff --git a/aegisub/src/audio_provider_ram.cpp b/aegisub/src/audio_provider_ram.cpp index 4e8f50e90..061171523 100644 --- a/aegisub/src/audio_provider_ram.cpp +++ b/aegisub/src/audio_provider_ram.cpp @@ -57,6 +57,7 @@ RAMAudioProvider::RAMAudioProvider(AudioProvider *source) { // Init blockcache = NULL; blockcount = 0; + samples_native_endian = source->AreSamplesNativeEndian(); // Allocate cache int64_t ssize = source->GetNumSamples() * source->GetBytesPerSample(); diff --git a/aegisub/src/audio_provider_ram.h b/aegisub/src/audio_provider_ram.h index f2ac0c781..4e9ebadbb 100644 --- a/aegisub/src/audio_provider_ram.h +++ b/aegisub/src/audio_provider_ram.h @@ -48,6 +48,7 @@ class RAMAudioProvider : public AudioProvider { private: char** blockcache; int blockcount; + bool samples_native_endian; void Clear(); @@ -55,5 +56,7 @@ public: RAMAudioProvider(AudioProvider *source); ~RAMAudioProvider(); + bool AreSamplesNativeEndian() { return samples_native_endian; } + void GetAudio(void *buf, int64_t start, int64_t count); }; diff --git a/aegisub/src/audio_provider_stream.h b/aegisub/src/audio_provider_stream.h index 2ab52d49d..b8c8bcaa8 100644 --- a/aegisub/src/audio_provider_stream.h +++ b/aegisub/src/audio_provider_stream.h @@ -68,6 +68,12 @@ public: StreamAudioProvider(); ~StreamAudioProvider(); + // The AreSamplesNativeEndian() method is intentionally missing. + // This class was used with the experimental scrubbing code but never + // used outside that. + // The method is left out to make it break compilation in case it does + // get used again, so it can get a review and stuff. + void GetAudio(void *buf, int64_t start, int64_t count); void Append(void *buf, int64_t count); void SetParams(int channels,int rate,int bps); diff --git a/aegisub/src/include/aegisub/audio_provider.h b/aegisub/src/include/aegisub/audio_provider.h index 2dcf3cacd..63615e75f 100644 --- a/aegisub/src/include/aegisub/audio_provider.h +++ b/aegisub/src/include/aegisub/audio_provider.h @@ -74,6 +74,7 @@ public: int GetSampleRate(); int GetBytesPerSample(); int GetChannels(); + virtual bool AreSamplesNativeEndian() = 0; void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale); };