2010-10-18 25 views
5

Tôi đang viết một ứng dụng iPhone sẽ ghi lại giọng nói của người dùng và cho dữ liệu âm thanh vào thư viện để sửa đổi như thay đổi tiến độ và độ cao. Tôi bắt đầu với ví dụ mã SpeakHere từ Apple:Định dạng dữ liệu từ ghi bằng khung âm thanh Queue

http://developer.apple.com/library/ios/#samplecode/SpeakHere/Introduction/Intro.html

Đó dự án đặt nền tảng cho ghi âm giọng nói của người dùng và chơi nó trở lại. Nó hoạt động tốt.

Bây giờ tôi đang đi sâu vào mã và tôi cần tìm hiểu cách nạp dữ liệu âm thanh vào thư viện SoundTouch (http://www.surina.net/soundtouch/) để thay đổi độ cao. Tôi đã quen thuộc với khung xếp hàng âm thanh trong khi xem xét mã và tôi đã tìm thấy nơi tôi nhận được dữ liệu âm thanh từ bản ghi.

Về cơ bản, bạn gọi số AudioQueueNewInput để tạo hàng đợi nhập mới. Bạn vượt qua một chức năng gọi lại được gọi là mỗi khi một đoạn dữ liệu âm thanh có sẵn. Đó là trong cuộc gọi lại này mà tôi cần truyền các khối dữ liệu vào SoundTouch.

Tôi có tất cả thiết lập, nhưng nhiễu tôi phát lại từ thư viện SoundTouch rất khó (nó hầu như không giống với bản gốc). Nếu tôi không vượt qua nó thông qua SoundTouch và chơi âm thanh gốc nó hoạt động tốt.

Về cơ bản, tôi thiếu điều gì đó về dữ liệu thực tế tôi đang đại diện. Tôi đã giả định rằng tôi đang nhận được một dòng của short s là mẫu, 1 mẫu cho mỗi kênh. Đó là cách SoundTouch mong đợi nó, vì vậy nó không phải là đúng bằng cách nào đó.

Đây là mã thiết lập hàng đợi âm thanh để bạn có thể xem cách cấu hình nó.

void AQRecorder::SetupAudioFormat(UInt32 inFormatID) 
{ 
memset(&mRecordFormat, 0, sizeof(mRecordFormat)); 

UInt32 size = sizeof(mRecordFormat.mSampleRate); 
XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, 
              &size, 
              &mRecordFormat.mSampleRate), "couldn't get hardware sample rate"); 

size = sizeof(mRecordFormat.mChannelsPerFrame); 
XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, 
              &size, 
              &mRecordFormat.mChannelsPerFrame), "couldn't get input channel count"); 

mRecordFormat.mFormatID = inFormatID; 
if (inFormatID == kAudioFormatLinearPCM) 
{ 
    // if we want pcm, default to signed 16-bit little-endian 
    mRecordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; 
    mRecordFormat.mBitsPerChannel = 16; 
    mRecordFormat.mBytesPerPacket = mRecordFormat.mBytesPerFrame = (mRecordFormat.mBitsPerChannel/8) * mRecordFormat.mChannelsPerFrame; 
    mRecordFormat.mFramesPerPacket = 1; 
} 
} 

Và đây là một phần của mã mà thực sự đặt nó lên:

SetupAudioFormat(kAudioFormatLinearPCM); 

    // create the queue 
    XThrowIfError(AudioQueueNewInput(
            &mRecordFormat, 
            MyInputBufferHandler, 
            this /* userData */, 
            NULL /* run loop */, NULL /* run loop mode */, 
            0 /* flags */, &mQueue), "AudioQueueNewInput failed"); 

Và cuối cùng, đây là callback mà xử lý dữ liệu âm thanh mới:

void AQRecorder::MyInputBufferHandler(void *inUserData, 
            AudioQueueRef inAQ, 
            AudioQueueBufferRef inBuffer, 
            const AudioTimeStamp *inStartTime, 
            UInt32 inNumPackets, 
            const AudioStreamPacketDescription *inPacketDesc) { 
AQRecorder *aqr = (AQRecorder *)inUserData; 
try { 
     if (inNumPackets > 0) { 
      CAStreamBasicDescription queueFormat = aqr->DataFormat(); 
      SoundTouch *soundTouch = aqr->getSoundTouch(); 

      soundTouch->putSamples((const SAMPLETYPE *)inBuffer->mAudioData, 
            inBuffer->mAudioDataByteSize/2/queueFormat.NumberChannels()); 

      SAMPLETYPE *samples = (SAMPLETYPE *)malloc(sizeof(SAMPLETYPE) * 10000 * queueFormat.NumberChannels()); 
      UInt32 numSamples; 
      while((numSamples = soundTouch->receiveSamples((SAMPLETYPE *)samples, 10000))) { 
       // write packets to file 
       XThrowIfError(AudioFileWritePackets(aqr->mRecordFile, 
                FALSE, 
                numSamples * 2 * queueFormat.NumberChannels(), 
                NULL, 
                aqr->mRecordPacket, 
                &numSamples, 
                samples), 
           "AudioFileWritePackets failed"); 
       aqr->mRecordPacket += numSamples; 
      } 
      free(samples); 
     } 

     // if we're not stopping, re-enqueue the buffe so that it gets filled again 
     if (aqr->IsRunning()) 
      XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL), "AudioQueueEnqueueBuffer failed"); 
} catch (CAXException e) { 
    char buf[256]; 
    fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf)); 
} 
} 

Bạn có thể thấy rằng Tôi đang chuyển dữ liệu trong inBuffer->mAudioData sang SoundTouch. Trong hàm gọi lại của tôi, những byte chính xác là gì, tức là làm cách nào để trích xuất các mẫu từ mAudioData?

+0

làm thế nào để bạn có kế hoạch phù hợp với giấy phép LGPL của thư viện SoundTouch với chính sách của Apple từ chối liên kết động thư viện cho ứng dụng iOS? – IlDan

+0

chúng ta có thể sử dụng thư viện soundTouch trong ios hợp pháp hay không? – Tornado

Trả lời

0

Bạn phải kiểm tra xem endianess, signedness, v.v. của những gì bạn đang nhận được phù hợp với những gì thư viện mong đợi. Sử dụng mFormatFlags của AudioStreamBasicDescription để xác định định dạng nguồn. Sau đó, bạn có thể phải chuyển đổi các mẫu (ví dụ: newSample = sample + 0x8000)

+3

Cuối cùng tôi đã phát hiện ra rằng SoundTouch đang mong đợi các mẫu được nổi. Biên dịch lại nó với các kiểu mẫu int hoạt động ngay bây giờ. Cảm ơn! –

1

Kết thúc mặc định của Hàng đợi âm thanh có thể ngược lại với những gì bạn mong đợi. Bạn có thể phải trao đổi các byte trên và dưới của mỗi mẫu âm thanh 16 bit sau khi ghi và trước khi phát.

sample_le = (0xff00 & (sample_be << 8)) | (0x00ff & (sample_be >> 8)) ; 
+0

Tôi cũng nghĩ về điều đó, và tôi đã tìm thấy một lá cờ tôi có thể chuyển sang các tùy chọn định dạng để làm cho nó trở thành phần cuối lớn thay vì kiểu cuối nhỏ sao cho tôi không phải làm theo cách thủ công. Đó không phải là vấn đề, nhưng cảm ơn! –

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