2011-08-19 31 views
14

Tôi đang cố ghi lại âm thanh được tạo ra bởi đầu ra của bộ trộn.Cách ghi âm được tạo ra bởi đầu ra của bộ trộn (Đồ thị Âm thanh & Âm thanh Lõi của iOS)

Đối với thời điểm hiện tại, mã của tôi được dựa trên bản demo apple MixerHost iOS app: Một nút trộn được kết nối với một xa IO nút trên graphe âm thanh.

Và tôi thử đặt gọi lại trên nút IO từ xa đầu vào trên đầu ra của bộ trộn.

Tôi làm điều gì đó sai nhưng tôi không thể tìm thấy lỗi.

Đây là mã bên dưới. Này được thực hiện ngay sau khi đơn vị cài đặt đa kênh Mixer:

UInt32 flag = 1; 

// Enable IO for playback 
result = AudioUnitSetProperty(iOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 
           0, // Output bus 
           &flag, 
           sizeof(flag)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty EnableIO" withStatus: result]; return;} 

/* can't do that because *** AudioUnitSetProperty EnableIO error: -1073752493 00000000 
result = AudioUnitSetProperty(iOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 
           0, // Output bus 
           &flag, 
           sizeof(flag)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty EnableIO" withStatus: result]; return;} 
*/ 

Sau đó, tạo một định dạng stream:

// I/O stream format 
iOStreamFormat.mSampleRate   = 44100.0; 
iOStreamFormat.mFormatID   = kAudioFormatLinearPCM; 
iOStreamFormat.mFormatFlags   = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
iOStreamFormat.mFramesPerPacket  = 1; 
iOStreamFormat.mChannelsPerFrame = 1; 
iOStreamFormat.mBitsPerChannel  = 16; 
iOStreamFormat.mBytesPerPacket  = 2; 
iOStreamFormat.mBytesPerFrame  = 2; 

[self printASBD: iOStreamFormat]; 

Sau đó ảnh hưởng đến định dạng và xác định tỷ lệ mẫu:

result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 
           1, // Input bus 
           &iOStreamFormat, 
           sizeof(iOStreamFormat)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty StreamFormat" withStatus: result]; return;} 

result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
           0, // Output bus 
           &iOStreamFormat, 
           sizeof(iOStreamFormat)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty StreamFormat" withStatus: result]; return;} 

// SampleRate I/O 
result = AudioUnitSetProperty (iOUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 
           0, // Output 
           &graphSampleRate, 
           sizeof (graphSampleRate)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty (set I/O unit input stream format)" withStatus: result]; return;} 

Sau đó, tôi cố gắng đặt lại gọi lại.

Giải pháp 1 >>> callback ghi âm của tôi là không bao giờ được gọi là

effectState.rioUnit = iOUnit; 

AURenderCallbackStruct renderCallbackStruct; 
renderCallbackStruct.inputProc  = &recordingCallback; 
renderCallbackStruct.inputProcRefCon = &effectState; 
result = AudioUnitSetProperty (iOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 
           0, // Output bus 
           &renderCallbackStruct, 
           sizeof (renderCallbackStruct)); 
if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty SetRenderCallback" withStatus: result]; return;} 

Giải pháp 2 >>> treo ứng dụng của tôi lúc khởi động về vấn đề này

AURenderCallbackStruct renderCallbackStruct; 
renderCallbackStruct.inputProc  = &recordingCallback; 
renderCallbackStruct.inputProcRefCon = &effectState; 

result = AUGraphSetNodeInputCallback (processingGraph, iONode, 
             0, // Output bus 
             &renderCallbackStruct); 
if (noErr != result) {[self printErrorMessage: @"AUGraphSetNodeInputCallback (I/O unit input callback bus 0)" withStatus: result]; return;} 

Nếu bất cứ ai có một ý tưởng ...

CHỈNH SỬA GIẢI PHÁP 3 (nhờ an toàn hơn) >> bây giờ có một vấn đề định dạng

AudioStreamBasicDescription dstFormat = {0}; 
dstFormat.mSampleRate=44100.0; 
dstFormat.mFormatID=kAudioFormatLinearPCM; 
dstFormat.mFormatFlags=kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked; 
dstFormat.mBytesPerPacket=4; 
dstFormat.mBytesPerFrame=4; 
dstFormat.mFramesPerPacket=1; 
dstFormat.mChannelsPerFrame=2; 
dstFormat.mBitsPerChannel=16; 
dstFormat.mReserved=0; 

result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 
        1, 
        &stereoStreamFormat, 
        sizeof(stereoStreamFormat)); 

if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty" withStatus: result]; return;} 


result = AudioUnitSetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 
        0, 
        &stereoStreamFormat, 
        sizeof(stereoStreamFormat)); 

if (noErr != result) {[self printErrorMessage: @"AudioUnitSetProperty" withStatus: result]; return;} 


AudioUnitAddRenderNotify(
         iOUnit, 
         &recordingCallback, 
         &effectState 
         ); 

và cài đặt file:

if (noErr != result) {[self printErrorMessage: @"AUGraphInitialize" withStatus: result]; return;} 

// On initialise le fichier audio 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
NSString *documentsDirectory = [paths objectAtIndex:0]; 
NSString *destinationFilePath = [[[NSString alloc] initWithFormat: @"%@/output.caf", documentsDirectory] autorelease]; 
NSLog(@">>> %@", destinationFilePath); 
CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)destinationFilePath, kCFURLPOSIXPathStyle, false); 

OSStatus setupErr = ExtAudioFileCreateWithURL(destinationURL, kAudioFileWAVEType, &dstFormat, NULL, kAudioFileFlags_EraseFile, &effectState.audioFileRef); 
CFRelease(destinationURL); 
NSAssert(setupErr == noErr, @"Couldn't create file for writing"); 

setupErr = ExtAudioFileSetProperty(effectState.audioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &stereoStreamFormat); 
NSAssert(setupErr == noErr, @"Couldn't create file for format"); 

setupErr = ExtAudioFileWriteAsync(effectState.audioFileRef, 0, NULL); 
NSAssert(setupErr == noErr, @"Couldn't initialize write buffers for audio file"); 

Và gọi lại ghi:

static OSStatus recordingCallback  (void *       inRefCon, 
           AudioUnitRenderActionFlags *  ioActionFlags, 
           const AudioTimeStamp *   inTimeStamp, 
           UInt32       inBusNumber, 
           UInt32       inNumberFrames, 
           AudioBufferList *     ioData) { 
if (*ioActionFlags == kAudioUnitRenderAction_PostRender && inBusNumber == 0) 
{ 
    EffectState *effectState = (EffectState *)inRefCon; 

    ExtAudioFileWriteAsync(effectState->audioFileRef, inNumberFrames, ioData); 
} 
return noErr;  
} 

Có cái gì đó thiếu trong file output output.caf :). Tôi hoàn toàn bị mất các định dạng để áp dụng.

+0

Tôi đang cố gắng để làm như vậy BT không thể thực hiện mã ur qua MixerHost Ví dụ u có thể plz giúp tôi .. – Aadil

+0

Hi lefakir và arlomedia, Có thể một trong các bạn có thể đăng lớp EffectState không? Tôi đang cố gắng để tái tạo mã làm việc bằng cách sử dụng MixerHost và ở trên. Tốt nhất, Gregor –

+0

Xin chào Gregor, bạn nên xem tại đây http://stackoverflow.com/questions/7032468/record-sounds-played-by-my-iphone-app-with-audio-units. Cấu trúc EffectState được khai báo trong câu hỏi này. – lefakir

Trả lời

15

Tôi không nghĩ bạn cần bật đầu vào trên thiết bị I/O. Tôi cũng sẽ nhận xét định dạng và cấu hình tốc độ mẫu mà bạn đang thực hiện trên thiết bị I/O cho đến khi bạn gọi lại, vì định dạng không phù hợp hoặc không được hỗ trợ có thể ngăn các đơn vị âm thanh được liên kết với nhau.

Để thêm gọi lại, hãy thử phương pháp này:

AudioUnitAddRenderNotify(
    iOUnit, 
    &recordingCallback, 
    self 
); 

Rõ ràng các phương pháp khác sẽ thay thế các kết nối nút, nhưng phương pháp này sẽ không - vì vậy đơn vị âm thanh của bạn có thể kết nối ngay cả khi bạn đã thêm một cuộc gọi lại.

Khi callback của bạn đang chạy, nếu bạn thấy rằng không có dữ liệu trong bộ đệm (ioData), quấn mã này khoảng mã callback của bạn:

if (*ioActionFlags == kAudioUnitRenderAction_PostRender) { 
    // your code 
} 

này là cần thiết vì một callback thêm theo cách này chạy cả trước và sau khi thiết bị âm thanh hiển thị âm thanh của nó, nhưng bạn chỉ muốn chạy mã sau khi nó hiển thị.

Khi cuộc gọi lại đang chạy, bước tiếp theo là tìm ra định dạng âm thanh nào nhận được và xử lý nó một cách thích hợp. Thử thêm này để gọi lại của bạn:

SInt16 *dataLeftChannel = (SInt16 *)ioData->mBuffers[0].mData; 
for (UInt32 frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber) { 
    NSLog(@"sample %lu: %d", frameNumber, dataLeftChannel[frameNumber]); 
} 

này sẽ làm chậm ứng dụng của bạn nhiều đến nỗi nó có thể sẽ ngăn chặn bất kỳ âm thanh từ thực sự chơi, nhưng bạn sẽ có thể chạy nó đủ lâu để xem những gì các mẫu như thế nào. Nếu gọi lại nhận âm thanh 16 bit, mẫu phải là số nguyên dương hoặc âm giữa -32000 và 32000. Nếu mẫu thay thế giữa số thường và số nhỏ hơn nhiều, hãy thử mã này trong hàm gọi lại của bạn thay vào đó:

SInt32 *dataLeftChannel = (SInt32 *)ioData->mBuffers[0].mData; 
for (UInt32 frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber) { 
    NSLog(@"sample %lu: %ld", frameNumber, dataLeftChannel[frameNumber]); 
} 

Điều này sẽ hiển thị cho bạn 8.24 mẫu hoàn chỉnh.

Nếu bạn có thể lưu dữ liệu ở định dạng cuộc gọi lại đang nhận, thì bạn nên có những gì bạn cần. Nếu bạn cần lưu nó ở định dạng khác, bạn sẽ có thể chuyển đổi định dạng trong thiết bị âm thanh I/O từ xa ... nhưng tôi haven't been able to figure out how to do that khi được kết nối với bộ trộn đa kênh. Thay vào đó, bạn có thể chuyển đổi dữ liệu bằng cách sử dụng Audio Converter Services. Thứ nhất, xác định các định dạng đầu vào và đầu ra:

AudioStreamBasicDescription monoCanonicalFormat; 
size_t bytesPerSample = sizeof (AudioUnitSampleType); 
monoCanonicalFormat.mFormatID   = kAudioFormatLinearPCM; 
monoCanonicalFormat.mFormatFlags  = kAudioFormatFlagsAudioUnitCanonical; 
monoCanonicalFormat.mBytesPerPacket = bytesPerSample; 
monoCanonicalFormat.mFramesPerPacket = 1; 
monoCanonicalFormat.mBytesPerFrame  = bytesPerSample; 
monoCanonicalFormat.mChannelsPerFrame = 1; 
monoCanonicalFormat.mBitsPerChannel = 8 * bytesPerSample; 
monoCanonicalFormat.mSampleRate  = graphSampleRate; 

AudioStreamBasicDescription mono16Format; 
bytesPerSample = sizeof (SInt16); 
mono16Format.mFormatID   = kAudioFormatLinearPCM; 
mono16Format.mFormatFlags  = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
mono16Format.mChannelsPerFrame = 1; 
mono16Format.mSampleRate  = graphSampleRate; 
mono16Format.mBitsPerChannel = 16; 
mono16Format.mFramesPerPacket = 1; 
mono16Format.mBytesPerPacket = 2; 
mono16Format.mBytesPerFrame = 2; 

Sau đó xác định một chuyển đổi ở đâu đó bên ngoài gọi lại của bạn, và tạo ra một bộ đệm tạm thời để xử lý dữ liệu trong chuyển đổi:

AudioConverterRef formatConverterCanonicalTo16; 
@property AudioConverterRef formatConverterCanonicalTo16; 
@synthesize AudioConverterRef; 
AudioConverterNew(
    &monoCanonicalFormat, 
    &mono16Format, 
    &formatConverterCanonicalTo16 
); 

SInt16 *data16; 
@property (readwrite) SInt16 *data16; 
@synthesize data16; 
data16 = malloc(sizeof(SInt16) * 4096); 

Sau đó thêm video này vào callback của bạn , trước khi bạn lưu dữ liệu của mình:

UInt32 dataSizeCanonical = ioData->mBuffers[0].mDataByteSize; 
SInt32 *dataCanonical = (SInt32 *)ioData->mBuffers[0].mData; 
UInt32 dataSize16 = dataSizeCanonical; 

AudioConverterConvertBuffer(
    effectState->formatConverterCanonicalTo16, 
    dataSizeCanonical, 
    dataCanonical, 
    &dataSize16, 
    effectState->data16 
); 

Sau đó, bạn có thể lưu dữ liệu16, ở định dạng 16 bit và có thể là những gì bạn muốn lưu trong tệp của mình. Nó sẽ tương thích hơn và một nửa lớn như dữ liệu kinh điển.

Khi bạn đã hoàn tất, bạn có thể dọn dẹp một vài điều:

AudioConverterDispose(formatConverterCanonicalTo16); 
free(data16); 
+0

cảm ơn! gọi lại của tôi được xử lý nhưng tôi đang ghi âm tiếng ồn! – lefakir

+0

Tôi vừa cập nhật câu trả lời của mình để bao gồm các bước tiếp theo ngay bây giờ mà bạn có cuộc gọi lại đang chạy. – arlomedia

+0

Tôi chỉ chỉnh sửa câu hỏi của mình để cung cấp thêm mã về những gì tôi đang cố gắng làm – lefakir

Các vấn đề liên quan