2012-04-13 3 views
2

J'ai un code qui lit un événement audio mono (bips courts à diverses fréquences). Je crée un AudioOutputUnit, l'arrête et chaque fois que j'ai besoin de lire l'audio. Je le commence. Quand je l'ai joué pendant le temps requis, je l'arrête.AudioOutputUnitStart très lent

Sons assez simples.

Cependant, AudioOutputUnitStart prendra habituellement 180ms pour revenir sur mon iPhone 4S (avec iOS 5.1), c'est beaucoup trop.

Voici la création/initialisation du AudioOutputUnit

void createAOU() 
{ 
    m_init = false; 
    // find the default playback output unit 
    AudioComponentDescription defaultOutputDescription; 
    defaultOutputDescription.componentType = kAudioUnitType_Output; 
    defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO; 
    defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple; 
    defaultOutputDescription.componentFlags = 0; 
    defaultOutputDescription.componentFlagsMask = 0; 

    // Get the default playback output unit 
    AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription); 
    if (defaultOutput == NULL) 
     return; 

    OSErr err = AudioComponentInstanceNew(defaultOutput, &m_toneUnit); 
    if (err != noErr) 
     return; 

    // single channel, floating point, linear PCM 
    AudioStreamBasicDescription streamFormat; 
    streamFormat.mSampleRate = m_framerate; 
    streamFormat.mFormatID = kAudioFormatLinearPCM; 
    streamFormat.mFormatFlags = 
    kLinearPCMFormatFlagIsFloat | 
    kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked; 
    streamFormat.mBytesPerPacket = sizeof(float); 
    streamFormat.mFramesPerPacket = 1; 
    streamFormat.mBytesPerFrame = sizeof(float); 
    streamFormat.mChannelsPerFrame = 1; 
    streamFormat.mBitsPerChannel = sizeof(float) * 8; 
    err = AudioUnitSetProperty (m_toneUnit, 
           kAudioUnitProperty_StreamFormat, 
           kAudioUnitScope_Input, 
           0, 
           &streamFormat, 
           sizeof(AudioStreamBasicDescription)); 
    if (err != noErr) 
     return; 

    // Attach callback to default output 
    AURenderCallbackStruct input; 
    input.inputProc = RenderTone; 
    input.inputProcRefCon = this; 
    err = AudioUnitSetProperty(m_toneUnit, 
           kAudioUnitProperty_SetRenderCallback, 
           kAudioUnitScope_Input, 
           0, 
           &input, 
           sizeof(input)); 
    if (err != noErr) 
     return; 

    float aBufferLength = 0.001; // In seconds 
    AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, 
          sizeof(aBufferLength), &aBufferLength); 

    err = AudioUnitInitialize(m_toneUnit); 
    if (err != noErr) 
     return; 
    reset(); 
    m_init = true; 
} 

Pour commencer, je voudrais simplement appeler:

OSErr err = AudioOutputUnitStart(m_toneUnit); 

Lorsque vous avez terminé:

OSErr err = AudioOutputUnitStop(m_toneUnit); 

J'ai essayé diverses choses: rendant la propriété kAudioSessionProperty_PreferredHardwareIOBufferDuration t o une valeur très faible (comme 5ms) Réduire le framerate (essayé 48kHz, 44.1kHz, 8kHz)

Peu importe ce que je fais ... première fois que le AudioUnit est démarré, AudioOutputUnitStart retourne seulement après 160-180ms. Si je m'arrête et que je démarre tout de suite, cela ne prend que 5m, ce qui est beaucoup plus acceptable.

La latence dans mon application est assez importante, et 180ms est définitivement inacceptable.

Des suggestions? J'ai vu quelques personnes poser des questions similaires, mais ils n'ont généralement jamais eu de réponse.

J'espère que cette fois les choses seront différentes :)

+0

Vous avez probablement déjà pensé à cela - mais l'ajout/suppression de renderCallback fonctionne-t-il ici? Aussi vu cela (bien que votre tampon est minuscule): http://stackoverflow.com/questions/5690460/objective-c-audiooutputunitstart-takes-a-long-time-to-start-possible-workaroun –

+0

Je n'ai pas réellement pensé à supprimer le rappel; dans le rappel, l'onde sonore est réellement générée, de sorte que le tampon audio peut être minuscule. J'essaierai – jyavenard

Répondre

2

La façon de traiter avec l'unité audio latence de démarrage est de commencer avant l'heure de l'unité audio nécessaire, et non l'arrêter. Au lieu de cela, il suffit de configurer la session audio pour les tampons courts, puis remplir les tampons de rappel de rendu de l'unité audio avec le silence jusqu'à ce qu'il soit temps de les remplir avec le son souhaité. Ensuite, après votre son, revenez à remplir les tampons avec le silence au lieu de vous arrêter.

+0

le problème qui me préoccupait est que vous savez avoir un 2ème thread en cours d'exécution à tout moment, et avec mon tampon audio court (pour réduire la latence) cela aura certainement un impact sur les performances un peu juste. – jyavenard

+1

@jyavenard: IIRC, une unité audio RemoteIO fonctionnant en continu avec des tampons très courts, utilise moins de 1% des cycles du processeur sur un iPhone 4. Tout impact sur les performances serait probablement totalement imperceptible. – hotpaw2