Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting audio channel from Linear PCM

Tags:

iphone

audio

pcm

I would like to extract a channel audio from the an LPCM raw file ie extract left and right channel of a stereo LPCM file. The LPCM is 16 bit depth,interleaved, 2 channels,litle endian. From what I gather the order of byte is {LeftChannel,RightChannel,LeftChannel,RightChannel...} and since it is 16 bit depth there will be 2 bytes of sample for each channel right?

So my question is if i want to extract the left channel then I would take the bytes in 0,2,4,6...n*2 address? while the right channel would be 1,3,4,...(n*2+1).

Also after extracting the audio channel, should i set the format of the extracted channel as 16 bit depth ,1 channel?

Thanks in advance

This is the code that I currently use to extract PCM audio from AssetReader.. This code works fine with writing a music file without its channel being extracted so I it might be caused by the format or something...

    NSURL *assetURL = [song valueForProperty:MPMediaItemPropertyAssetURL];
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];
NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, 
                                [NSNumber numberWithFloat:44100.0], AVSampleRateKey,
                                [NSNumber numberWithInt:2], AVNumberOfChannelsKey,
                            //  [NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey,
                                [NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
                                [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
                                [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                                [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
                                nil];
NSError *assetError = nil;
AVAssetReader *assetReader = [[AVAssetReader assetReaderWithAsset:songAsset
                                                            error:&assetError]
                              retain];
if (assetError) {
    NSLog (@"error: %@", assetError);
    return;
}

AVAssetReaderOutput *assetReaderOutput = [[AVAssetReaderAudioMixOutput 
                                           assetReaderAudioMixOutputWithAudioTracks:songAsset.tracks
                                           audioSettings: outputSettings]
                                          retain];
if (! [assetReader canAddOutput: assetReaderOutput]) {
    NSLog (@"can't add reader output... die!");
    return;
}
[assetReader addOutput: assetReaderOutput];


NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [dirs objectAtIndex:0];

//CODE TO SPLIT STEREO
[self setupAudioWithFormatMono:kAudioFormatLinearPCM];
NSString *splitExportPath = [[documentsDirectoryPath stringByAppendingPathComponent:@"monoleft.caf"] retain];
if ([[NSFileManager defaultManager] fileExistsAtPath:splitExportPath]) {
    [[NSFileManager defaultManager] removeItemAtPath:splitExportPath error:nil];
}

AudioFileID mRecordFile;
NSURL *splitExportURL = [NSURL fileURLWithPath:splitExportPath];


OSStatus status =  AudioFileCreateWithURL(splitExportURL, kAudioFileCAFType, &_streamFormat, kAudioFileFlags_EraseFile,
                                          &mRecordFile);

NSLog(@"status os %d",status);

[assetReader startReading];

CMSampleBufferRef sampBuffer = [assetReaderOutput copyNextSampleBuffer];
UInt32 countsamp= CMSampleBufferGetNumSamples(sampBuffer);
NSLog(@"number of samples %d",countsamp);

SInt64 countByteBuf = 0;
SInt64 countPacketBuf = 0;
UInt32 numBytesIO = 0;
UInt32 numPacketsIO = 0;
NSMutableData * bufferMono = [NSMutableData new];
while (sampBuffer) {


    AudioBufferList  audioBufferList;
    CMBlockBufferRef blockBuffer;
    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampBuffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer);
    for (int y=0; y<audioBufferList.mNumberBuffers; y++) {
        AudioBuffer audioBuffer = audioBufferList.mBuffers[y];
        //frames = audioBuffer.mData;
        NSLog(@"the number of channel for buffer number %d is %d",y,audioBuffer.mNumberChannels);
        NSLog(@"The buffer size is %d",audioBuffer.mDataByteSize);






        //Append mono left to buffer data
        for (int i=0; i<audioBuffer.mDataByteSize; i= i+4) {
            [bufferMono appendBytes:(audioBuffer.mData+i) length:2];
        }

        //the number of bytes in the mutable data containing mono audio file
        numBytesIO = [bufferMono length];
        numPacketsIO = numBytesIO/2;
        NSLog(@"numpacketsIO %d",numPacketsIO);
        status = AudioFileWritePackets(mRecordFile, NO, numBytesIO, &_packetFormat, countPacketBuf, &numPacketsIO, audioBuffer.mData);
        NSLog(@"status for writebyte %d, packets written %d",status,numPacketsIO);
        if(numPacketsIO != (numBytesIO/2)){
            NSLog(@"Something wrong");
            assert(0);
        }


        countPacketBuf = countPacketBuf + numPacketsIO;
        [bufferMono setLength:0];


    }

    sampBuffer = [assetReaderOutput copyNextSampleBuffer];
    countsamp= CMSampleBufferGetNumSamples(sampBuffer);
    NSLog(@"number of samples %d",countsamp);
}
AudioFileClose(mRecordFile);
[assetReader cancelReading];
[self performSelectorOnMainThread:@selector(updateCompletedSizeLabel:)
                       withObject:0
                    waitUntilDone:NO];

The output format with audiofileservices is as follows:

        _streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
    _streamFormat.mBitsPerChannel = 16;
    _streamFormat.mChannelsPerFrame = 1;
    _streamFormat.mBytesPerPacket = 2;
    _streamFormat.mBytesPerFrame = 2;// (_streamFormat.mBitsPerChannel / 8) * _streamFormat.mChannelsPerFrame;
    _streamFormat.mFramesPerPacket = 1;
    _streamFormat.mSampleRate = 44100.0;

    _packetFormat.mStartOffset = 0;
    _packetFormat.mVariableFramesInPacket = 0;
    _packetFormat.mDataByteSize = 2;
like image 231
Samuel Avatar asked Jan 07 '11 22:01

Samuel


1 Answers

Sounds almost right - you have a 16 bit depth, so that means each sample will take 2 bytes. That means the left channel data will be in bytes {0,1}, {4,5}, {8,9} and so on. Interleaved means the samples are interleaved, not the bytes. Other than that I would try it out and see if you have any problems with your code.

Also after extracting the audio channel, should i set the format of the extracted channel as 16 bit depth ,1 channel?

Only one of the two channels is remaining after your extraction, so yes, this is correct.

like image 136
BrokenGlass Avatar answered Sep 19 '22 18:09

BrokenGlass