2015-03-22 1 views
1

У меня есть буфер с образцами размером 8 кГц, и я пытаюсь просто применить фильтр нижних частот к буфере. Смысл, я начинаю с буфера 8 кГц образцов, и я хочу в итоге получить буфер из 8khz LOWPASSED образцов. Если я подключу низкочастотный блок и подключу его к блоку вывода по умолчанию и поставлю свой буфер, он будет звучать идеально и неправильно пройденный. Однако, как только я удаляю вывод и вызываю AudioUnitRender на низкочастотном аудиоустройстве напрямую, результирующие сэмплы сглаживаются и обрезаются.автономный рендеринг с фильтром нижних частот вызывает сглаживание и обрезку

#import "EffectMachine.h" 
#import <AudioToolbox/AudioToolbox.h> 
#import "AudioHelpers.h" 
#import "Buffer.h" 

@interface EffectMachine() 
@property (nonatomic, strong) Buffer *buffer; 
@end 

typedef struct EffectPlayer { 
    NSUInteger index; 
    AudioUnit lowPassUnit; 
    __unsafe_unretained Buffer *buffer; 
} EffectPlayer; 

OSStatus EffectMachineCallbackRenderProc(void *inRefCon, 
             AudioUnitRenderActionFlags *ioActionFlags, 
             const AudioTimeStamp *inTimeStamp, 
             UInt32 inBusNumber, 
             UInt32 inNumberFrames, 
             AudioBufferList * ioData); 

OSStatus EffectMachineCallbackRenderProc(void *inRefCon, 
             AudioUnitRenderActionFlags *ioActionFlags, 
             const AudioTimeStamp *inTimeStamp, 
             UInt32 inBusNumber, 
             UInt32 inNumberFrames, 
             AudioBufferList * ioData) { 
    struct EffectPlayer *player = (struct EffectPlayer *)inRefCon; 

    for (int i = 0; i < inNumberFrames; i++) { 
     float sample; 
     if (player->index < player->buffer.size) { 
      sample = (float)player->buffer.samples[player->index]; 
      player->index += 1; 
     } else { 
      sample = 0; 
     } 
     ((float *)ioData->mBuffers[0].mData)[i] = sample; 
     ((float *)ioData->mBuffers[1].mData)[i] = sample; 
    } 

    return noErr; 
} 

@implementation EffectMachine { 
    EffectPlayer player; 
} 

-(instancetype)initWithBuffer:(Buffer *)buffer { 
    if (self = [super init]) { 
     self.buffer = buffer; 
    } 
    return self; 
} 

-(Buffer *)process { 
    struct EffectPlayer initialized = {0}; 
    player  = initialized; 
    player.buffer = self.buffer; 

    [self setupAudioUnits]; 
    Buffer *buffer = [self processedBuffer]; 
    [self cleanup]; 

    return buffer; 
} 

-(void)setupAudioUnits { 
    AudioComponentDescription lowpasscd = {0}; 
    lowpasscd.componentType = kAudioUnitType_Effect; 
    lowpasscd.componentSubType = kAudioUnitSubType_LowPassFilter; 
    lowpasscd.componentManufacturer = kAudioUnitManufacturer_Apple; 

    AudioComponent comp = AudioComponentFindNext(NULL, &lowpasscd); 
    if (comp == NULL) NSLog(@"can't get lowpass unit"); 

    AudioComponentInstanceNew(comp, &player.lowPassUnit); 

    AURenderCallbackStruct input; 
    input.inputProc = EffectMachineCallbackRenderProc; 
    input.inputProcRefCon = &player; 

    CheckError(AudioUnitSetProperty(player.lowPassUnit, 
            kAudioUnitProperty_SetRenderCallback, 
            kAudioUnitScope_Input, 
            0, 
            &input, 
            sizeof(input)), 
       "AudioUnitSetProperty for callback failed"); 

    CheckError(AudioUnitSetParameter(player.lowPassUnit, 
            kLowPassParam_CutoffFrequency, 
            kAudioUnitScope_Global, 
            0, 
            1500, 
            0), "AudioUnitSetParameter cutoff for lowpass failed"); 

    CheckError(AudioUnitSetParameter(player.lowPassUnit, 
            kLowPassParam_Resonance, 
            kAudioUnitScope_Global, 
            0, 
            0, 
            0), "AudioUnitSetParameter resonance for lowpass failed"); 

    CheckError(AudioUnitInitialize(player.lowPassUnit), 
       "Couldn't initialize lowpass unit"); 
} 

-(Buffer *)processedBuffer { 
    AudioBufferList *bufferlist = malloc(sizeof(AudioBufferList)); 
    UInt32 blockSize = 1024; 
    float *left = malloc(sizeof(float) * blockSize); 
    float *right = malloc(sizeof(float) * blockSize); 

    bufferlist->mBuffers[0].mData = left; 
    bufferlist->mBuffers[1].mData = right; 
    UInt32 size = sizeof(float) * blockSize; 

    AudioTimeStamp inTimeStamp; 
    memset(&inTimeStamp, 0, sizeof(AudioTimeStamp)); 
    inTimeStamp.mSampleTime = 0; 

    AudioUnitRenderActionFlags flag = 0; 

    NSUInteger length = ceil(self.buffer.size/(float)blockSize); 

    double *processed = malloc(sizeof(double) * blockSize * length); 

    for (int i = 0; i < length; i++) { 
     bufferlist->mBuffers[0].mDataByteSize = size; 
     bufferlist->mBuffers[1].mDataByteSize = size; 
     bufferlist->mNumberBuffers = 2; 
     inTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; 

     AudioUnitRender(player.lowPassUnit, &flag, &inTimeStamp, 0, blockSize, bufferlist); 
     for (NSUInteger j = 0; j < blockSize; j++) { 
      processed[j + (blockSize * i)] = left[j]; 
     } 
     inTimeStamp.mSampleTime += blockSize; 
    } 

    Buffer *buffer = [[Buffer alloc] initWithSamples:processed size:self.buffer.size sampleRate:self.buffer.sampleRate]; 

    free(bufferlist); 
    free(left); 
    free(right); 
    free(processed); 

    return buffer; 
} 

-(void)cleanup { 
    AudioOutputUnitStop(player.lowPassUnit); 
    AudioUnitUninitialize(player.lowPassUnit); 
    AudioComponentInstanceDispose(player.lowPassUnit); 
} 

@end 

Если добавить общий вывод и попытаться установить 8KHZ ASBD на входе, то я просто получить мусор шум на выходе .. Похоже, 0,0,0,0,0,17438231945853048031929171968.000000, 0,0,0, -2548199532257382185315640279040.000000 ... Yikes!

Я попытался добавить ASBD на вход и выход блока нижних частот, предоставив ему свойство частоты дискретизации 8 кГц, и он ничего не сделал. Я попытался добавить преобразовательные блоки (с ASBDs, установленными на 8 кГц) раньше, а затем, а затем до И после фильтра нижних частот (в цепочке) это также не сработало.

В качестве побочного вопроса, мой буфер представляет собой монограммы 8 кГц, и если у меня есть список буферов, у mNumberBuffers установлен 1, то мой обработчик ввода нижних частот никогда не вызывается ... Есть ли способ не использовать стереоканалы?

+0

Я могу взглянуть на проблему отсечения, если вы нажмете свои изменения (предположительно, на тот же проект, что и раньше) –

ответ

0

Я использую преобразователи на обоих концах с ASBD, установленным на 8000 выборочных монопотоков для ввода входного преобразователя и выхода выходного преобразователя при использовании стереозвука 44100.0 для ввода и вывода блока низких частот и вызова AudioUnitRender на концевом преобразователе без единицы io для автономного рендеринга. Для онлайн-рендеринга я помещаю блок преобразователя перед блоком io, поэтому обратный вызов визуализации будет извлекаться из буферов с 8 КБ для воспроизведения. Похоже, что более низкая частота дискретизации на выходе ASBD требует более высоких максимальных кадров на срез и меньшего фрагмента (AudioUnitRender inNumberFrames), и поэтому он не будет отображаться.

#import "ViewController.h" 
#import <AudioToolbox/AudioToolbox.h> 


@implementation ViewController{ 


    int sampleCount; 
    int renderBufferHead; 
    float *renderBuffer; 
} 


- (void)viewDidLoad { 

    [super viewDidLoad]; 
    float sampleRate = 8000; 

    int bufferSeconds = 3; 
    sampleCount = sampleRate * bufferSeconds;//seconds 
    float *originalSaw = generateSawWaveBuffer(440, sampleRate, sampleCount); 

    renderBuffer = originalSaw; 
    renderBufferHead = 0; 


    AURenderCallbackStruct cbStruct = {renderCallback,(__bridge void *)self}; 

    //this will do offline render using the render callback, callback just reads from renderBuffer at samplerate 
    float *processedBuffer = offlineRender(sampleCount, sampleRate, &cbStruct); 

    renderBufferHead = 0;//rewind render buffer after processing 

    //set up audio units to do live render using the render callback at sample rate then self destruct after delay 
    //it will play originalSaw for bufferSeconds, then after delay will switch renderBuffer to point at processedBuffer 
    float secondsToPlayAudio = (bufferSeconds + 1) * 2; 
    onlineRender(sampleRate, &cbStruct,secondsToPlayAudio); 


    //wait for original to finish playing, then change render callback source buffer to processed buffer 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((secondsToPlayAudio/2) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     renderBuffer = processedBuffer; 
     renderBufferHead = 0;//rewind render buffer 
    }); 

    //destroy after all rendering done 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(secondsToPlayAudio * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     free(originalSaw); 
     free(processedBuffer); 
    }); 
} 


float * offlineRender(int count, double sampleRate, AURenderCallbackStruct *cbStruct){ 

    AudioComponentInstance inConverter = getComponentInstance(kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter); 
    AudioComponentInstance lowPass = getComponentInstance(kAudioUnitType_Effect, kAudioUnitSubType_LowPassFilter); 
    AudioComponentInstance outConverter = getComponentInstance(kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter); 

    AudioStreamBasicDescription asbd = getMonoFloatASBD(sampleRate); 
    AudioUnitSetProperty(inConverter, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, sizeof(AudioStreamBasicDescription)); 
    AudioUnitSetProperty(outConverter, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &asbd, sizeof(AudioStreamBasicDescription)); 

    AudioUnitSetProperty(inConverter, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, cbStruct, sizeof(AURenderCallbackStruct)); 

    formatAndConnect(inConverter, lowPass); 
    formatAndConnect(lowPass, outConverter); 

    UInt32 maxFramesPerSlice = 4096; 
    AudioUnitSetProperty(inConverter, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(UInt32)); 
    AudioUnitSetProperty(lowPass, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(UInt32)); 
    AudioUnitSetProperty(outConverter, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(UInt32)); 

    AudioUnitInitialize(inConverter); 
    AudioUnitInitialize(lowPass); 
    AudioUnitInitialize(outConverter); 

    AudioUnitSetParameter(lowPass, kLowPassParam_CutoffFrequency, kAudioUnitScope_Global, 0, 500, 0); 

    AudioBufferList *bufferlist = malloc(sizeof(AudioBufferList) + sizeof(AudioBufferList));//stereo bufferlist + sizeof(AudioBuffer) 
    float *left = malloc(sizeof(float) * 4096); 
    bufferlist->mBuffers[0].mData = left; 
    bufferlist->mNumberBuffers = 1; 

    AudioTimeStamp inTimeStamp; 
    memset(&inTimeStamp, 0, sizeof(AudioTimeStamp)); 
    inTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; 
    inTimeStamp.mSampleTime = 0; 

    float *buffer = malloc(sizeof(float) * count); 
    int inNumberframes = 512; 
    AudioUnitRenderActionFlags flag = 0; 
    int framesRead = 0; 
    while (count){ 
     inNumberframes = MIN(inNumberframes, count); 
     bufferlist->mBuffers[0].mDataByteSize = sizeof(float) * inNumberframes; 
     printf("Offline Render %i frames\n",inNumberframes); 
     AudioUnitRender(outConverter, &flag, &inTimeStamp, 0, inNumberframes, bufferlist); 
     memcpy(buffer + framesRead, left, sizeof(float) * inNumberframes); 
     inTimeStamp.mSampleTime += inNumberframes; 
     count -= inNumberframes; 
     framesRead += inNumberframes; 

    } 
    free(left); 
// free(right); 
    free(bufferlist); 
    AudioUnitUninitialize(inConverter); 
    AudioUnitUninitialize(lowPass); 
    AudioUnitUninitialize(outConverter); 
    return buffer; 
} 

OSStatus renderCallback(void *       inRefCon, 
         AudioUnitRenderActionFlags * ioActionFlags, 
         const AudioTimeStamp *   inTimeStamp, 
         UInt32       inBusNumber, 
         UInt32       inNumberFrames, 
         AudioBufferList *    ioData){ 

    ViewController *self = (__bridge ViewController*)inRefCon; 
    float *left = ioData->mBuffers[0].mData; 

    for (int i = 0; i < inNumberFrames; i++) { 
     if (self->renderBufferHead >= self->sampleCount) { 
      left[i] = 0; 
     } 
     else{ 
      left[i] = self->renderBuffer[self->renderBufferHead++]; 
     } 
    } 
    if(ioData->mNumberBuffers == 2){ 
     memcpy(ioData->mBuffers[1].mData, left, sizeof(float) * inNumberFrames); 
    } 
    printf("render %f to %f\n",inTimeStamp->mSampleTime,inTimeStamp->mSampleTime + inNumberFrames); 
    return noErr; 
} 

void onlineRender(double sampleRate, AURenderCallbackStruct *cbStruct,float duration){ 
    AudioComponentInstance converter = getComponentInstance(kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter); 
    AudioComponentInstance ioUnit = getComponentInstance(kAudioUnitType_Output, kAudioUnitSubType_DefaultOutput); 

    AudioStreamBasicDescription asbd = getMonoFloatASBD(sampleRate); 
    AudioUnitSetProperty(converter, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, sizeof(AudioStreamBasicDescription)); 
    AudioUnitSetProperty(converter, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, cbStruct, sizeof(AURenderCallbackStruct)); 

    formatAndConnect(converter, ioUnit); 

    AudioUnitInitialize(converter); 
    AudioUnitInitialize(ioUnit); 
    AudioOutputUnitStart(ioUnit); 

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     AudioOutputUnitStop(ioUnit); 
     AudioUnitUninitialize(ioUnit); 
     AudioUnitUninitialize(converter); 
    }); 

} 

float * generateSawWaveBuffer(float frequency,float sampleRate, int sampleCount){ 
    float *buffer = malloc(sizeof(float) * sampleCount); 
    float increment = (frequency/sampleRate) * 2; 
    int increasing = 1; 
    float sample = 0; 
    for (int i = 0; i < sampleCount; i++) { 
     if (increasing) { 
      sample += increment; 
      if (sample >= 1) { 
       increasing = 0; 
      } 
     } 
     else{ 
      sample -= increment; 
      if (sample < -1) { 
       increasing = 1; 
      } 
     } 
     buffer[i] = sample; 
    } 
    return buffer; 
} 
AudioComponentInstance getComponentInstance(OSType type,OSType subType){ 
    AudioComponentDescription desc = {0}; 
    desc.componentFlags = 0; 
    desc.componentFlagsMask = 0; 
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; 
    desc.componentSubType = subType; 
    desc.componentType = type; 
    AudioComponent ioComponent = AudioComponentFindNext(NULL, &desc); 
    AudioComponentInstance unit; 
    AudioComponentInstanceNew(ioComponent, &unit); 
    return unit; 
} 


AudioStreamBasicDescription getMonoFloatASBD(double sampleRate){ 
    AudioStreamBasicDescription asbd = {0}; 
    asbd.mSampleRate = sampleRate; 
    asbd.mFormatID = kAudioFormatLinearPCM; 
    asbd.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked; 
    asbd.mFramesPerPacket = 1; 
    asbd.mChannelsPerFrame = 1; 
    asbd.mBitsPerChannel = 32; 
    asbd.mBytesPerPacket = 4; 
    asbd.mBytesPerFrame = 4; 
    return asbd; 
} 

void formatAndConnect(AudioComponentInstance src,AudioComponentInstance dst){ 

    AudioStreamBasicDescription asbd; 
    UInt32 propsize = sizeof(AudioStreamBasicDescription); 
    AudioUnitGetProperty(dst, kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,0,&asbd,&propsize); 
    AudioUnitSetProperty(src, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &asbd, sizeof(AudioStreamBasicDescription)); 

    AudioUnitConnection connection = {0}; 
    connection.destInputNumber = 0; 
    connection.sourceAudioUnit = src; 
    connection.sourceOutputNumber = 0; 
    AudioUnitSetProperty(dst, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, 0, &connection, sizeof(AudioUnitConnection)); 
} 
@end