2012-01-21 36 views
5

Tôi đang cố gắng viết (những gì nên được) một ứng dụng đơn giản có một loạt các đơn vị âm thanh theo thứ tự trong một biểu đồ và sau đó viết đầu ra vào một tập tin. Tôi đã thêm một cuộc gọi lại bằng cách sử dụng AUGraphAddRenderNotify. Đây là chức năng gọi lại của tôi:Cách viết đầu ra của AUGraph vào một tệp?

OSStatus MyAURenderCallback(void *inRefCon, 
         AudioUnitRenderActionFlags *actionFlags, 
         const AudioTimeStamp *inTimeStamp, 
         UInt32 inBusNumber, 
         UInt32 inNumberFrames, 
         AudioBufferList *ioData) { 
    if (*actionFlags & kAudioUnitRenderAction_PostRender) { 
     ExtAudioFileRef outputFile = (ExtAudioFileRef)inRefCon; 
     ExtAudioFileWriteAsync(outputFile, inNumberFrames, ioData); 
    } 
} 

Loại tác phẩm này. Các tập tin có thể chơi được và tôi có thể nghe những gì tôi ghi lại nhưng có một số lượng khủng khiếp của tĩnh mà làm cho nó hầu như không nghe được.

Có ai biết điều gì sai với điều này không? Hoặc có ai biết cách tốt hơn để ghi lại đầu ra AUGraph vào một tập tin không?

Cảm ơn sự giúp đỡ.

+0

Làm các định dạng của biểu đồ AUGraph và ExtAudioFile (định dạng dữ liệu khách hàng) của bạn? Ngoài ra, bạn có cần ghi lại trong thời gian thực không? – sbooth

+0

Tôi nghĩ chúng phù hợp. Tôi đã sử dụng cùng một AudioStreamBasicDescription để định cấu hình tất cả các đơn vị âm thanh và tạo tệp. Tôi không chắc chắn làm thế nào để thực sự kiểm tra mặc dù. Bạn có biết làm thế nào để làm điều đó? Tôi đoán tôi sẽ xem xét nó. Tôi đoán tôi không cần phải ghi lại trong thời gian thực nhưng có cách nào khác để làm điều đó không? Nếu tôi không lưu dữ liệu một số nơi trong thời gian thực thì nó bị mất. – HowsItStack

+0

Tôi tin rằng thay vì gọi 'AUGraphStart' bạn muốn gọi' AudioUnitRender' nhiều lần trên đầu biểu đồ. Tôi nghĩ rằng vấn đề khi thực hiện nó trong thời gian thực là bộ đệm vòng trong của ExtAudioFile' sẽ lấp đầy và mất dữ liệu. – sbooth

Trả lời

6

tôi đã có một sự hiển linh liên quan đến đơn vị âm thanh chỉ là bây giờ mà đã giúp tôi giải quyết vấn đề của riêng tôi. Tôi đã có một quan niệm sai lầm về cách các kết nối đơn vị âm thanh và gọi lại hoạt động. Tôi nghĩ rằng họ đã hoàn toàn riêng biệt nhưng nó chỉ ra rằng một kết nối chỉ là ngắn tay cho một gọi lại render.

Làm một kAudioUnitProperty_MakeConnection từ đầu ra của âm thanh đơn vị A đến đầu vào của âm thanh đơn vị B cũng giống như làm kAudioUnitProperty_SetRenderCallback trên đầu vào của đơn vị B và có chức năng gọi lại cuộc gọi AudioUnitRender trên đầu ra của âm thanh đơn vị A.

Tôi đã thử nghiệm điều này bằng cách thực hiện kết nối sau khi thiết lập gọi lại kết xuất của tôi và gọi lại hiển thị không còn được gọi.

Do đó, tôi đã có thể giải quyết vấn đề của tôi bằng cách làm như sau:

Và họ hàm callback của tôi đã làm một cái gì đó như thế này:

OSStatus MyAURenderCallback(void *inRefCon, 
         AudioUnitRenderActionFlags *actionFlags, 
         const AudioTimeStamp *inTimeStamp, 
         UInt32 inBusNumber, 
         UInt32 inNumberFrames, 
         AudioBufferList *ioData) { 

    AudioUnit mixerUnit = (AudioUnit)inRefCon; 

    AudioUnitRender(mixerUnit, 
        actionFlags, 
        inTimeStamp, 
        0, 
        inNumberFrames, 
        ioData); 

    ExtAudioFileWriteAsync(outputFile, 
          inNumberFrames, 
          ioData); 

    return noErr; 
} 

này có lẽ cần phải có được rõ ràng với tôi nhưng vì nó không phải là tôi sẽ đặt cược có những người khác đã bị nhầm lẫn trong cùng một cách vì vậy hy vọng điều này là hữu ích cho họ quá.

Tôi vẫn không chắc tại sao tôi gặp sự cố với cuộc gọi lại AUGraphAddRenderNotify. Tôi sẽ đào sâu hơn về điều này sau nhưng bây giờ tôi đã tìm ra một giải pháp có vẻ hiệu quả.

+0

Mã trên có thực sự hoạt động không? Tôi sẽ nghĩ rằng nó sẽ ném một lỗi kể từ khi bạn đặt refCon để 'tự' và sau đó trong gọi lại đúc nó như là một AUMixer. –

+0

Bạn chính xác. Tôi hơi đơn giản hóa mã từ những gì có trong dự án của tôi. Tôi đã qua đời và sau đó truy cập mixerUnit thông qua một tài sản. Tôi đã cập nhật bài đăng của mình để vượt qua trong mixerUnit là đơn vị trước đơn vị đầu ra có đầu ra mà tôi muốn ghi vào tệp trước khi chuyển nó tới đơn vị đầu ra. – HowsItStack

+0

Ok- đã tự hỏi. Bạn có thể quan tâm đến giải pháp của tôi để lưu đầu ra đồ thị vào tệp - đặt renderCallback trên cá thể từ xa và sau đó nhận dữ liệu được hiển thị trong giai đoạn 'post_Render'. Không biết nếu đó là một cách 'tốt' để làm điều đó nhưng nó hoạt động. Cũng đã lưu trực tiếp vào định dạng nén ACC hoạt động (yay!): Http://stackoverflow.com/questions/10113977/recording-to-aac-from-remoteio-data-is-getting-written-but-file-unplayable –

0

Có thể thử điều này. Sao chép dữ liệu từ cuộc gọi lại đơn vị âm thanh vào bộ đệm dài. Phát bộ đệm để kiểm tra nó, sau đó ghi toàn bộ bộ đệm vào một tệp sau khi bạn đã xác minh rằng toàn bộ bộ đệm là OK.

1

Dưới đây là một số mẫu mã từ Apple (dự án là PlaySequence, nhưng nó không phải là MIDI cụ thể) có thể giúp:

{ 
    CAStreamBasicDescription clientFormat = CAStreamBasicDescription(); 
    ca_require_noerr (result = AudioUnitGetProperty(outputUnit, 
                kAudioUnitProperty_StreamFormat, 
                kAudioUnitScope_Output, 0, 
                &clientFormat, &size), fail); 
    size = sizeof(clientFormat); 
    ca_require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail); 

    { 
     MusicTimeStamp currentTime; 
     AUOutputBL outputBuffer (clientFormat, numFrames); 
     AudioTimeStamp tStamp; 
     memset (&tStamp, 0, sizeof(AudioTimeStamp)); 
     tStamp.mFlags = kAudioTimeStampSampleTimeValid; 
     int i = 0; 
     int numTimesFor10Secs = (int)(10./(numFrames/srate)); 
     do { 
      outputBuffer.Prepare(); 
      AudioUnitRenderActionFlags actionFlags = 0; 
      ca_require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail); 

      tStamp.mSampleTime += numFrames; 

      ca_require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail);  

      ca_require_noerr (result = MusicPlayerGetTime (player, &currentTime), fail); 
      if (shouldPrint && (++i % numTimesFor10Secs == 0)) 
       printf ("current time: %6.2f beats\n", currentTime); 
     } while (currentTime < sequenceLength); 
    } 
} 
Các vấn đề liên quan