Restructured audio providing a bit. If a provider cannot provide 16-bit mono audio with a sample rate higher than 32kHz, an intermediate converting provider will be inserted to fix it. Made the lavc audio provider rely on this for downmixing instead of doing it with libavcodec (used to fail on audio with >2 channels).

Originally committed to SVN as r2265.
This commit is contained in:
Karl Blomster 2008-07-16 13:22:06 +00:00
parent 2bd33541c1
commit 0badb4059f
5 changed files with 33 additions and 33 deletions

View File

@ -199,8 +199,12 @@ AudioProvider *AudioProviderFactoryManager::GetAudioProvider(wxString filename,
// Try a PCM provider first // Try a PCM provider first
provider = CreatePCMAudioProvider(filename); provider = CreatePCMAudioProvider(filename);
if (provider) { if (provider) {
if (provider->GetBytesPerSample() == 2 && provider->GetSampleRate() >= 32000) return provider; if (provider->GetBytesPerSample() == 2 && provider->GetSampleRate() >= 32000 && provider->GetChannels() == 1)
return new ConvertAudioProvider(provider); return provider;
else {
provider = CreateConvertAudioProvider(provider);
return provider;
}
} }
} }
@ -229,7 +233,8 @@ AudioProvider *AudioProviderFactoryManager::GetAudioProvider(wxString filename,
if (!provider) throw error; if (!provider) throw error;
// Give it a conversor if needed // Give it a conversor if needed
if (provider->GetBytesPerSample() != 2 || provider->GetSampleRate() < 32000) provider = new ConvertAudioProvider(provider); if (provider->GetBytesPerSample() != 2 || provider->GetSampleRate() < 32000 || provider->GetChannels() != 1)
provider = CreateConvertAudioProvider(provider);
// Change provider to RAM/HD cache if needed // Change provider to RAM/HD cache if needed
if (cache == -1) cache = Options.AsInt(_T("Audio Cache")); if (cache == -1) cache = Options.AsInt(_T("Audio Cache"));

View File

@ -37,13 +37,14 @@
/////////// ///////////
// Headers // Headers
#include "audio_provider_convert.h" #include "audio_provider_convert.h"
#include "audio_provider_downmix.h"
/////////////// ///////////////
// Constructor // Constructor
ConvertAudioProvider::ConvertAudioProvider(AudioProvider *src) { ConvertAudioProvider::ConvertAudioProvider(AudioProvider *src) {
source = src; source = src;
channels = 1; channels = source->GetChannels();
num_samples = source->GetNumSamples(); num_samples = source->GetNumSamples();
sample_rate = source->GetSampleRate(); sample_rate = source->GetSampleRate();
bytes_per_sample = 2; bytes_per_sample = 2;
@ -159,3 +160,15 @@ void ConvertAudioProvider::GetAudio(void *destination, int64_t start, int64_t co
delete [] buffer2; delete [] buffer2;
} }
} }
// 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(source_provider);
if (provider->GetChannels() != 1)
provider = new DownmixingAudioProvider(provider);
return provider;
}

View File

@ -59,3 +59,5 @@ public:
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(); }
}; };
AudioProvider *CreateConvertAudioProvider(AudioProvider *source_provider);

View File

@ -115,21 +115,17 @@ LAVCAudioProvider::LAVCAudioProvider(Aegisub::String _filename)
if (!sample_rate) if (!sample_rate)
sample_rate = codecContext->sample_rate; sample_rate = codecContext->sample_rate;
channels = 1; /* rely on the downmixing audio provider to do downmixing for us later */
channels = codecContext->channels;
/* FIXME: this entire provider always assumes 16-bit audio. Currently that isn't a problem since /* FIXME: this entire provider always assumes 16-bit audio. Currently that isn't a problem since
ffmpeg always converts everything to 16-bit, but in the future it might become one. */ ffmpeg always converts everything to 16-bit, but in the future it might become one. */
bytes_per_sample = 2; bytes_per_sample = 2;
/* aegisub currently supports mono only, so always resample unless it's mono with the desired samplerate */ /* aegisub currently supports mono only, so always resample unless it's mono with the desired samplerate */
if ((sample_rate != codecContext->sample_rate) || (codecContext->channels > 1)) { if (sample_rate != codecContext->sample_rate) {
// FIXME: ffmpeg currently doesn't support downmixing audio with more than two channels, rsct = audio_resample_init(channels, channels, sample_rate, codecContext->sample_rate);
// remove the following line when it does.
if (codecContext->channels > 2)
throw _T("ffmpeg audio provider: Downmixing audio with more than two channels is currently not supported by ffmpeg");
rsct = audio_resample_init(1, codecContext->channels, sample_rate, codecContext->sample_rate);
if (!rsct) if (!rsct)
throw _T("ffmpeg audio provider: Failed to initialize resampling"); throw _T("ffmpeg audio provider: Failed to initialize resampling");
resample_ratio = (float)sample_rate / (float)codecContext->sample_rate; resample_ratio = (float)sample_rate / (float)codecContext->sample_rate;
} }
@ -141,7 +137,7 @@ LAVCAudioProvider::LAVCAudioProvider(Aegisub::String _filename)
length = (double)lavcfile->fctx->duration / AV_TIME_BASE; length = (double)lavcfile->fctx->duration / AV_TIME_BASE;
else else
length = (double)stream->duration * av_q2d(stream->time_base); length = (double)stream->duration * av_q2d(stream->time_base);
num_samples = (int64_t)(length * sample_rate); num_samples = (int64_t)(length * sample_rate); // number of samples per channel
buffer = (int16_t *)malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); buffer = (int16_t *)malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
if (!buffer) if (!buffer)
@ -177,15 +173,15 @@ void LAVCAudioProvider::GetAudio(void *buf, int64_t start, int64_t count)
{ {
int16_t *_buf = (int16_t *)buf; int16_t *_buf = (int16_t *)buf;
int64_t samples_to_decode = num_samples - start; /* samples left to the end of the stream */ int64_t samples_to_decode = (num_samples - start) * channels; /* samples left to the end of the stream */
if (count < samples_to_decode) /* haven't reached the end yet, so just decode the requested number of samples */ if (count < samples_to_decode) /* haven't reached the end yet, so just decode the requested number of samples */
samples_to_decode = count; samples_to_decode = count * channels; /* times the number of channels */
if (samples_to_decode < 0) /* requested beyond the end of the stream */ if (samples_to_decode < 0) /* requested beyond the end of the stream */
samples_to_decode = 0; samples_to_decode = 0;
/* if we got asked for more samples than there are left in the stream, add zeros to the decoding buffer until /* if we got asked for more samples than there are left in the stream, add zeros to the decoding buffer until
we have enough to fill the request */ we have enough to fill the request */
memset(_buf + samples_to_decode, 0, (count - samples_to_decode) * 2); memset(_buf + samples_to_decode, 0, ((count * channels) - samples_to_decode) * 2);
/* do we have leftover samples from last time we were called? */ /* do we have leftover samples from last time we were called? */
if (leftover_samples > 0) { if (leftover_samples > 0) {
@ -220,7 +216,7 @@ void LAVCAudioProvider::GetAudio(void *buf, int64_t start, int64_t count)
decoded_bytes = temp_output_buffer_size; decoded_bytes = temp_output_buffer_size;
decoded_samples = decoded_bytes / 2; /* 2 bytes per sample */ decoded_samples = decoded_bytes / 2; /* 2 bytes per sample */
size -= decoded_bytes; size -= retval;
/* do we need to resample? */ /* do we need to resample? */
if (rsct) { if (rsct) {

View File

@ -37,7 +37,6 @@
#include <wx/filename.h> #include <wx/filename.h>
#include <wx/file.h> #include <wx/file.h>
#include "audio_provider_pcm.h" #include "audio_provider_pcm.h"
#include "audio_provider_downmix.h"
#include "utils.h" #include "utils.h"
#include "aegisub_endian.h" #include "aegisub_endian.h"
#include <stdint.h> #include <stdint.h>
@ -383,21 +382,6 @@ AudioProvider *CreatePCMAudioProvider(const wxString &filename)
provider = 0; provider = 0;
wxLogDebug(_T("Creating PCM WAV reader failed with message: %s\nProceeding to try other providers."), msg); wxLogDebug(_T("Creating PCM WAV reader failed with message: %s\nProceeding to try other providers."), msg);
} }
catch (...) {
provider = 0;
}
if (provider && provider->GetChannels() > 1) {
// Can't feed non-mono audio to the rest of the program.
// Create a downmixing proxy and if it fails, don't provide PCM.
try {
provider = new DownmixingAudioProvider(provider);
}
catch (...) {
delete provider;
provider = 0;
}
}
return provider; return provider;
} }