2009-09-01 12 views
18

Chciałbym odtworzyć dźwięk syntezatora na iPhonie. Zamiast używać wcześniej nagranego dźwięku i używając SystemSoundID do odtworzenia istniejącego pliku binarnego, chciałbym go zsyntetyzować. Częściowo dlatego, że chcę odtwarzać dźwięk w sposób ciągły (np. Gdy palec użytkownika jest na ekranie) zamiast jednorazowej próbki dźwiękowej.Jak mogę zsyntetyzować dźwięki za pomocą CoreAudio na iPhone/Mac?

Gdybym chciał zsyntetyzować Środek A + 1 (A4) (440 Hz), mogę obliczyć falę sinusoidalną za pomocą sin(); to, czego nie wiem, to jak rozmieścić te bity w pakiecie, który może następnie odtwarzać CoreAudio. Większość tutoriali dostępnych w sieci dotyczy po prostu odtwarzania istniejących plików binarnych.

Czy ktoś może mi pomóc z prostą syntezowaną falą sinusoidalną o częstotliwości 440Hz?

Odpowiedz

13

Co chcesz zrobić, aby ustawić AudioQueue. Umożliwia wypełnienie bufora zsyntetyzowanymi danymi dźwiękowymi w ramach wywołania zwrotnego. Byś skonfigurować AudeioQueue uruchomić w nowym wątku, takich jak:

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

ty metoda zwrotna AudioQueueCallback zostaną zaproszeni gdy AudioQueue potrzebuje więcej danych. Zaimplementować coś podobnego:

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

To nie jest poprawne. Nie powinieneś wywoływać AudioQueueCallback w pętli alokacji. Nie wierzę też, że opis jest poprawny. Dodatkowo, powinieneś wywoływać AudioQueueStart (audioQueue, zero) zamiast w ten dziwny sposób. Zamiast tego spójrz na AudioUnit Framework. – thefaj

+0

@thefaj: Wierzę, że jesteś tym, który jest nieprawidłowy. Ten przykład pochodzi z mojej aplikacji SC68 Player (http://itunes.apple.com/se/app/sc68-player/id295290413?mt=8), w której pierwotnie pobrałem kod do odtwarzania dźwięku z przykładowej aplikacji Apple na iPhone'a SpeakHere (http://developer.apple.com/iphone/library/samplecode/SpeakHere/), spójrz na plik AQPlayer.mm. Pełny kod źródłowy dla odtwarzacza SC68 jest dostępny (http://www.peylow.se/sc68player.html). – PeyloW

+0

W przykładzie brakuje funkcji AudioQueueStart(), która powinna wywoływać funkcję AudioQueueCallback. – thefaj

0

Wiele technologii audio umożliwia przekazywanie danych zamiast pliku dźwiękowego. AVAudioPlayer, na przykład, ma:

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

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

Jednak nie jestem pewien, jak można przejść w ptr danych, należy uruchomić dźwięk, a następnie utrzymanie go w pętli, przekazując w innych ptrs danych, lub powtarzając to samo, itp.

4

Link Davide Vosti do http://lists.apple.com/archives/coreaudio-api/2008/Dec/msg00173.html już nie działa, ponieważ wykazy firmy Apple wydają się być obojętne. Oto pamięć podręczna Google dla kompletności.

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

Dodałbym to jako komentarz do pytania Davide'a, ale istnieje limit 600 znaków dla komentarzy. – AlBlue

Powiązane problemy