代码之家  ›  专栏  ›  技术社区  ›  coneybeare

使用扩展的音频文件服务将两个文件混合在一起

  •  1
  • coneybeare  · 技术社区  · 15 年前

    我正在使用音频单元进行一些自定义音频后处理。我有两个文件合并在一起(链接如下),但在输出中出现了一些奇怪的噪音。我做错什么了?

    我已经确认在这一步之前,这两个文件( 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
    • 之后 混合音频文件2ref 字节用完了 outputAudioFileRef 听起来应该和 混合音频文件2ref

    预期的声音应该是混合淡入淡出开始时,以产生一个自我交叉淡出当轨道是环。请听输出,看看代码,让我知道哪里出错了。

    源音 : http://cl.ly/2g2F2A3k1r3S36210V23
    产生的音调声音 : http://cl.ly/3q2w3S3Y0x0M3i2a1W3v

    2 回复  |  直到 15 年前
        1
  •  1
  •   coneybeare    15 年前

    原来这里有两个问题。

    缓冲区处理代码

    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

        2
  •  0
  •   Nik Reiman    15 年前

    你公布的答案看起来不错,我只看到一个小问题。你的剪辑解决方案,除以2将有帮助,但它也相当于应用50%的增益减少。那就是 与标准化相同; normalization 是指查看整个音频文件,找到最高峰值,并应用给定的增益降低,使该峰值达到某一水平(通常为0.0dB)的过程。结果是,在正常(即非限幅)情况下,输出信号将非常低,需要再次增强。

    在合成过程中,无疑会遇到导致失真的溢出,因为该值会环绕并导致信号跳变。你想做的是应用一种叫做 brick-wall limiter ,这基本上会对正在剪裁的样本应用硬天花板。最简单的方法是:

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

    这项技术的结果是,您将听到一些失真周围的样本,这是剪辑,但声音不会完全损坏,就像情况下的整数溢出。这种失真虽然存在,但并没有破坏听力体验。