将 1ch 16000Hz PCM 转换为 2ch 44100Hz PCM
Posted
技术标签:
【中文标题】将 1ch 16000Hz PCM 转换为 2ch 44100Hz PCM【英文标题】:Converting 1ch 16000Hz PCM to 2ch 44100Hz PCM 【发布时间】:2014-04-15 09:10:00 【问题描述】:我需要转换格式为 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】:一些提示。
1.) AudioFileID refAudioFileID; ExtAudioFileRef 输入文件ID; ExtAudioFileRef 输出文件ID;
^ 用 NULL 初始化指针。
2.) CFURLRef sourceURL = (__bridge CFURLRef)[[NSURL alloc] initFileURLWithPath:path];
^ 做: NSURL *sourceURL = [NSURL fileURLWithPath:path];
3.) NSData *content = [NSData dataWithBytes:NULL 长度:0];
^ 使用: NSData *content = [NSData 数据];
4.) 结果 = ExtAudioFileCreateWithURL(sourceURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outputFileID);
^ 看起来好像您使用了错误的 kAudioFileType:尝试 kAudioFileAIFFType 或 kAudioFileWAVEType
我的两分钱。祝你好运!
【讨论】:
以上是关于将 1ch 16000Hz PCM 转换为 2ch 44100Hz PCM的主要内容,如果未能解决你的问题,请参考以下文章
使用 NAudio 将 PCM S16 LE (s16l) 转换为 GSM610