2009-09-01 13 views
18

Me gustaría reproducir un sonido sintetizado en un iPhone. En lugar de usar un sonido pregrabado y usar SystemSoundID para reproducir un binario existente, me gustaría sintetizarlo. Parcialmente, eso es porque quiero poder reproducir el sonido continuamente (por ejemplo, cuando el dedo del usuario está en la pantalla) en lugar de una muestra de sonido única.¿Cómo puedo sintetizar sonidos con CoreAudio en iPhone/Mac?

Si quería sintetizar un Medio A + 1 (A4) (440Hz), puedo calcular una onda sinusoidal usando sin(); lo que no sé es cómo organizar esos bits en un paquete que CoreAudio puede reproducir. La mayoría de los tutoriales que existen en la red están relacionados con simplemente jugar binarios existentes.

¿Alguien me puede ayudar con una onda sinusoidal sintetizada simple a 440Hz?

Respuesta

13

Lo que desea hacer es probablemente configurar un AudioQueue. Le permite llenar un búfer con datos de audio sintetizados en una devolución de llamada. Se podría configurar la AudeioQueue para funcionar en un nuevo hilo como tal:

#define BUFFER_SIZE 16384 
#define BUFFER_COUNT 3 
static AudioQueueRef audioQueue; 
void SetupAudioQueue() { 
    OSStatus err = noErr; 
    // Setup the audio device. 
    AudioStreamBasicDescription deviceFormat; 
    deviceFormat.mSampleRate = 44100; 
    deviceFormat.mFormatID = kAudioFormatLinearPCM; 
    deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 
    deviceFormat.mBytesPerPacket = 4; 
    deviceFormat.mFramesPerPacket = 1; 
    deviceFormat.mBytesPerFrame = 4; 
    deviceFormat.mChannelsPerFrame = 2; 
    deviceFormat.mBitsPerChannel = 16; 
    deviceFormat.mReserved = 0; 
    // Create a new output AudioQueue for the device. 
    err = AudioQueueNewOutput(&deviceFormat, AudioQueueCallback, NULL, 
           CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 
           0, &audioQueue); 
    // Allocate buffers for the AudioQueue, and pre-fill them. 
    for (int i = 0; i < BUFFER_COUNT; ++i) { 
     AudioQueueBufferRef mBuffer; 
     err = AudioQueueAllocateBuffer(audioQueue, BUFFER_SIZE, mBuffer); 
     if (err != noErr) break; 
     AudioQueueCallback(NULL, audioQueue, mBuffer); 
    } 
    if (err == noErr) err = AudioQueueStart(audioQueue, NULL); 
    if (err == noErr) CFRunLoopRun(); 
    } 

Usted método de devolución de llamada AudioQueueCallback a continuación, se llama cada vez que el AudioQueue necesita más datos. Implementar con algo como:

void AudioQueueCallback(void* inUserData, AudioQueueRef inAQ, 
         AudioQueueBufferRef inBuffer) { 
    void* pBuffer = inBuffer->mAudioData; 
    UInt32 bytes = inBuffer->mAudioDataBytesCapacity; 
    // Write max <bytes> bytes of audio to <pBuffer> 
    outBuffer->mAudioDataByteSize = actualNumberOfBytesWritten 
    err = AudioQueueEnqueueBuffer(audioQueue, inBuffer, 0, NULL); 
} 
+0

Esto no es correcto. No debería llamar a AudioQueueCallback en el bucle de asignación. No creo que la descripción sea correcta, tampoco. Además, debería llamar a AudioQueueStart (audioQueue, nil) en lugar de esta forma extraña. Mire el Marco AudioUnit en su lugar. – thefaj

+0

@thefaj: Creo que eres el que está incorrecto. Este ejemplo está tomado de mi aplicación SC68 Player (http://itunes.apple.com/se/app/sc68-player/id295290413?mt=8), donde originalmente tomé el código de reproducción de audio de la aplicación de ejemplo de iPhone de Apple SpeakHere (http://developer.apple.com/iphone/library/samplecode/SpeakHere/), mira el archivo AQPlayer.mm. El código fuente completo para SC68 Player está disponible (http://www.peylow.se/sc68player.html). – PeyloW

+0

En su ejemplo falta AudioQueueStart(), que es cómo debe llamarse AudioQueueCallback. – thefaj

0

Muchas de las tecnologías de audio permiten pasar datos en lugar de un archivo de sonido. AVAudioPlayer, por ejemplo, tiene:

-initWithData:error: 
Initializes and returns an audio player for playing a designated memory buffer. 

- (id)initWithData:(NSData *)data error:(NSError **)outError 

Sin embargo, no estoy seguro de cómo le gustaría pasar en un PTR de datos, inicie el sonido, y luego mantenerlo bucle pasando en otros PAD de datos, o la repetición de la misma, etc.

4

enlace de Davide Vosti a http://lists.apple.com/archives/coreaudio-api/2008/Dec/msg00173.html ya no funciona, ya que las listas de Apple parecen ser insensibles. Aquí está el caché de Google para completar.

// 
// AudioUnitTestAppDelegate.m 
// AudioUnitTest 
// 
// Created by Marc Vaillant on 11/25/08. 
// Copyright __MyCompanyName__ 2008. All rights reserved. 
// 

#import "AudioUnitTestAppDelegate.h" 
#include <AudioUnit/AudioUnit.h> 
//#include "MachTimer.hpp" 
#include <vector> 
#include <iostream> 

using namespace std; 

#define kOutputBus 0 
#define kInputBus 1 
#define SAMPLE_RATE 44100 

vector<int> _pcm; 
int _index; 

@implementation AudioUnitTestAppDelegate 

@synthesize window; 

void generateTone(
       vector<int>& pcm, 
       int freq, 
       double lengthMS, 
       int sampleRate, 
       double riseTimeMS, 
       double gain) 
{ 
    int numSamples = ((double) sampleRate) * lengthMS/1000.; 
    int riseTimeSamples = ((double) sampleRate) * riseTimeMS/1000.; 

    if(gain > 1.) 
    gain = 1.; 
    if(gain < 0.) 
    gain = 0.; 

    pcm.resize(numSamples); 

    for(int i = 0; i < numSamples; ++i) 
    { 
    double value = sin(2. * M_PI * freq * i/sampleRate); 
    if(i < riseTimeSamples) 
     value *= sin(i * M_PI/(2.0 * riseTimeSamples)); 
    if(i > numSamples - riseTimeSamples - 1) 
     value *= sin(2. * M_PI * (i - (numSamples - riseTimeSamples) + riseTimeSamples)/ (4. * riseTimeSamples)); 

    pcm[i] = (int) (value * 32500.0 * gain); 
    pcm[i] += (pcm[i]<<16); 
    } 

} 

static OSStatus playbackCallback(void *inRefCon, 
            AudioUnitRenderActionFlags *ioActionFlags, 
            const AudioTimeStamp *inTimeStamp, 
            UInt32 inBusNumber, 
            UInt32 inNumberFrames, 
            AudioBufferList *ioData) 
{  
    cout<<"index = "<<_index<<endl; 
    cout<<"numBuffers = "<<ioData->mNumberBuffers<<endl; 

    int totalNumberOfSamples = _pcm.size(); 
    for(UInt32 i = 0; i < ioData->mNumberBuffers; ++i) 
    { 
     int samplesLeft = totalNumberOfSamples - _index; 
     int numSamples = ioData->mBuffers[i].mDataByteSize/4; 
     if(samplesLeft > 0) 
     { 
     if(samplesLeft < numSamples) 
     { 
      memcpy(ioData->mBuffers[i].mData, &_pcm[_index], samplesLeft * 4); 
      _index += samplesLeft; 
      memset((char*) ioData->mBuffers[i].mData + samplesLeft * 4, 0, (numSamples - samplesLeft) * 4) ; 
     } 
     else 
     { 
      memcpy(ioData->mBuffers[i].mData, &_pcm[_index], numSamples * 4) ; 
      _index += numSamples; 
     } 
     } 
     else 
     memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); 
    } 

    return noErr; 
} 

- (void)applicationDidFinishLaunching:(UIApplication *)application 
{  
    //generate pcm tone freq = 800, duration = 1s, rise/fall time = 5ms 

    generateTone(_pcm, 800, 1000, SAMPLE_RATE, 5, 0.8); 
    _index = 0; 

    OSStatus status; 
    AudioComponentInstance audioUnit; 

    // Describe audio component 
    AudioComponentDescription desc; 
    desc.componentType = kAudioUnitType_Output; 
    desc.componentSubType = kAudioUnitSubType_RemoteIO; 
    desc.componentFlags = 0; 
    desc.componentFlagsMask = 0; 
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; 

    // Get component 
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc); 

    // Get audio units 
    status = AudioComponentInstanceNew(inputComponent, &audioUnit); 
    //checkStatus(status); 

    UInt32 flag = 1; 
    // Enable IO for playback 
    status = AudioUnitSetProperty(audioUnit, 
        kAudioOutputUnitProperty_EnableIO, 
        kAudioUnitScope_Output, 
        kOutputBus, 
        &flag, 
        sizeof(flag)); 
    //checkStatus(status); 

    // Describe format 

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

    // Apply format 

    status = AudioUnitSetProperty(audioUnit, 
        kAudioUnitProperty_StreamFormat, 
        kAudioUnitScope_Input, 
        kOutputBus, 
        &audioFormat, 
        sizeof(audioFormat)); 
// checkStatus(status); 

    // Set output callback 
    AURenderCallbackStruct callbackStruct; 
    callbackStruct.inputProc = playbackCallback; 
    callbackStruct.inputProcRefCon = self; 
    status = AudioUnitSetProperty(audioUnit, 
        kAudioUnitProperty_SetRenderCallback, 
        kAudioUnitScope_Global, 
        kOutputBus, 
        &callbackStruct, 
        sizeof(callbackStruct)); 

    // Initialize 
    status = AudioUnitInitialize(audioUnit); 

    // Start playing 

    status = AudioOutputUnitStart(audioUnit); 

    [window makeKeyAndVisible]; 
} 


- (void)dealloc { 
    [window release]; 
    [super dealloc]; 
} 


@end 
+0

Lo habría agregado como un comentario a la pregunta de Davide, pero hay un límite de 600 caracteres para los comentarios. – AlBlue