How to Add Playable(Such as Wav,Wmv) Header with Pcm Data/Buffer in iOS

How to add playable(such as wav,wmv) header with PCM data/buffer in iOS?

OK, I am answering my own question if it helps someone else. After few days of tireless trying, at last i have got it working. Below is a complete Function written with Objective-C and C. It takes a file path as a parameter which contains RAW PCM data directly captured from microphone and returns a file path which contains PCM data followed by appropriate wav header info. Then you can play that file with AVAudioPlayer or AVPlayer. Here is the code...

- (NSURL *) getAndCreatePlayableFileFromPcmData:(NSString *)filePath
{
NSString *wavFileName = [[filePath lastPathComponent] stringByDeletingPathExtension];
NSString *wavFileFullName = [NSString stringWithFormat:@"%@.wav",wavFileName];

[self createFileWithName:wavFileFullName];
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *wavFilePath = [docsDir stringByAppendingPathComponent:wavFileFullName];

NSLog(@"PCM file path : %@",filePath);

FILE *fout;

short NumChannels = AUDIO_CHANNELS_PER_FRAME;
short BitsPerSample = AUDIO_BITS_PER_CHANNEL;
int SamplingRate = AUDIO_SAMPLE_RATE;
int numOfSamples = [[NSData dataWithContentsOfFile:filePath] length];

int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 46 + DataSize;
short audioFormat = 1;

if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}

fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 4, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 4, fout);
fwrite(&DataSize, sizeof(int), 1, fout);

fclose(fout);

NSMutableData *pamdata = [NSMutableData dataWithContentsOfFile:filePath];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
[handle seekToEndOfFile];
[handle writeData:pamdata];
[handle closeFile];

return [NSURL URLWithString:wavFilePath];
}

But that function only works with the following audio settings.

// Audio settings.
#define AUDIO_SAMPLE_RATE 8000
#define AUDIO_FRAMES_PER_PACKET 1
#define AUDIO_CHANNELS_PER_FRAME 1
#define AUDIO_BITS_PER_CHANNEL 16
#define AUDIO_BYTES_PER_PACKET 2
#define AUDIO_BYTES_PER_FRAME 2

PCM to WAV file

peek at the AudioFile and ExtAudioFile APIs (in AudioToolbox).

How to play PCM data/buffer just using AVAudioPlayer or AVPlayer?

I am answering my own question if it helps someone else. After few days of tireless trying, at last i have got it working. Though in a separate way. I have added a wav header info on top of RAW PCM data and saved a file with .wav extension. Then just play the file using AVAudioPlayer or AVPlayer. Here you will get full sample code on How to add playable(such as wav,wmv) header with PCM data/buffer in iOS
ou.

How do I convert flac to wav on iOS?

I was able to implement this using this code for decoding and this code to actually write the WAV header/body.

As an added bonus, this was very helpful in decoding NSData instead of a file.

Here is my finished decoder, though I wouldn't expect it to work in any case except mine.

//
// FlacToWavConverter.m
// SuperpoweredMQAExample
//
// Created by Tony Lawrence on 5/18/17.
// Copyright © 2017 imect. All rights reserved.
//

#import "FlacToWavConverter.h"
#import "avformat.h"
#import "avcodec.h"
#import "avutil.h"
#import "swresample.h"
#import "file.h"

@implementation FlacToWavConverter

+(NSURL*)convertFlacToWav:(NSData*)data {

//const char* input_filename = [filePath UTF8String];
int buffer_size = 16384;

// This call is necessarily done once in your app to initialize
// libavformat to register all the muxers, demuxers and protocols.
av_register_all();

// A media container
AVFormatContext* container = avformat_alloc_context();

//Make a custom IO context so that we can read from memory instead of a file...
unsigned char* iobuffer = av_malloc(buffer_size);
struct buffer_data bd = { 0 };
bd.ptr = (uint8_t*)data.bytes;
bd.size = data.length;
AVIOContext* ioContext = avio_alloc_context(iobuffer, buffer_size, 0, &bd, &read_packet, NULL, NULL);

container->pb = ioContext;

if (avformat_open_input(&container, "arbitrary", NULL, NULL) < 0) {
NSLog(@"Could not open file");
}

if (avformat_find_stream_info(container, NULL) < 0) {
NSLog(@"Could not find file info");
}

int stream_id = -1;

// To find the first audio stream. This process may not be necessary
// if you can gurarantee that the container contains only the desired
// audio stream
int i;
for (i = 0; i < container->nb_streams; i++) {
if (container->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
stream_id = i;
break;
}
}

if (stream_id == -1) {
NSLog(@"Could not find an audio stream");
}

// Extract some metadata
AVDictionary* metadata = container->metadata;

// Find the apropriate codec and open it
AVCodecContext* codec_context = container->streams[stream_id]->codec;

AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);

if (avcodec_open2(codec_context, codec, NULL) < 0) {
NSLog(@"Could not find open the needed codec");
}

NSMutableData *pcmFile = [NSMutableData new];

AVPacket packet;
int8_t buffer[buffer_size];

while (1) {

//buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;

// Read one packet into `packet`
if (av_read_frame(container, &packet) < 0) {
break; // End of stream. Done decoding.
}

// Decodes from `packet` into the buffer
if (avcodec_decode_audio3(codec_context, (int16_t*)buffer, &buffer_size, &packet) < 1) {
break; // Error in decoding
}

// Send the buffer contents to the audio device
[pcmFile appendBytes:buffer length:buffer_size];

}

avformat_close_input(&container);

//fprintf(stdout, "Done playing. Exiting...");

NSURL *file = [FlacToWavConverter getAndCreatePlayableFileFromPcmData:pcmFile];

NSLog(@"Got a playable file maybe? %@", [file absoluteString]);

return file;
}

+(NSURL *) getAndCreatePlayableFileFromPcmData:(NSData *)data
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *wavFilePath = [docsDir stringByAppendingPathComponent:@"output.wav"];

//NSLog(@"PCM data : %@",data);

FILE *fout;

short NumChannels = 2;
short BitsPerSample = 16;
int SamplingRate = 44100;
int numOfSamples = [data length];

int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 46 + DataSize;
short audioFormat = 1;

if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}

fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 4, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 4, fout);
fwrite(&DataSize, sizeof(int), 1, fout);

fclose(fout);

NSMutableData *pamdata = [NSMutableData dataWithData:data];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
[handle seekToEndOfFile];
[handle writeData:pamdata];
[handle closeFile];

NSLog(@"Saved wav: %@", wavFilePath);

return [NSURL URLWithString:wavFilePath];
}

struct buffer_data {
uint8_t *ptr;
size_t size; ///< size left in the buffer
};

static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
struct buffer_data *bd = (struct buffer_data *)opaque;
buf_size = FFMIN(buf_size, bd->size);
//printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
/* copy internal buffer data to buf */
memcpy(buf, bd->ptr, buf_size);
bd->ptr += buf_size;
bd->size -= buf_size;
return buf_size;
}

@end


Related Topics



Leave a reply



Submit