将1ch 16000Hz PCM转换为2ch 44100Hz PCM

时间:2014-04-15 09:10:00

标签: ios objective-c audio core-audio pcm

我需要使用以下格式转换PCM音频数据:

Data format:     1 ch,  16000 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer
            no channel layout.
estimated duration: 1.101063 sec
audio bytes: 35234
audio packets: 17617
bit rate: 256000 bits per second
packet size upper bound: 2
maximum packet size: 2
audio data file offset: 44
optimized
source bit depth: I16

至16位2ch(立体声)44100Hz PCM。

我的输入文件是NSData,理想情况下,如果我最终可以使用NSData而不是将输出保存到文件。我已经看过许多转换不同音频格式的教程和示例,但它们看起来非常复杂,我想知道是否有任何简单的解决方案可以做到这一点。这是我到目前为止尝试过的代码:

-(void)convertAudioToRequiredFormat:(NSData *)data {

AudioFileID refAudioFileID;
ExtAudioFileRef inputFileID;
ExtAudioFileRef outputFileID;

OSStatus result = AudioFileOpenWithCallbacks((__bridge void *)(data), readProc, 0, getSizeProc, 0, kAudioFormatLinearPCM, &refAudioFileID);
if (result != noErr) {
    DLog(@"error reading input audio file");
}

result = ExtAudioFileWrapAudioFileID(refAudioFileID, false, &inputFileID);
if (result != noErr){
    DLog(@"problem in theAudioFileReaderWithData function Wraping the audio FileID: result code %i \n", (int)result);
}

AudioStreamBasicDescription clientFormat;
memset(&clientFormat, 0, sizeof(clientFormat));
clientFormat.mFormatID          = kAudioFormatLinearPCM;
clientFormat.mSampleRate        = 16000;
clientFormat.mFramesPerPacket   = 1;
clientFormat.mBytesPerPacket    = 2; //16 bits * 1 channel
clientFormat.mBytesPerFrame     = 2;
clientFormat.mChannelsPerFrame  = 1; //1 channel
clientFormat.mBitsPerChannel    = 16;
clientFormat.mFormatFlags       = kCAFLinearPCMFormatFlagIsLittleEndian;
clientFormat.mReserved          = 0;


AudioStreamBasicDescription outputFormat;
memset(&outputFormat, 0, sizeof(outputFormat));
outputFormat.mFormatID             = kAudioFormatLinearPCM;
outputFormat.mSampleRate           = 44100;
outputFormat.mFramesPerPacket      = 1;  //it is always 1 for PCM
outputFormat.mBytesPerPacket       = 4;  //4 Bytes = 2 * 16 bits
outputFormat.mBytesPerFrame        = 4;
outputFormat.mChannelsPerFrame     = 2;  //2 channels = stereo
outputFormat.mBitsPerChannel       = 16; //16 bits per channel
outputFormat.mFormatFlags          = kCAFLinearPCMFormatFlagIsLittleEndian;
clientFormat.mReserved             = 0;

UInt32 outputFormatSize = sizeof(outputFormat);
result = 0;
result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outputFormatSize, &outputFormat);
if(result != noErr)
    NSLog(@"could not set the output format with status code %i \n",(int)result);

NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [docPaths objectAtIndex:0];
NSString *path = [docPath stringByAppendingPathComponent:@"newFormat.wav"];
CFURLRef sourceURL = (__bridge CFURLRef)[[NSURL alloc] initFileURLWithPath:path];
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:path]) {
    NSData *content = [NSData dataWithBytes:NULL length:0];
    [fm createFileAtPath:path contents:content attributes:nil];
}

result      =  0;
result      =  ExtAudioFileCreateWithURL(sourceURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outputFileID);
if(result != noErr){
    NSLog(@"ExtAudioFileCreateWithURL failed for outputFileID with status %i \n", (int)result);
}
int size = sizeof(clientFormat);
result = 0;
result = ExtAudioFileSetProperty(inputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
if(result != noErr)
    NSLog(@"error on ExtAudioFileSetProperty for input File with result code %i \n", (int)result);
size = sizeof(clientFormat);
result = 0;
result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
if(result != noErr)
    NSLog(@"error on ExtAudioFileSetProperty for output File with result code %i \n", (int)result);
int totalFrames = 0;
UInt32 outputFilePacketPosition = 0; //in bytes
UInt32 encodedBytes = 0;
while (1) {
    UInt32 bufferByteSize       = 22050 * 4 * 2;
    char srcBuffer[bufferByteSize];
    UInt32 numFrames            = (bufferByteSize/clientFormat.mBytesPerFrame);
    AudioBufferList fillBufList;
    fillBufList.mNumberBuffers  = 1;
    fillBufList.mBuffers[0].mNumberChannels     = clientFormat.mChannelsPerFrame;
    fillBufList.mBuffers[0].mDataByteSize       = bufferByteSize;
    fillBufList.mBuffers[0].mData               = srcBuffer;
    result = 0;
    result = ExtAudioFileRead(inputFileID, &numFrames, &fillBufList);
    if (result != noErr) {
        NSLog(@"Error on ExtAudioFileRead with result code %i \n", (int)result);
        totalFrames = 0;
        break;
    }
    if (!numFrames)
        break;
    totalFrames = totalFrames + numFrames;
    result = 0;
    result = ExtAudioFileWrite(outputFileID,
                               numFrames,
                               &fillBufList);
    if(result!= noErr){
        NSLog(@"ExtAudioFileWrite failed with code %i \n", (int)result);
    }
    encodedBytes += numFrames  * clientFormat.mBytesPerFrame;
}
//Clean up
ExtAudioFileDispose(inputFileID);
ExtAudioFileDispose(outputFileID);
AudioFileClose(refAudioFileID);

}

static OSStatus readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount)
{
NSData *inAudioData = (__bridge NSData *) clientData;
size_t dataSize = inAudioData.length;
size_t bytesToRead = 0;
if(position < dataSize) {
    size_t bytesAvailable = dataSize - position;
    bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable;
    [inAudioData getBytes: buffer range:NSMakeRange(position, bytesToRead)];
} else {
    NSLog(@"data was not read \n");
    bytesToRead = 0;
}
if(actualCount)
    *actualCount = bytesToRead;
return noErr;
}


static SInt64 getSizeProc(void* clientData) {
    NSData *inAudioData = (__bridge NSData *) clientData;
    size_t dataSize = inAudioData.length;
    return dataSize;
}

不幸的是它不起作用,我最终会在那行中使用EXC_BAD_ACCESS:

result      =  ExtAudioFileCreateWithURL(sourceURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outputFileID);

我不知道是什么导致了这个错误(错误的AudioStreamBasicDescription?)。有人可以帮我解决吗?或者可能有更简单的方法将音频数据转换为所需的PCM格式?

1 个答案:

答案 0 :(得分:0)

一些tipps。

1)。     AudioFileID refAudioFileID;     ExtAudioFileRef inputFileID;     ExtAudioFileRef outputFileID;

^使用NULL初始化指针。

2)。     CFURLRef sourceURL =(__ bridge CFURLRef)[[NSURL alloc] initFileURLWithPath:path];

^做:     NSURL * sourceURL = [NSURL fileURLWithPath:path];

3)。     NSData * content = [NSData dataWithBytes:NULL length:0];

^使用:     NSData * content = [NSData data];

4)。     result = ExtAudioFileCreateWithURL(sourceURL,kAudioFileM4AType,&amp; outputFormat,NULL,kAudioFileFlags_EraseFile,&amp; outputFileID);

^看起来好像使用了错误的kAudioFileType:尝试kAudioFileAIFFType或kAudioFileWAVEType

我的两分钱。祝你好运!