Запуск кода пользовательского интерфейса из Audio Unit Render Proc на iOS - PullRequest
1 голос
/ 22 августа 2011

У меня есть аудиоустройство Multichannel Mixer, воспроизводящее аудиофайлы в приложении для iOS, и мне нужно выяснить, как обновить пользовательский интерфейс приложения и выполнить сброс, когда обратный вызов рендеринга достигает конца самого длинного аудиофайла (который является настроен на работу на автобусе 0). Как показывает мой код ниже, я пытаюсь использовать KVO для достижения этой цели (используя логическую переменную tapesUnderway - AutoreleasePool необходим, так как этот код Objective C работает за пределами своего обычного домена, см. http://www.cocoabuilder.com/archive/cocoa/57412-nscfnumber-no-pool-in-place-just-leaking.html).

static OSStatus tapesRenderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
    SoundBufferPtr sndbuf = (SoundBufferPtr)inRefCon;

    UInt32 bufferFrames = sndbuf[inBusNumber].numFrames;
    AudioUnitSampleType *in = sndbuf[inBusNumber].data; 

    // These mBuffers are the output buffers and are empty; these two lines are just  setting the references to them (via outA and outB)
    AudioUnitSampleType *outA = (AudioUnitSampleType *)ioData->mBuffers[0].mData;
    AudioUnitSampleType *outB = (AudioUnitSampleType *)ioData->mBuffers[1].mData;

    UInt32 sample = sndbuf[inBusNumber].sampleNum;


    // --------------------------------------------------------------
    // Set the start time here
    if(inBusNumber == 0 && !tapesFirstRenderPast)
    {
        printf("Tapes first render past\n");

        tapesStartSample = inTimeStamp->mSampleTime;
        tapesFirstRenderPast = YES;                     // MAKE SURE TO RESET THIS ON SONG RESTART
        firstPauseSample = tapesStartSample;
    }

    // --------------------------------------------------------------
    // Now process the samples
     for(UInt32 i = 0; i < inNumberFrames; ++i)
     {
         if(inBusNumber == 0)
         {
            // ------------------------------------------------------
            // Bus 0 is the backing track, and is always playing back

            outA[i] = in[sample++];
            outB[i] = in[sample++];     // For stereo set desc.SetAUCanonical to (2, true) and increment samples in both output calls

            lastSample = inTimeStamp->mSampleTime + (Float64)i;     // Set the last played sample in order to compensate for pauses


            // ------------------------------------------------------
            // Use this logic to mark end of tune
            if(sample >= (bufferFrames * 2) && !tapesEndPast)
            {
                // USE KVO TO NOTIFY METHOD OF VALUE CHANGE

                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                FuturesEPMedia *futuresMedia = [FuturesEPMedia sharedFuturesEPMedia];
                NSNumber *boolNo = [[NSNumber alloc] initWithBool: NO];
                [futuresMedia setValue: boolNo forKey: @"tapesUnderway"];
                [boolNo release];
                [pool release];

                tapesEndPast = YES;
            }
        }
        else
        {
            // ------------------------------------------------------
            // The other buses are the open sections, and are synched through the tapesSectionsTimes array

            Float64 sectionTime = tapesSectionTimes[inBusNumber] * kGraphSampleRate;        // Section time in samples
            Float64 currentSample = inTimeStamp->mSampleTime + (Float64)i;

            if(!isPaused && !playFirstRenderPast)
            {
                pauseGap += currentSample - firstPauseSample;
                playFirstRenderPast = YES;
                pauseFirstRenderPast = NO;
            }


            if(currentSample > (tapesStartSample + sectionTime + pauseGap) && sample < (bufferFrames * 2))
            {
                outA[i] = in[sample++];
                outB[i] = in[sample++];
            }
            else
            {
                outA[i] = 0;
                outB[i] = 0;
            }
        }
    }

   sndbuf[inBusNumber].sampleNum = sample;

   return noErr;
}

В тот момент, когда эта переменная изменяется, она запускает метод в self, но это приводит к недопустимой задержке (20-30 секунд) при выполнении из этого обратного вызова рендеринга (я думаю, потому что это код Objective C, работающий в высокоприоритетная звуковая нить?). Как мне эффективно инициировать такое изменение без задержки? (Триггер изменит кнопку паузы на кнопку воспроизведения и вызовет метод сброса, чтобы подготовиться к следующему воспроизведению.)

Спасибо

Ответы [ 2 ]

4 голосов
/ 30 августа 2011

Да.Не используйте код objc в потоке рендеринга, поскольку он имеет высокий приоритет.Если вы сохраняете состояние в памяти (ptr или struct), а затем получаете таймер в основном потоке для опроса (проверки) значений в памяти.Таймер не обязательно должен быть таким же быстрым, как поток рендеринга, и будет очень точным.

2 голосов
/ 08 марта 2013

Попробуйте это.

Global:

BOOL FlgTotalSampleTimeCollected = False;
Float64 HigestSampleTime = 0 ;
Float64 TotalSampleTime = 0;

in - (OSStatus) setUpAUFilePlayer:

AudioStreamBasicDescription fileASBD;
// get the audio data format from the file
UInt32 propSize = sizeof(fileASBD);
CheckError(AudioFileGetProperty(inputFile, kAudioFilePropertyDataFormat,
                                &propSize, &fileASBD),
           "couldn't get file's data format");
UInt64 nPackets;
UInt32 propsize = sizeof(nPackets);
CheckError(AudioFileGetProperty(inputFile, kAudioFilePropertyAudioDataPacketCount,
                                &propsize, &nPackets),
           "AudioFileGetProperty[kAudioFilePropertyAudioDataPacketCount] failed");
Float64 sTime = nPackets * fileASBD.mFramesPerPacket;
if (HigestSampleTime < sTime)
{
    HigestSampleTime = sTime;
}

В RenderCallBack:

if (*actionFlags & kAudioUnitRenderAction_PreRender)
{


    if (!THIS->FlgTotalSampleTimeCollected)
    {
        [THIS setFlgTotalSampleTimeCollected:TRUE];
       [THIS setTotalSampleTime:(inTimeStamp->mSampleTime + THIS->HigestSampleTime)];
    }


}
else if (*actionFlags & kAudioUnitRenderAction_PostRender)
{



    if (inTimeStamp->mSampleTime > THIS->TotalSampleTime)
    {
        NSLog(@"inTimeStamp->mSampleTime :%f",inTimeStamp->mSampleTime);
        NSLog(@"audio completed");
        [THIS callAudioCompletedMethodHere];

    }


}

Это сработало для меня.Тест в устройстве.

...