Superpowered - экспорт в файл с проблемой Mixer и Decoder (разные sampleRates и samplesPerFrame)
Я пытаюсь смешать голос пользователя с музыкой и сохранить его в файл.
Я создал 2 декодера - 1 для голоса и 1 для музыки и поместил их на вход микшера. Я декодирую каждый кадр и сохраняю его в файл, используя FILE/createWAV/fwrite.
Все отлично работает, когда моя песня имеет формат.wav и имеет тот же sampleRate и samplesPerFrame, что и записанный голос (48000/1024).
Однако, когда я хочу использовать файл.mp3 с другими параметрами (44100/1152), конечный файл неверен - он растянут или имеет некоторые потрескивающие звуки. Я думаю, это потому, что мы получаем разные sampledDecoded для каждого декодера и когда они помещаются в микшер или сохраняются в файл - разница между этими семплами отсутствует.
Насколько я обеспокоен, когда мы делаем voiceDecoder->decode(buffer, &samplesDecoded)
это движется samplePosition
от samplesDecoded
,
То, что я пытался сделать, это использовать минимальное значение от обоих декодеров. Однако в соответствии с приведенным выше предложением на каждой итерации песни будет потеряно (1152 - 1024 = 128) 128 сэмплов, поэтому я также попытался найти songDecoder таким же, как voiceDecoder: songDecoder->seek(voiceDecoder->samplePosition, true)
но это привело к совершенно неверному файлу.
Подводя итог: Как я должен обрабатывать микшер / офлайн-обработку с 2-мя декодерами, когда каждый из них имеет разные sampleRate и samplesPerFrame?
Код:
void AudioProcessor::startProcessing() {
SuperpoweredStereoMixer *mixer = new SuperpoweredStereoMixer();
float *mixerInputs_[] = {0,0,0,0};
float *mixerOutputs_[] = {0,0};
float inputLevels_[]= {0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
float outputLevels_[] = { 1.0f, 1.0f };
SuperpoweredDecoder *voiceDecoder = new SuperpoweredDecoder();
SuperpoweredDecoder *songDecoder = new SuperpoweredDecoder();
if (voiceDecoder->open(voiceInputPath, false) || songDecoder->open(songInputPath, false, songOffset, songLength)) {
delete voiceDecoder;
delete songDecoder;
delete mixer;
callJavaVoidMethodWithBoolParam(jvm, jObject, processingFinishedMethodId, false);
return;
};
FILE *fd = createWAV(outputPath, songDecoder->samplerate, 2);
if (!fd) {
delete voiceDecoder;
delete songDecoder;
delete mixer;
callJavaVoidMethodWithBoolParam(jvm, jObject, processingFinishedMethodId, false);
return;
};
// Create a buffer for the 16-bit integer samples coming from the decoder.
short int *voiceIntBuffer = (short int *)malloc(voiceDecoder->samplesPerFrame * 4 * sizeof(short int) + 32768);
short int *songIntBuffer = (short int *)malloc(songDecoder->samplesPerFrame * 4 * sizeof(short int) + 32768);
short int *outputIntBuffer = (short int *)malloc(voiceDecoder->samplesPerFrame * 4 * sizeof(short int) + 32768);
// Create a buffer for the 32-bit floating point samples required by the effect.
float *voiceFloatBuffer = (float *)malloc(voiceDecoder->samplesPerFrame * 4 * sizeof(float) + 32768);
float *songFloatBuffer = (float *)malloc(songDecoder->samplesPerFrame * 4 * sizeof(float) + 32768);
float *outputFloatBuffer = (float *)malloc(voiceDecoder->samplesPerFrame * 4 * sizeof(float) + 32768);
bool isError = false;
// Processing.
while (true) {
if (isCanceled) {
isError = true;
break;
}
// Decode one frame. samplesDecoded will be overwritten with the actual decoded number of samples.
unsigned int voiceSamplesDecoded = voiceDecoder->samplesPerFrame;
if (voiceDecoder->decode(voiceIntBuffer, &voiceSamplesDecoded) == SUPERPOWEREDDECODER_ERROR) {
break;
}
if (voiceSamplesDecoded < 1) {
break;
}
//
// Decode one frame. samplesDecoded will be overwritten with the actual decoded number of samples.
unsigned int songSamplesDecoded = songDecoder->samplesPerFrame;
if (songDecoder->decode(songIntBuffer, &songSamplesDecoded) == SUPERPOWEREDDECODER_ERROR) {
break;
}
if (songSamplesDecoded < 1) {
break;
}
unsigned int samplesDecoded = static_cast<unsigned int>(fmin(voiceSamplesDecoded, songSamplesDecoded));
// Convert the decoded PCM samples from 16-bit integer to 32-bit floating point.
SuperpoweredShortIntToFloat(voiceIntBuffer, voiceFloatBuffer, samplesDecoded);
SuperpoweredShortIntToFloat(songIntBuffer, songFloatBuffer, samplesDecoded);
//setup mixer inputs
mixerInputs_[0] = voiceFloatBuffer;
mixerInputs_[1] = songFloatBuffer;
mixerInputs_[2] = NULL;
mixerInputs_[3] = NULL;
// setup mixer outputs, might have two separate outputs (L/R) if second not null
mixerOutputs_[0] = outputFloatBuffer;
mixerOutputs_[1] = NULL;
mixer->process(mixerInputs_, mixerOutputs_, inputLevels_, outputLevels_, NULL, NULL, samplesDecoded);
// Convert the PCM samples from 32-bit floating point to 16-bit integer.
SuperpoweredFloatToShortInt(outputFloatBuffer, outputIntBuffer, samplesDecoded);
// Write the audio to disk.
fwrite(outputIntBuffer, 1, samplesDecoded * 4, fd);
// songDecoder->seek(voiceDecoder->samplePosition, true);
}
// Cleanup.
closeWAV(fd);
delete voiceDecoder;
delete songDecoder;
delete mixer;
free(voiceIntBuffer);
free(voiceFloatBuffer);
free(songIntBuffer);
free(songFloatBuffer);
free(outputFloatBuffer);
free(outputIntBuffer);
}
Заранее спасибо!
1 ответ
Вам необходимо сопоставить частоты дискретизации, используя класс SuperpoweredResampler. Вам также понадобится некоторый циклический буфер для обоих входов, потому что доступное количество выборок не будет совпадать во многих случаях.
Хорошо, так что мне удалось заставить его работать. Я сделал то, что предложил @Gabor, но он не работал полностью. Чего мне не хватало, так это каналов - мне приходилось включать его в операции буфера / смены, и теперь все в порядке!