【问题标题】:Superpowered: can't get TimeStretching to work correctly, the output sound is distortedSuperpowered:无法让TimeStretching正常工作,输出声音失真
【发布时间】:2019-03-24 16:17:44
【问题描述】:

我正在尝试使用Superpowered SDK 对正在播放和同时录制的 mp3 文件应用实时时间拉伸和音高转换。问题是,无论我做什么,输出的声音质量都很糟糕(到了失真的地步)。
我怀疑这是由于每个帧号的样本冲突。这是我的cpp文件的完整源代码:

static SuperpoweredAndroidAudioIO *audioIO;
static SuperpoweredTimeStretching *stretching;
static SuperpoweredAudiopointerList *outputBuffers;
static SuperpoweredDecoder *decoder;
static SuperpoweredRecorder *recorder;
const char *outFilePath;
const char *tempFilePath;

static short int *intBuffer;
static float *playerBuffer;

bool audioInitialized = false;
bool playing = false;

static bool audioProcessing(
        void *__unused clientData, // custom pointer
        short int *audio,           // buffer of interleaved samples
        int numberOfFrames,         // number of frames to process
        int __unused sampleRate     // sampling rate
) {

    if (playing) {
        unsigned int samplesDecoded = decoder->samplesPerFrame;
        if (decoder->decode(intBuffer, &samplesDecoded) == SUPERPOWEREDDECODER_ERROR) return false;
        if (samplesDecoded < 1) {
            playing = false;
            return false;
        }



        SuperpoweredAudiobufferlistElement inputBuffer;
        inputBuffer.samplePosition = decoder->samplePosition;
        inputBuffer.startSample = 0;
        inputBuffer.samplesUsed = 0;
        inputBuffer.endSample = samplesDecoded;
        inputBuffer.buffers[0] = SuperpoweredAudiobufferPool::getBuffer(samplesDecoded * 8 + 64);
        inputBuffer.buffers[1] = inputBuffer.buffers[2] = inputBuffer.buffers[3] = NULL;


        SuperpoweredShortIntToFloat(intBuffer, (float *) inputBuffer.buffers[0], samplesDecoded);

        stretching->process(&inputBuffer, outputBuffers);

        if (outputBuffers->makeSlice(0, outputBuffers->sampleLength)) {

            while (true) { 
                int numSamples = 0;
                float *timeStretchedAudio = (float *) outputBuffers->nextSliceItem(&numSamples);
                if (!timeStretchedAudio) break;

                SuperpoweredFloatToShortInt(timeStretchedAudio, intBuffer,
                                            (unsigned int) numSamples);
                SuperpoweredShortIntToFloat(intBuffer, playerBuffer, (unsigned int) numSamples);

                recorder->process(playerBuffer, (unsigned int) numSamples);
                SuperpoweredFloatToShortInt(playerBuffer, audio, (unsigned int) numSamples);

            };
            outputBuffers->clear();
            return true;
        };
    }
    return false;
}


extern "C" JNIEXPORT void
Java_com_example_activities_DubsmashActivity_InitAudio(
        JNIEnv  __unused *env,
        jobject  __unused obj,
        jint bufferSize,
        jint sampleRate,
        jstring outputPath,
        jstring tempPath
) {

    decoder = new SuperpoweredDecoder();

    outputBuffers = new SuperpoweredAudiopointerList(8, 16);

    outFilePath = env->GetStringUTFChars(outputPath, 0);
    tempFilePath = env->GetStringUTFChars(tempPath, 0);

}

extern "C" JNIEXPORT jdouble
Java_com_example_activities_DubsmashActivity_OpenFile(
        JNIEnv *env,
        jobject  __unused obj,
        jstring filePath) {
    const char *path = env->GetStringUTFChars(filePath, 0);
    decoder->open(path);
    intBuffer = (short int *) malloc(decoder->samplesPerFrame * 2 * sizeof(short int) + 32768);
    playerBuffer = (float *) malloc(decoder->samplesPerFrame * 2 * sizeof(short int) + 32768);
    audioIO = new SuperpoweredAndroidAudioIO(
            decoder->samplerate,
            decoder->samplesPerFrame,
            false,
            true,
            audioProcessing,
            NULL,
            -1, -1,
            decoder->samplesPerFrame * 2
    );

    stretching = new SuperpoweredTimeStretching(decoder->samplerate);

    stretching->setRateAndPitchShift(1, 0);

    recorder = new SuperpoweredRecorder(
            tempFilePath,              
            decoder->samplerate,  
            1,                  
            2,                  
            false,             
            recorderStopped,    
            NULL               
    );

    return 0;
}

需要考虑的一些注意事项:

  1. 这不是 this question 的副本,因为该线程中的解决方案对我不起作用
  2. 我尝试过使用decoder-&gt;samplesPerFrame 和numSamples,但我无法获得不错的输出。
  3. 如果我将 Time Stretching 设置为 1 并将 Pitch Shift 设置为 0 ,声音会无缝播放。

更新 1:
在对样本数量进行了更多篡改和使用不同值之后,我认为问题一定出在音频输出 (DAC MAN) 期望的样本数量与 outputBuffers-&gt;nextSliceItem 实际提供的数量之间的差异。
话虽如此,我可以想出一种方法来缓解这个问题,那就是将outputBuffers-&gt;nextSliceItem 的输出附加到一个临时缓冲区,然后当它达到阈值时,将其定向到音频输出。

因此我的第二个问题是:C++ 中有没有办法将缓冲区附加到另一个缓冲区?

【问题讨论】:

  • 那是很多的代码。你有没有机会把它减少到 minimal reproducible example / SSCCE ?
  • @JesperJuhl,好的,刚刚从示例中删除了所有冗余代码。它现在应该更具可读性。
  • 为什么在 C++ 程序中使用malloc?除了明显的“手动内存管理是代码异味”注释之外,它(可能)从与new 不同的堆分配,如果您比较指针或类似的东西,您只是为将来的问题做好准备。但是,已经使用智能指针了......所有这些原始指针真的很痛苦;永远不会通过(我的)代码审查。
  • @JesperJuhl,那是因为我不是 C++ 程序员,老实说我不知道​​你在说什么。我只是按照SDK作者发布的示例进行的。
  • @JesperJuhl "从不同的堆分配" ????只有一个堆......智能指针肯定不会通过我的代码审查,因为实时音频需要精确控制内存分配(因为阻塞),而老式分配提供了查看究竟发生了什么的最佳方法。

标签: android c++ android-ndk signal-processing superpowered


【解决方案1】:

您需要输出 audioProcessing(int numberOfFrames) 帧数。因此,在 outputBuffers->makeSlice 中,您需要询问 numberOfFrames,而不是 outputBuffers->sampleLength(基本上您是在询问“outputBuffers 中的任意数量的帧”,而不是“numberOfFrames”)。

然后你从float转换为int,然后再转换回float?这没有意义。您在 timeStretchedAudio 中获得了浮点音频,可以立即由您的录音机处理。

在那之后,您将一些浮点样本转换为“音频”后忘记向前步进。

最后,您从 outputBuffers 中删除所有音频,而您只需要删除输出到“音频”的帧数。

【讨论】:

  • 谢谢。但是您所说的“音频向前迈进”是什么意思?我在你的任何样本中都没有看到这个词。关于清除缓冲区的另一件事。我应该使用outputBuffers-&gt;truncate(numberOfFrames, true);吗?
  • 您有一个名为“音频”的缓冲区。在你使用/复制了一些样本之后,你需要增加它的值,因为你在迭代中使用它,如果你不增加它,你将再次使用/复制相同的数据。
  • 能否用一点代码说明你的意思?
  • 音频 += numSamples * 2; // (2 因为立体声)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-25
  • 1970-01-01
  • 1970-01-01
  • 2014-09-18
  • 2020-10-21
  • 2012-09-05
  • 2017-11-06
相关资源
最近更新 更多