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.
This commit is contained in:
parent
88a5335004
commit
ce015fc820
15 changed files with 104 additions and 11 deletions
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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<count;i++) {
|
||||
dst[i] = (src[i]-128)*255;
|
||||
dst[i] = (short(src[i])-128)*255;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +79,9 @@ void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count)
|
|||
//////////////////////
|
||||
// Change sample rate
|
||||
// This requres 16-bit input
|
||||
void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_t count) {
|
||||
// The SampleConverter is a class overloading operator() with a function from short to short
|
||||
template<class SampleConverter>
|
||||
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<size;i++) {
|
||||
cur = next;
|
||||
next = *src++;
|
||||
next = converter(*src++);
|
||||
*(dst++) = cur;
|
||||
*(dst++) = (cur+next)/2;
|
||||
}
|
||||
|
@ -100,7 +103,7 @@ void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_
|
|||
short next = 0;
|
||||
for (int64_t i=0;i<size;i++) {
|
||||
cur = next;
|
||||
next = *(src++);
|
||||
next = converter(*src++);
|
||||
*(dst++) = cur;
|
||||
*(dst++) = (cur*3+next)/4;
|
||||
*(dst++) = (cur+next)/2;
|
||||
|
@ -109,11 +112,30 @@ void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_
|
|||
for (int i=0;i<count%4;i++) *(dst++) = next;
|
||||
}
|
||||
|
||||
// Nothing to do (shouldn't really get here, but...)
|
||||
else if (sampleMult == 1) memcpy((void*)src,dst,count);
|
||||
// Nothing much to do, just ensure correct endedness
|
||||
else if (sampleMult == 1) {
|
||||
while (count-- > 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;
|
||||
}
|
||||
|
|
|
@ -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<class SampleConverter>
|
||||
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(); }
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
/////////////////
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue