Формат данных из записи с использованием инфраструктуры Audio Queue
Я пишу приложение для iPhone, которое должно записывать голос пользователя и передавать аудиоданные в библиотеку для таких изменений, как изменение темпа и высоты тона. Я начал с примера кода SpeakHere от Apple:
http://developer.apple.com/library/ios/#samplecode/SpeakHere/Introduction/Intro.html
Этот проект закладывает основу для записи голоса пользователя и его воспроизведения. Это работает хорошо.
Сейчас я углубляюсь в код и мне нужно выяснить, как передавать аудиоданные в библиотеку SoundTouch (http://www.surina.net/soundtouch/) сменить высоту. Я познакомился с платформой Audio Queue во время прохождения кода и нашел место, где я получаю аудиоданные из записи.
По сути, вы звонитеAudioQueueNewInput
создать новую очередь ввода. Вы передаете функцию обратного вызова, которая вызывается каждый раз, когда доступен фрагмент аудиоданных. Именно в этом обратном вызове мне нужно передать куски данных в SoundTouch.
У меня все настроено, но шум, который я воспроизводил из библиотеки SoundTouch, очень статичен (он едва напоминает оригинал). Если я не пропущу его через SoundTouch и не воспроизведу оригинальное аудио, оно будет работать нормально.
По сути, я что-то упускаю из-за того, что представляют собой реальные данные, которые я получаю. Я предполагал, что я получаю потокshort
s, которые являются выборками, по 1 выборке для каждого канала. Вот как SoundTouch ожидает этого, так что, должно быть, это не так.
Вот код, который устанавливает аудио-очередь, чтобы вы могли увидеть, как она настроена.
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;
}
}
И вот часть кода, которая фактически устанавливает его:
SetupAudioFormat(kAudioFormatLinearPCM);
// create the queue
XThrowIfError(AudioQueueNewInput(
&mRecordFormat,
MyInputBufferHandler,
this /* userData */,
NULL /* run loop */, NULL /* run loop mode */,
0 /* flags */, &mQueue), "AudioQueueNewInput failed");
И, наконец, вот обратный вызов, который обрабатывает новые аудиоданные:
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));
}
}
Вы можете видеть, что я передаю данные вinBuffer->mAudioData
в SoundTouch. В моем обратном вызове, что именно представляют байты, т.е. как извлечь образцы изmAudioData
?