2012-09-10 2 views
5

주말 내내 iOS에서 오디오 합성을 프로그래밍하는 방법을 배우는 장애물이되었습니다. 몇 년 동안 iOS에서 개발 해왔지만 오디오 합성 측면에 착수하고 있습니다. 지금은 개념을 배우는 데 도움이되는 데모 애플리케이션을 프로그래밍하고 있습니다. 나는 현재 문제없이 오디오 유닛을위한 재생 렌더러에 사인파를 만들고 쌓을 수있었습니다. 그러나 렌더러에서 어떤 일이 일어나고 있는지 이해하고 싶기 때문에 각 왼쪽 및 오른쪽 채널에 2 개의 사인파를 렌더링 할 수 있습니다.iOS 오디오 장치 - 스테레오 사인파 만들기

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; 

하려면 :

에서

: 현재, 내 초기화 오디오 부분에서 나는 다음과 같이 변경해야한다고 가정

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; 

하지만를, 렌더러는 나에게 다소 그리스어 . 나는 내가 찾을 수있는 튜토리얼이나 샘플 코드를 가지고 일 해왔다. 모노 신호의 컨텍스트에 맞게 작업 할 수는 있지만 렌더러에서 스테레오 신호를 생성 할 수는 없습니다. 내가 원했던 것은 왼쪽 채널의 하나의 고유 주파수와 오른쪽 채널의 다른 주파수입니다. 그러나 솔직히 렌더러가 작동하도록 이해하지 못했습니다. memcpy 함수를 mBuffers [0] 및 mbuffers [1]에 시도했지만 응용 프로그램이 충돌합니다. 내 렌더링은 아래에 있습니다 (현재는 사인파가 겹쳐져 있지만 스테레오 예제에서는 각 채널에서 한 세트의 주파수 만 사용할 수 있습니다).

#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; 

} 

미리 도움 주셔서 감사합니다!

+0

형식이 인터리브 된 경우 (ASBD가 제안한대로) 샘플은 왼쪽과 오른쪽으로 번갈아 표시되는 한 개의 버퍼에 있습니다 :'LRLRLRLR'. 그러나 콜백에 인터리브 형식을 사용하는 것은 드문 경우입니다. 일반적으로 형식은 OS의 정식 형식입니다. – sbooth

+0

감사. 사실 몇 분 전에 모든 것을 다 알아 냈습니다. 그것은 당신이 말한 것처럼 인터리빙됩니다. 콜백을 통해 루프를 돌면서 사인파를 별개의 L & R 채널로 렌더링하는 방법을 찾아야했습니다. 도와 줘서 고마워 !! – jwkerr

+0

Hello jwkerr, 나는 당신에게 당신의 렌더링 기능 게시에 대해 이야기 할 수 있기를 희망했다. 나는 한동안 작동하는 입체 음향 렌더링을 얻으려고 노력해 왔으며 그것을 얻지 못했습니다. 감사합니다 – VaporwareWolf

답변

3

OP가 자신의 문제를 해결 한 것으로 보이지만 명시적인 답변을 게시하면 나머지는 도움이 될 것이라고 생각했습니다.

나는 왼쪽과 오른쪽 채널에 톤을 독립적으로 보내고 싶다는 동일한 질문을했습니다. Matt Gallagher의 현재 표준 인 An iOS tone generator (an introduction to AudioUnits)의 관점에서 설명하는 것이 가장 쉽습니다.

첫 번째 변경은 createToneUnit 메서드에서 (streamFormat.mChannelsPerFrame = 1; 대신)을 (@jwkerr 다음에) 설정하는 것입니다. 그 완료하고 각 프레임에 두 개의 채널/버퍼가되면, 당신은 RenderTone()에서 독립적으로 왼쪽과 오른쪽 버퍼를 작성해야합니다 : 물론 channelLR[2]

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

누구의 요소는 여부를 나타내는 설정 bool 배열입니다 각 채널은들을 수 있습니다. 프로그램이 무음 채널의 프레임을 명시 적으로 0으로 설정해야 함을주의하십시오. 그렇지 않으면 재미있는 음색이 나옵니다.

+0

channelLR은 더 설명해야합니다. –

+0

수정 됨. 도움이 되었습니까? – JohnK

+0

channelLR은 언제 설정합니까? 귀하의 예제를 시도했지만 정적입니다 –