2009-09-01 8 views
18

Je voudrais jouer un son synthétisé dans un iPhone. Au lieu d'utiliser un son préenregistré et d'utiliser SystemSoundID pour jouer un binaire existant, j'aimerais le synthétiser. En partie, c'est parce que je veux être capable de jouer le son en continu (par exemple lorsque le doigt de l'utilisateur est sur l'écran) au lieu d'un échantillon sonore unique.Comment synthétiser des sons avec CoreAudio sur iPhone/Mac

Si je voulais synthétiser un milieu A + 1 (A4) (440Hz), je peux calculer une onde sinusoïdale en utilisant sin(); Ce que je ne sais pas, c'est comment organiser ces bits dans un paquet que CoreAudio peut ensuite lire. La plupart des didacticiels qui existent sur le net concernent simplement la lecture des binaires existants.

Quelqu'un peut-il m'aider avec une simple onde sonore sinusoïdale synthétisée à 440Hz?

Répondre

13

Ce que vous voulez probablement faire pour configurer un AudioQueue. Il vous permet de remplir un tampon avec des données audio synthétisées dans un rappel. Vous configurer le AudeioQueue pour fonctionner dans un nouveau thread en tant que tel:

#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(); 
    } 

méthode de rappel Vous AudioQueueCallback sera ensuite appelée chaque fois que le besoin AudioQueue plus de données. Mettre en œuvre avec quelque chose comme:

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

Ceci est incorrect. Vous ne devez pas appeler AudioQueueCallback dans la boucle d'allocation. Je ne crois pas que la description soit correcte, non plus. De plus, vous devriez appeler AudioQueueStart (audioQueue, nil) au lieu de cette façon étrange. Regardez plutôt le cadre AudioUnit. – thefaj

+0

@thefaj: Je crois que vous êtes celui qui est incorrect. Cet exemple est tiré de mon application SC68 Player (http://itunes.apple.com/se/app/sc68-player/id295290413?mt=8), où j'ai initialement pris le code pour la relecture audio à partir de l'exemple de l'application iPhone d'Apple SpeakHere (http://developer.apple.com/iphone/library/samplecode/SpeakHere/), regardez le fichier AQPlayer.mm. Le code source complet du lecteur SC68 est disponible (http://www.peylow.se/sc68player.html). – PeyloW

+0

Votre exemple ne contient pas AudioQueueStart() qui doit être appelé comme AudioQueueCallback. – thefaj

0

De nombreuses technologies audio permettent de transmettre des données au lieu d'un fichier audio. AVAudioPlayer, par exemple, a:

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

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

Cependant, je ne sais pas comment vous passeriez dans un PTR de données, démarrez le son, puis le garder en boucle en passant dans d'autres données REE, ou répéter la même chose,

4

lien de Davide Vosti à http://lists.apple.com/archives/coreaudio-api/2008/Dec/msg00173.html ne fonctionne plus, puisque les listes d'Apple semblent ne pas répondre. Voici le cache de Google pour l'exhaustivité.

// 
// 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

Je l'aurais ajouté comme commentaire à la question de Davide, mais il y a une limite de 600 caractères pour les commentaires. – AlBlue

Questions connexes