2012-09-10 13 views
5

Durante el fin de semana he tropezado con un obstáculo para aprender a programar síntesis de audio en iOS. He estado desarrollando en iOS durante varios años, pero estoy entrando en el aspecto de síntesis de audio. En este momento, solo estoy programando aplicaciones de demostración para ayudarme a aprender los conceptos. En la actualidad, he podido construir y apilar ondas sinusoidales en un procesador de reproducción para unidades de audio sin problemas. Pero, quiero entender lo que está sucediendo en el renderizador para poder renderizar 2 ondas senoidales separadas en cada canal izquierdo y derecho. Actualmente, supongo que en mi sección de audio init que tendría que realizar los siguientes cambios:Unidad de audio iOS - Creación de ondas sinusoidales estéreo

Desde:

AudioStreamBasicDescription audioFormat; 
    audioFormat.mSampleRate = kSampleRate; 
    audioFormat.mFormatID = kAudioFormatLinearPCM; 
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    audioFormat.mFramesPerPacket = 1; 
    audioFormat.mChannelsPerFrame = 1; 
    audioFormat.mBitsPerChannel = 16; 
    audioFormat.mBytesPerPacket = 2; 
    audioFormat.mBytesPerFrame = 2; 

Para:

AudioStreamBasicDescription audioFormat; 
    audioFormat.mSampleRate = kSampleRate; 
    audioFormat.mFormatID = kAudioFormatLinearPCM; 
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    audioFormat.mFramesPerPacket = 1; 
    audioFormat.mChannelsPerFrame = 2; 
    audioFormat.mBitsPerChannel = 16; 
    audioFormat.mBytesPerPacket = 4; 
    audioFormat.mBytesPerFrame = 4; 

embargo, el procesador es un poco griego para mí . He estado trabajando en cualquier tutorial o código de ejemplo que pueda encontrar. Puedo hacer que las cosas funcionen para el contexto dado de una señal mono, pero no puedo hacer que el renderizador genere señales estéreo. Todo lo que quiero es una frecuencia distinta en un canal izquierdo y una frecuencia diferente en un canal derecho, pero la verdad es que no entiendo lo suficiente el renderizador como para que funcione. He intentado la función memcpy en mBuffers [0] y mbuffers [1], pero eso bloquea la aplicación. Mi render está debajo (actualmente contiene ondas sinusoidales apiladas, pero para el ejemplo estéreo puedo usar una onda de una frecuencia establecida en cada canal).

#define kOutputBus 0 
#define kSampleRate 44100 
//44100.0f 
#define kWaveform (M_PI * 2.0f/kSampleRate) 

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

     HomeViewController *me = (HomeViewController *)inRefCon; 

    static int phase = 1; 
    static int phase1 = 1; 

    for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) { 

     int samples = ioData->mBuffers[i].mDataByteSize/sizeof(SInt16); 

     SInt16 values[samples]; 

     float waves; 
     float volume=.5; 
     float wave1; 

     for(int j = 0; j < samples; j++) { 


      waves = 0; 
      wave1 = 0; 

      MyManager *sharedManager = [MyManager sharedManager]; 


      wave1 = sin(kWaveform * sharedManager.globalFr1 * phase1)*sharedManager.globalVol1; 
      if (0.000001f > wave1) { 
       [me setFr1:sharedManager.globalFr1]; 
       phase1 = 0; 
       //NSLog(@"switch"); 
      } 

      waves += wave1; 
      waves += sin(kWaveform * sharedManager.globalFr2 * phase)*sharedManager.globalVol2; 
      waves += sin(kWaveform * sharedManager.globalFr3 * phase)*sharedManager.globalVol3; 
      waves += sin(kWaveform * sharedManager.globalFr4 * phase)*sharedManager.globalVol4; 
      waves += sin(kWaveform * sharedManager.globalFr5 * phase)*sharedManager.globalVol5; 
      waves += sin(kWaveform * sharedManager.globalFr6 * phase)*sharedManager.globalVol6; 
      waves += sin(kWaveform * sharedManager.globalFr7 * phase)*sharedManager.globalVol7; 
      waves += sin(kWaveform * sharedManager.globalFr8 * phase)*sharedManager.globalVol8; 
      waves += sin(kWaveform * sharedManager.globalFr9 * phase)*sharedManager.globalVol9; 
      waves *= 32767/9; // <--------- make sure to divide by how many waves you're stacking 

      values[j] = (SInt16)waves; 
      values[j] += values[j]<<16; 

      phase++; 
      phase1++; 

     } 

     memcpy(ioData->mBuffers[i].mData, values, samples * sizeof(SInt16)); 

    } 


    return noErr; 

} 

¡Gracias de antemano por cualquier ayuda!

+0

Si el formato está intercalado (como sugiere su ASBD), las muestras estarán en un búfer alternando a la izquierda y a la derecha: 'LRLRLRLR'. Sin embargo, sería inusual tener un formato intercalado en una devolución de llamada, por lo general, el formato es el formato canónico para el sistema operativo. – sbooth

+0

Gracias. De hecho, lo descubrí hace solo unos minutos. Sin embargo, está intercalado como dijiste. Tuve que encontrar la manera de recorrer la devolución de llamada para renderizar las ondas sinusoidales en distintos canales de L & R. ¡Gracias por la ayuda! – jwkerr

+0

Hola jwkerr, esperaba poder hablar contigo para publicar tu función de representación. He intentado que la reproducción estéreo funcione por un tiempo y no puedo obtenerla. Gracias – VaporwareWolf

Respuesta

3

El OP parece haber resuelto su problema, pero pensé que publicar una respuesta explícita sería útil para el resto de nosotros.

Tenía la misma pregunta de querer dirigir los tonos a los canales izquierdo y derecho de forma independiente. Es más fácil de describir en términos de ahora estándar de Matt Gallagher An iOS tone generator (an introduction to AudioUnits).

El primer cambio para hacer es establecer (siguiendo @jwkerr) streamFormat.mChannelsPerFrame = 2; (en lugar de streamFormat.mChannelsPerFrame = 1;) en el método createToneUnit. Una vez hecho esto y usted tiene dos canales/buffers en cada cuadro, es necesario rellenar los amortiguadores izquierdo y derecho de forma independiente en RenderTone():

// Set the left and right buffers independently 
Float32 tmp; 
Float32 *buffer0 = (Float32 *)ioData->mBuffers[0].mData; 
Float32 *buffer1 = (Float32 *)ioData->mBuffers[1].mData; 

// Generate the samples 
for (UInt32 frame = 0; frame < inNumberFrames; frame++) { 
    tmp = sin(theta) * amplitude; 

    if (channelLR[0]) buffer0[frame] = tmp; else buffer0[frame] = 0; 
    if (channelLR[1]) buffer1[frame] = tmp; else buffer1[frame] = 0; 

    theta += theta_increment; 
    if (theta > 2.0 * M_PI) theta -= 2.0 * M_PI; 
} 

Por supuesto channelLR[2] es una matriz bool cuyos elementos se establece para indicar si el el canal respectivo es audible. Tenga en cuenta que el programa necesita establecer explícitamente los marcos de los canales silenciosos a cero, de lo contrario obtendrá algunos tonos divertidos.

+0

channelLR necesita más información. –

+0

Editado. ¿Eso es útil? – JohnK

+0

¿Cuándo configura channelLR? He intentado con su ejemplo, pero está estático –

Cuestiones relacionadas