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();
|
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 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);
|
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_convert.h"
|
||||||
#include "audio_provider_downmix.h"
|
#include "audio_provider_downmix.h"
|
||||||
|
#include "aegisub_endian.h"
|
||||||
|
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
|
@ -70,7 +71,7 @@ ConvertAudioProvider::~ConvertAudioProvider() {
|
||||||
// Convert to 16-bit
|
// Convert to 16-bit
|
||||||
void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count) {
|
void ConvertAudioProvider::Make16Bit(const char *src, short *dst, int64_t count) {
|
||||||
for (int64_t i=0;i<count;i++) {
|
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
|
// Change sample rate
|
||||||
// This requres 16-bit input
|
// 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
|
// Upsample by 2
|
||||||
if (sampleMult == 2) {
|
if (sampleMult == 2) {
|
||||||
int64_t size = count/2;
|
int64_t size = count/2;
|
||||||
|
@ -86,7 +89,7 @@ void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_
|
||||||
short next = 0;
|
short next = 0;
|
||||||
for (int64_t i=0;i<size;i++) {
|
for (int64_t i=0;i<size;i++) {
|
||||||
cur = next;
|
cur = next;
|
||||||
next = *src++;
|
next = converter(*src++);
|
||||||
*(dst++) = cur;
|
*(dst++) = cur;
|
||||||
*(dst++) = (cur+next)/2;
|
*(dst++) = (cur+next)/2;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +103,7 @@ void ConvertAudioProvider::ChangeSampleRate(const short *src, short *dst, int64_
|
||||||
short next = 0;
|
short next = 0;
|
||||||
for (int64_t i=0;i<size;i++) {
|
for (int64_t i=0;i<size;i++) {
|
||||||
cur = next;
|
cur = next;
|
||||||
next = *(src++);
|
next = converter(*src++);
|
||||||
*(dst++) = cur;
|
*(dst++) = cur;
|
||||||
*(dst++) = (cur*3+next)/4;
|
*(dst++) = (cur*3+next)/4;
|
||||||
*(dst++) = (cur+next)/2;
|
*(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;
|
for (int i=0;i<count%4;i++) *(dst++) = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to do (shouldn't really get here, but...)
|
// Nothing much to do, just ensure correct endedness
|
||||||
else if (sampleMult == 1) memcpy((void*)src,dst,count);
|
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
|
// Get audio
|
||||||
void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t count) {
|
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;
|
else if (srcBps == 2) last = buffer1;
|
||||||
|
|
||||||
// Convert sample rate
|
// Convert sample rate
|
||||||
if (sampleMult != 1) {
|
if (sampleMult != 1 && source->AreSamplesNativeEndian()) {
|
||||||
ChangeSampleRate(last,(short*)destination,count * channels);
|
ChangeSampleRate(last,(short*)destination,count * channels, NullSampleConverter());
|
||||||
|
}
|
||||||
|
else if (!source->AreSamplesNativeEndian()) {
|
||||||
|
ChangeSampleRate(last,(short*)destination,count * channels, EndianSwapSampleConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
delete [] buffer1;
|
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
|
// See if we need to downmix the number of channels
|
||||||
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider) {
|
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider) {
|
||||||
AudioProvider *provider = 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)
|
if (provider->GetChannels() != 1)
|
||||||
|
{
|
||||||
provider = new DownmixingAudioProvider(provider);
|
provider = new DownmixingAudioProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,17 @@ private:
|
||||||
|
|
||||||
AudioProvider *source;
|
AudioProvider *source;
|
||||||
void Make16Bit(const char *src, short *dst, int64_t count);
|
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:
|
public:
|
||||||
ConvertAudioProvider(AudioProvider *source);
|
ConvertAudioProvider(AudioProvider *source);
|
||||||
~ConvertAudioProvider();
|
~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);
|
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
wxString GetFilename() { return source->GetFilename(); }
|
wxString GetFilename() { return source->GetFilename(); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,6 +55,8 @@ DownmixingAudioProvider::DownmixingAudioProvider(AudioProvider *source) {
|
||||||
|
|
||||||
if (!(bytes_per_sample == 1 || bytes_per_sample == 2))
|
if (!(bytes_per_sample == 1 || bytes_per_sample == 2))
|
||||||
throw _T("Downmixing Audio Provider: Can only downmix 8 and 16 bit audio");
|
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(AudioProvider *source);
|
||||||
~DownmixingAudioProvider();
|
~DownmixingAudioProvider();
|
||||||
|
|
||||||
|
// Downmixing requires samples to be native endian beforehand
|
||||||
|
bool AreSamplesNativeEndian() { return true; }
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,5 +52,7 @@ public:
|
||||||
DummyAudioProvider(unsigned long dur_ms, bool _noise);
|
DummyAudioProvider(unsigned long dur_ms, bool _noise);
|
||||||
~DummyAudioProvider();
|
~DummyAudioProvider();
|
||||||
|
|
||||||
|
bool AreSamplesNativeEndian() { return true; }
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,6 +60,9 @@ public:
|
||||||
FFmpegSourceAudioProvider(Aegisub::String filename);
|
FFmpegSourceAudioProvider(Aegisub::String filename);
|
||||||
virtual ~FFmpegSourceAudioProvider();
|
virtual ~FFmpegSourceAudioProvider();
|
||||||
|
|
||||||
|
// FFMS always delivers samples in machine endian
|
||||||
|
bool AreSamplesNativeEndian() { return true; }
|
||||||
|
|
||||||
virtual void GetAudio(void *buf, int64_t start, int64_t count);
|
virtual void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -59,6 +59,7 @@ HDAudioProvider::HDAudioProvider(AudioProvider *source) {
|
||||||
channels = source->GetChannels();
|
channels = source->GetChannels();
|
||||||
sample_rate = source->GetSampleRate();
|
sample_rate = source->GetSampleRate();
|
||||||
filename = source->GetFilename();
|
filename = source->GetFilename();
|
||||||
|
samples_native_endian = source->AreSamplesNativeEndian();
|
||||||
|
|
||||||
// Check free space
|
// Check free space
|
||||||
wxLongLong freespace;
|
wxLongLong freespace;
|
||||||
|
|
|
@ -50,6 +50,7 @@ private:
|
||||||
wxMutex diskmutex;
|
wxMutex diskmutex;
|
||||||
wxFile file_cache;
|
wxFile file_cache;
|
||||||
wxString diskCacheFilename;
|
wxString diskCacheFilename;
|
||||||
|
bool samples_native_endian;
|
||||||
|
|
||||||
static wxString DiskCachePath();
|
static wxString DiskCachePath();
|
||||||
static wxString DiskCacheName();
|
static wxString DiskCacheName();
|
||||||
|
@ -58,5 +59,7 @@ public:
|
||||||
HDAudioProvider(AudioProvider *source);
|
HDAudioProvider(AudioProvider *source);
|
||||||
~HDAudioProvider();
|
~HDAudioProvider();
|
||||||
|
|
||||||
|
bool AreSamplesNativeEndian() { return samples_native_endian; }
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,6 +89,10 @@ private:
|
||||||
public:
|
public:
|
||||||
LAVCAudioProvider(Aegisub::String _filename);
|
LAVCAudioProvider(Aegisub::String _filename);
|
||||||
virtual ~LAVCAudioProvider();
|
virtual ~LAVCAudioProvider();
|
||||||
|
|
||||||
|
// Supposedly lavc always returns machine endian samples
|
||||||
|
bool AreSamplesNativeEndian() { return true; }
|
||||||
|
|
||||||
virtual void GetAudio(void *buf, int64_t start, int64_t count);
|
virtual void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -365,6 +365,16 @@ public:
|
||||||
filepos += (Endian::LittleToMachine(ch.size) + 1) & ~1;
|
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;
|
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
|
// Init
|
||||||
blockcache = NULL;
|
blockcache = NULL;
|
||||||
blockcount = 0;
|
blockcount = 0;
|
||||||
|
samples_native_endian = source->AreSamplesNativeEndian();
|
||||||
|
|
||||||
// Allocate cache
|
// Allocate cache
|
||||||
int64_t ssize = source->GetNumSamples() * source->GetBytesPerSample();
|
int64_t ssize = source->GetNumSamples() * source->GetBytesPerSample();
|
||||||
|
|
|
@ -48,6 +48,7 @@ class RAMAudioProvider : public AudioProvider {
|
||||||
private:
|
private:
|
||||||
char** blockcache;
|
char** blockcache;
|
||||||
int blockcount;
|
int blockcount;
|
||||||
|
bool samples_native_endian;
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
@ -55,5 +56,7 @@ public:
|
||||||
RAMAudioProvider(AudioProvider *source);
|
RAMAudioProvider(AudioProvider *source);
|
||||||
~RAMAudioProvider();
|
~RAMAudioProvider();
|
||||||
|
|
||||||
|
bool AreSamplesNativeEndian() { return samples_native_endian; }
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count);
|
void GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
};
|
};
|
||||||
|
|
|
@ -68,6 +68,12 @@ public:
|
||||||
StreamAudioProvider();
|
StreamAudioProvider();
|
||||||
~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 GetAudio(void *buf, int64_t start, int64_t count);
|
||||||
void Append(void *buf, int64_t count);
|
void Append(void *buf, int64_t count);
|
||||||
void SetParams(int channels,int rate,int bps);
|
void SetParams(int channels,int rate,int bps);
|
||||||
|
|
|
@ -74,6 +74,7 @@ public:
|
||||||
int GetSampleRate();
|
int GetSampleRate();
|
||||||
int GetBytesPerSample();
|
int GetBytesPerSample();
|
||||||
int GetChannels();
|
int GetChannels();
|
||||||
|
virtual bool AreSamplesNativeEndian() = 0;
|
||||||
|
|
||||||
void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale);
|
void GetWaveForm(int *min,int *peak,int64_t start,int w,int h,int samples,float scale);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue