Смешивание двух файлов вместе с использованием расширенных служб аудиофайлов

StackOverflow https://stackoverflow.com/questions/4159653

Вопрос

Я делаю кальстрающую аудиопостовую обработку с использованием аудиоустройств. У меня есть два файла, которые я объединяю вместе (ссылки ниже), но придумаю какой-то странный шум в выходе. Что я делаю неправильно?

Я проверил это до этого шага, 2 файла (workTrack1 а также workTrack2) находятся в правильном состоянии и звучат хорошо. Никаких ошибок также не попадает в процесс.

Код обработки буфера:

- (BOOL)mixBuffersWithBuffer1:(const int16_t *)buffer1 buffer2:(const int16_t *)buffer2 outBuffer:(int16_t *)mixbuffer outBufferNumSamples:(int)mixbufferNumSamples {
    BOOL clipping = NO;

    for (int i = 0 ; i < mixbufferNumSamples; i++) {
        int32_t s1 = buffer1[i];
        int32_t s2 = buffer2[i];
        int32_t mixed = s1 + s2;

        if ((mixed < -32768) || (mixed > 32767)) {
            clipping = YES; // don't break here because we dont want to lose data, only to warn the user
        }

        mixbuffer[i] = (int16_t) mixed;
    }
    return clipping;
}

Код смеси:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////      PHASE 4      ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// In phase 4, open workTrack1 and workTrack2 for reading,
// mix together, and write out to outfile.

// open the outfile for writing -- this will erase the infile if they are the same, but its ok cause we are done with it
err = [self openExtAudioFileForWriting:outPath audioFileRefPtr:&outputAudioFileRef numChannels:numChannels];
if (err) { [self cleanupInBuffer1:inBuffer1 inBuffer2:inBuffer2 outBuffer:outBuffer err:err]; return NO; }

// setup vars
framesRead = 0;
totalFrames = [self totalFrames:mixAudioFile1Ref]; // the long one.
NSLog(@"Mix-down phase, %d frames (%0.2f secs)", totalFrames, totalFrames / RECORD_SAMPLES_PER_SECOND);

moreToProcess = YES;
while (moreToProcess) {

    conversionBuffer1.mBuffers[0].mDataByteSize = LOOPER_BUFFER_SIZE;
    conversionBuffer2.mBuffers[0].mDataByteSize = LOOPER_BUFFER_SIZE;

    UInt32 frameCount1 = framesInBuffer;
    UInt32 frameCount2 = framesInBuffer;

    // Read a buffer of input samples up to AND INCLUDING totalFrames
    int numFramesRemaining = totalFrames - framesRead; // Todo see if we are off by 1 here.  Might have to add 1
    if (numFramesRemaining == 0) {
        moreToProcess = NO; // If no frames are to be read, then this phase is finished

    } else {
        if (numFramesRemaining < frameCount1) { // see if we are near the end
            frameCount1 = numFramesRemaining;
            frameCount2 = numFramesRemaining;
            conversionBuffer1.mBuffers[0].mDataByteSize = (frameCount1 * bytesPerFrame);
            conversionBuffer2.mBuffers[0].mDataByteSize = (frameCount2 * bytesPerFrame);
        }

        NSbugLog(@"Attempting to read %d frames from mixAudioFile1Ref", (int)frameCount1);
        err = ExtAudioFileRead(mixAudioFile1Ref, &frameCount1, &conversionBuffer1);
        if (err) { [self cleanupInBuffer1:inBuffer1 inBuffer2:inBuffer2 outBuffer:outBuffer err:err]; return NO; }

        NSLog(@"Attempting to read %d frames from mixAudioFile2Ref", (int)frameCount2);
        err = ExtAudioFileRead(mixAudioFile2Ref, &frameCount2, &conversionBuffer2);
        if (err) { [self cleanupInBuffer1:inBuffer1 inBuffer2:inBuffer2 outBuffer:outBuffer err:err]; return NO; }

        NSLog(@"Read %d frames from mixAudioFile1Ref in mix-down phase", (int)frameCount1);
        NSLog(@"Read %d frames from mixAudioFile2Ref in mix-down phase", (int)frameCount2);

        // If no frames were returned, phase is finished
        if (frameCount1 == 0) {
            moreToProcess = NO;

        } else { // Process pcm data

            // if buffer2 was not filled, fill with zeros
            if (frameCount2 < frameCount1) {
                bzero(inBuffer2 + frameCount2, (frameCount1 - frameCount2));
                frameCount2 = frameCount1;
            }

            const int numSamples = (frameCount1 * bytesPerFrame) / sizeof(int16_t);

            if ([self mixBuffersWithBuffer1:(const int16_t *)inBuffer1
                                    buffer2:(const int16_t *)inBuffer2
                                  outBuffer:(int16_t *)outBuffer
                        outBufferNumSamples:numSamples]) {
                NSLog(@"Clipping");
            }
            // Write pcm data to the main output file
            conversionOutBuffer.mBuffers[0].mDataByteSize = (frameCount1 * bytesPerFrame);
            err = ExtAudioFileWrite(outputAudioFileRef, frameCount1, &conversionOutBuffer);

            framesRead += frameCount1;
        } // frame count
    } // else

    if (err) {
        moreToProcess = NO;
    }
} // while moreToProcess

// Check for errors
TTDASSERT(framesRead == totalFrames);
if (err) {
    if (error) *error = [NSError errorWithDomain:kUAAudioSelfCrossFaderErrorDomain
                                            code:UAAudioSelfCrossFaderErrorTypeMixDown
                                        userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:err],@"Underlying Error Code",[self commonExtAudioResultCode:err],@"Underlying Error Name",nil]];
    [self cleanupInBuffer1:inBuffer1 inBuffer2:inBuffer2 outBuffer:outBuffer err:err];
    return NO;
}
NSLog(@"Done with mix-down phase");


Допущения

  • mixAudioFile1Ref всегда дольше, чем mixAudioFile2Ref
  • После mixAudioFile2Ref Выбегает из байтов, outputAudioFileRef должен звучать точно так же, как mixAudioFile2Ref

Предполагается, что ожидаемый звук смешивает исчезновение над исчезновением в начале, чтобы создать самооценки, когда трасса зацикливается. Пожалуйста, слушайте вывод, посмотрите на код и дайте мне знать, где я пойду не так.

Source Tone Sound: http://cl.ly/2g2f2a3k1r3s36210v23.
Полученный тональный звук: http://cl.ly/3q2w3s3y0x0m3i2a1w3v.

Это было полезно?

Решение

Оказывается, здесь было две проблемы.

Код обработки буфера

int32_t mixed = s1 + s2; вызывал отсечение. Лучший способ - разделить по количеству каналов, смешанных:int32_t mixed = (s1 + s2)/2; Затем нормализовать в другом проходе позже.

Рамы! = БайтыПри развязке буферов второго трека, когда звук выбежал, я был неправильно установлен смещение и продолжительность в качестве кадров не байтов. Это произвело мусор в буфере и создал шум, который вы слышите периодически. Легко исправить:

if (frameCount2 < frameCount1) {
    bzero(inBuffer2 + (frameCount2 * bytesPerFrame), (frameCount1 - frameCount2) * bytesPerFrame);
    frameCount2 = frameCount1;
}

Теперь образец велик: http://cl.ly/1e2q1l441s2b3e2x2z0j.

Другие советы

Ваш опубликовый ответ выглядит хорошо; Я могу видеть только одну незначительную проблему. Ваше решение для отсечения, деления на двое поможет, но он также является эквивалентом применения снижения усиления на 50%. То есть нет так же, как нормализация; нормализация Процесс просматривать целый аудиофайл, нахождение самого высокого пика и применение данного снижения усиления, чтобы этот пик попал в определенный уровень (обычно 0,0 дБ). Результатом заключается в том, что при нормальном (т. Е. Невыполнение) обстоятельств выходной сигнал будет очень низким и должен быть увеличен снова.

Во время вашего смеси вы, несомненно, столкнулись с переполнением, который вызвал искажение, поскольку значение будет обернуть и вызвать прыжок в сигнал. То, что вы хотите сделать вместо этого, это применить методику под названием «Ограничитель кирпича«Что в основном применяет жесткий потолок к образцам, которые вытекают. Самый простой способ сделать это:

int32_t mixed = s1 + s2;
if(mixed >= 32767) {
  mixed = 32767;
}
else if(mixed <= -32767) {
  mixed = -32767;
}

Результатом этого метода состоит в том, что вы услышите немного искажения вокруг образцов, которые вытекают, но звук не будет полностью пронзен, так как был бы в случае с целочисленным переполнением. Искажение, хотя присутствует, не разрушает опыт прослушивания.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top