mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 15:43:24 +02:00
Support MP3 files as sound assets
This commit is contained in:
parent
22ca20e33a
commit
bc4d900f7d
2 changed files with 111 additions and 0 deletions
|
@ -31,6 +31,8 @@
|
|||
#include "AudioLogging.h"
|
||||
#include "AudioSRC.h"
|
||||
|
||||
#include "flump3dec.h"
|
||||
|
||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) {
|
||||
return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
@ -90,7 +92,9 @@ void SoundProcessor::run() {
|
|||
QString fileName = _url.fileName().toLower();
|
||||
|
||||
static const QString WAV_EXTENSION = ".wav";
|
||||
static const QString MP3_EXTENSION = ".mp3";
|
||||
static const QString RAW_EXTENSION = ".raw";
|
||||
|
||||
if (fileName.endsWith(WAV_EXTENSION)) {
|
||||
|
||||
QByteArray outputAudioByteArray;
|
||||
|
@ -103,6 +107,20 @@ void SoundProcessor::run() {
|
|||
}
|
||||
|
||||
downSample(outputAudioByteArray, sampleRate);
|
||||
|
||||
} else if (fileName.endsWith(MP3_EXTENSION)) {
|
||||
|
||||
QByteArray outputAudioByteArray;
|
||||
|
||||
int sampleRate = interpretAsMP3(rawAudioByteArray, outputAudioByteArray);
|
||||
if (sampleRate == 0) {
|
||||
qCDebug(audio) << "Unsupported MP3 file type";
|
||||
emit onError(300, "Failed to load sound file, reason: unsupported MP3 file type");
|
||||
return;
|
||||
}
|
||||
|
||||
downSample(outputAudioByteArray, sampleRate);
|
||||
|
||||
} else if (fileName.endsWith(RAW_EXTENSION)) {
|
||||
// check if this was a stereo raw file
|
||||
// since it's raw the only way for us to know that is if the file was called .stereo.raw
|
||||
|
@ -113,6 +131,7 @@ void SoundProcessor::run() {
|
|||
|
||||
// Process as 48khz RAW file
|
||||
downSample(rawAudioByteArray, 48000);
|
||||
|
||||
} else {
|
||||
qCDebug(audio) << "Unknown sound file type";
|
||||
emit onError(300, "Failed to load sound file, reason: unknown sound file type");
|
||||
|
@ -286,3 +305,94 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
|
|||
_duration = (float)(outputAudioByteArraySize / (wave.sampleRate * wave.numChannels * wave.bitsPerSample / 8.0f));
|
||||
return wave.sampleRate;
|
||||
}
|
||||
|
||||
// returns MP3 sample rate, used for resampling
|
||||
int SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) {
|
||||
using namespace flump3dec;
|
||||
|
||||
static const int MP3_SAMPLES_MAX = 1152;
|
||||
static const int MP3_CHANNELS_MAX = 2;
|
||||
static const int MP3_BUFFER_SIZE = MP3_SAMPLES_MAX * MP3_CHANNELS_MAX * sizeof(int16_t);
|
||||
uint8_t mp3Buffer[MP3_BUFFER_SIZE];
|
||||
|
||||
// create bitstream
|
||||
Bit_stream_struc *bitstream = bs_new();
|
||||
if (bitstream == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// create decoder
|
||||
mp3tl *decoder = mp3tl_new(bitstream, MP3TL_MODE_16BIT);
|
||||
if (decoder == nullptr) {
|
||||
bs_free(bitstream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// initialize
|
||||
bs_set_data(bitstream, (uint8_t*)inputAudioByteArray.data(), inputAudioByteArray.size());
|
||||
int frameCount = 0;
|
||||
int sampleRate = 0;
|
||||
int numChannels = 0;
|
||||
|
||||
// skip ID3 tag, if present
|
||||
Mp3TlRetcode result = mp3tl_skip_id3(decoder);
|
||||
|
||||
while (!(result == MP3TL_ERR_NO_SYNC || result == MP3TL_ERR_NEED_DATA)) {
|
||||
|
||||
mp3tl_sync(decoder);
|
||||
|
||||
// find MP3 header
|
||||
const fr_header *header = nullptr;
|
||||
result = mp3tl_decode_header(decoder, &header);
|
||||
|
||||
if (result == MP3TL_ERR_OK) {
|
||||
|
||||
if (frameCount++ == 0) {
|
||||
|
||||
qCDebug(audio) << "Decoding MP3 with bitrate =" << header->bitrate
|
||||
<< "sample rate =" << header->sample_rate
|
||||
<< "channels =" << header->channels;
|
||||
|
||||
// save header info
|
||||
sampleRate = header->sample_rate;
|
||||
numChannels = header->channels;
|
||||
|
||||
// skip Xing header, if present
|
||||
result = mp3tl_skip_xing(decoder, header);
|
||||
}
|
||||
|
||||
// decode MP3 frame
|
||||
if (result == MP3TL_ERR_OK) {
|
||||
|
||||
result = mp3tl_decode_frame(decoder, mp3Buffer, MP3_BUFFER_SIZE);
|
||||
|
||||
// fill bad frames with silence
|
||||
int len = header->frame_samples * header->channels * sizeof(int16_t);
|
||||
if (result == MP3TL_ERR_BAD_FRAME) {
|
||||
memset(mp3Buffer, 0, len);
|
||||
}
|
||||
|
||||
if (result == MP3TL_ERR_OK || result == MP3TL_ERR_BAD_FRAME) {
|
||||
outputAudioByteArray.append((char*)mp3Buffer, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free decoder
|
||||
mp3tl_free(decoder);
|
||||
|
||||
// free bitstream
|
||||
bs_free(bitstream);
|
||||
|
||||
int outputAudioByteArraySize = outputAudioByteArray.size();
|
||||
if (outputAudioByteArraySize == 0) {
|
||||
qCDebug(audio) << "Error decoding MP3 file";
|
||||
return 0;
|
||||
}
|
||||
|
||||
_isStereo = (numChannels == 2);
|
||||
_isAmbisonic = false;
|
||||
_duration = (float)outputAudioByteArraySize / (sampleRate * numChannels * sizeof(int16_t));
|
||||
return sampleRate;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
|
||||
void downSample(const QByteArray& rawAudioByteArray, int sampleRate);
|
||||
int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
|
||||
int interpretAsMP3(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
|
||||
|
||||
signals:
|
||||
void onSuccess(QByteArray data, bool stereo, bool ambisonic, float duration);
|
||||
|
|
Loading…
Reference in a new issue