2009-09-01 10 views
18

Mi piacerebbe riprodurre un suono sintetizzato in un iPhone. Invece di usare un suono pre-registrato e utilizzare SystemSoundID per riprodurre un binario esistente, mi piacerebbe sintetizzarlo. In parte, è perché voglio essere in grado di riprodurre il suono in modo continuo (ad esempio quando il dito dell'utente è sullo schermo) invece di un campione sonoro unico.Come sintetizzare i suoni con CoreAudio su iPhone/Mac

Se volessi sintetizzare un Medio A + 1 (A4) (440Hz), posso calcolare un'onda sinusoidale usando sin(); quello che non so è come sistemare quei bit in un pacchetto che CoreAudio può quindi riprodurre. La maggior parte delle esercitazioni che esistono sulla rete riguardano semplicemente la riproduzione di binari esistenti.

Qualcuno può aiutarmi con un semplice sinusoide sinusoidale sintetizzato a 440Hz?

risposta

13

Cosa si vuole fare probabilmente per impostare un AudioQueue. Ti permette di riempire un buffer con dati audio sintetizzati in un callback. Si potrebbe impostare l'AudeioQueue per l'esecuzione in un nuovo thread come tale:

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

È metodo di callback AudioQueueCallback sarà poi chiamato ogni volta che l'AudioQueue ha bisogno di più dati. Implementare con qualcosa di simile:

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

Questo non è corretto. Non dovresti chiamare AudioQueueCallback nel ciclo di allocazione. Non credo nemmeno che la descrizione sia corretta. Inoltre, dovresti chiamare AudioQueueStart (audioQueue, nil) invece di questo strano modo. Guarda invece AudioUnit Framework. – thefaj

+0

@thefaj: credo che tu sia quello che non è corretto. Questo esempio è tratto dalla mia app SC68 Player (http://itunes.apple.com/se/app/sc68-player/id295290413?mt=8), dove in origine ho preso il codice per la riproduzione audio dall'app di iPhone di esempio di Apple SpeakHere (http://developer.apple.com/iphone/library/samplecode/SpeakHere/), guarda il file AQPlayer.mm. È disponibile il codice sorgente completo per il lettore SC68 (http://www.peylow.se/sc68player.html). – PeyloW

+0

Nell'esempio manca AudioQueueStart(), che deve essere chiamato AudioQueueCallback. – thefaj

0

Molte delle tecnologie audio consentono di trasferire i dati anziché un file audio. AVAudioPlayer, per esempio, ha:

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

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

Tuttavia, io non sono sicuro di come si potrebbe passare in una ptr di dati, avviare il suono, e quindi mantenendolo looping passando in altri PTR di dati, o ripetere la stessa, ecc.

4

collegamento di Davide Vosti per http://lists.apple.com/archives/coreaudio-api/2008/Dec/msg00173.html non funziona più, dal momento che le liste di Apple sembrano non rispondere. Ecco la cache di Google per completezza.

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

L'avrei aggiunto come commento alla domanda di Davide, ma c'è un limite di 600 caratteri per i commenti. – AlBlue