Discuss with you how to use opencore AMR to decode on iOS (solved)

Source: Internet
Author: User

Opencore for iOS was compiled when I was idle two weeks ago. For how to compile opencore for iOS, see this article. Today, I try to use the example library, and I want to use .amr's file decode.wav format. There is a simple example in the test directory to teach you how to use this library, so I wrote a for IOS running in xcode according to the code in it, and the results were disappointing, the size of the converted file is only 4 K.

First, let me talk about my methods.

Create an iOS project, drag the compiled lib and include files to the project, modify the wav. cpp suffix to WAV. Mm, and modify the content as follows:

#import <UIKit/UIkit.h>#include "wav.h"void WavWriter::writeString(const char *str) {fputc(str[0], wav);fputc(str[1], wav);fputc(str[2], wav);fputc(str[3], wav);}void WavWriter::writeInt32(int value) {fputc((value >>  0) & 0xff, wav);fputc((value >>  8) & 0xff, wav);fputc((value >> 16) & 0xff, wav);fputc((value >> 24) & 0xff, wav);}void WavWriter::writeInt16(int value) {fputc((value >> 0) & 0xff, wav);fputc((value >> 8) & 0xff, wav);}void WavWriter::writeHeader(int length) {writeString("RIFF");writeInt32(4 + 8 + 16 + 8 + length);writeString("WAVE");writeString("fmt ");writeInt32(16);int bytesPerFrame = bitsPerSample/8*channels;int bytesPerSec = bytesPerFrame*sampleRate;writeInt16(1);             // FormatwriteInt16(channels);      // ChannelswriteInt32(sampleRate);    // SampleratewriteInt32(bytesPerSec);   // Bytes per secwriteInt16(bytesPerFrame); // Bytes per framewriteInt16(bitsPerSample); // Bits per samplewriteString("data");writeInt32(length);}WavWriter::WavWriter(const char *filename, int sampleRate, int bitsPerSample, int channels) {NSArray *paths               = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentPath       = [paths objectAtIndex:0];NSString *docFilePath        = [documentPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s", filename]];NSLog(@"documentPath=%@", documentPath);wav = fopen([docFilePath cStringUsingEncoding:NSASCIIStringEncoding], "wb");if (wav == NULL)return;dataLength = 0;this->sampleRate = sampleRate;this->bitsPerSample = bitsPerSample;this->channels = channels;writeHeader(dataLength);}WavWriter::~WavWriter() {if (wav == NULL)return;fseek(wav, 0, SEEK_SET);writeHeader(dataLength);fclose(wav);}void WavWriter::writeData(const unsigned char* data, int length) {if (wav == NULL)return;fwrite(data, length, 1, wav);dataLength += length;}

In fact, only one code is modified, which is the first parameter in fopen.

Now that the project is ready, decode is ready. So I wrote a method:

const int sizes[] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0 };- (IBAction)amrnbToWav:(id)sender{NSString * path = [[NSBundle mainBundle] pathForResource:  @"test" ofType: @"amr"]; FILE* in = fopen([path cStringUsingEncoding:NSASCIIStringEncoding], "rb");if (!in) {NSLog(@"open file error");}char header[6];int n = fread(header, 1, 6, in);if (n != 6 || memcmp(header, "#!AMR\n", 6)) {NSLog(@"Bad header");}WavWriter wav("out.wav", 8000, 16, 1);void* amr = Decoder_Interface_init();while (true) {uint8_t buffer[500];/* Read the mode byte */n = fread(buffer, 1, 1, in);if (n <= 0)break;/* Find the packet size */int size = sizes[(buffer[0] >> 3) & 0x0f];if (size <= 0)break;n = fread(buffer + 1, 1, size, in);if (n != size)break;/* Decode the packet */int16_t outbuffer[160];Decoder_Interface_Decode(amr, buffer, outbuffer, 0);/* Convert to little endian and write to wav */uint8_t littleendian[320];uint8_t* ptr = littleendian;for (int i = 0; i < 160; i++) {*ptr++ = (outbuffer[i] >> 0) & 0xff;*ptr++ = (outbuffer[i] >> 8) & 0xff;}wav.writeData(littleendian, 320);}fclose(in);Decoder_Interface_exit(amr);}

Be sure to introduce the corresponding header file.

#import "wav.h"#import "interf_dec.h"#import "dec_if.h"#import "interf_enc.h"

Ah, I thought it would be successful, but the result could not be converted. Does hero know why ???

At first I thought it was a WAV header problem, so I went to study the wav header. Looking at this figure, I found that there was no error in the wavwriter class. Later, I thought it was a problem for iOS compilation, So I compiled a for Mac version directly on the Mac, and then created a Mac project test, get the same result as on IOS. The final conclusion is that the usage is incorrect. How to use it? There are too few materials on the Internet. Have you ever used it?

I took the image in WAV Header Format and listened to ruxun for good things.

You are welcome to discuss it together.

I have uploaded the project to the online storage. If you have time, I will help you study it.

Http://115.com/file/bhiqw3xd #
Amrdemoforios.zip

On cocoachina asked Gao Ren, ask me to see: http://www.cublog.cn/u3/112227/showart_2233739.html

According to the samples, the two differences are that when the frame size is calculated, the array is different, but the official sample is not available. It is really depressing. The official array members are less than 20, does anyone know why it is officially written? I can't figure it out, but this time it was converted into a WAV file. Haha, after lunch, I came back to compare the code and found that it was caused by a wrong frame in my AMR file. Therefore, I should discard the wrong frame when reading the wrong frame, however, processing cannot end, because there are still correct frames. So I modified the conversion function as follows:

-(Ibaction) amrnbtowav :( ID) sender {nsstring * Path = [[nsbundle mainbundle] pathforresource: @ "test" oftype: @ "Amr"]; file * In = fopen ([path cstringusingencoding: nsasciistringencoding], "rb"); If (! In) {nslog (@ "Open File error");} Char header [6]; int n = fread (header, 1, 6, in); If (n! = 6 | memcmp (header ,"#! Amr \ n ", 6) {nslog (@" Bad Header ");} wavwriter WAV (" out.wav ", 8000, 16, 1 ); void * AMR = decoder_interface_init (); int frame = 0; unsigned char stdframeheader; while (true) {uint8_t buffer [500];/* read the mode byte */unsigned char frameheader; int size; int index; If (frame = 0) {n = fread (& stdframeheader, 1, sizeof (unsigned char), In); Index = (stdframeheader> 3) & 0x0f;} else {While (1) // discard the error frame and process the correct frame {n = fr EAD (& frameheader, 1, sizeof (unsigned char), In); If (feof (in) return; If (frameheader = stdframeheader) break ;} index = (frameheader> 3) & 0x0f;} If (n <= 0) break;/* Find the packet size */size = sizes [Index]; if (size <= 0) break; n = fread (buffer + 1, 1, size, in); If (n! = Size) break; frame ++;/* decode the packet */int16_t outbuffer [1, 160]; decoder_interface_decode (AMR, buffer, outbuffer, 0 ); /* convert to little endian and write to WAV */uint8_t littleendian [320]; uint8_t * PTR = littleendian; For (INT I = 0; I <160; I ++) {* PTR ++ = (outbuffer [I]> 0) & 0xff; * PTR ++ = (outbuffer [I]> 8) & 0xff;} WAV. writedata (littleendian, 320);} nslog (@ "frame = % d", frame); fclose (in); decoder_interface_exit (AMR );}

Now you can convert the AMR file to a WAV file. The official array is optimized !!

Download the demo project. Some netizens reported to me that the conversion of the first button in the demo could not be successful, and the conversion of the second button could be successful. After I run it, this problem exists. I wrote the conversion of the first button according to the sample of opencore Amr. I didn't care too much at the time, because I tested the conversion of the second button successfully, so I didn't continue to test the first button. I studied it today and found that the main reason is that the wav header is not processed. The standard WAV header has some fixed-section alignment issues. You only need to set the writeheader (int
Length.

Void wavwriter: writeheader (INT length) {writestring ("riff"); writeint32 (4 + 8 + 20 + 8 + length ); // change 16 to 20 writestring ("wave"); writestring ("FMT"); writeint32 (20); int bytesperframe = bitspersample/8 * channels; int bytespersec = bytesperframe * samplerate; writeint16 (1); // formatwriteint16 (channels); // channelswriteint32 (samplerate); // sampleratewriteint32 (bytespersec ); // bytes per secwriteint16 (bytesperframe); // bytes per framewriteint16 (bitspersample); // bits per samplewriteint32 (0); // bytes must be aligned here nexsizewritestring ("data "); writeint32 (length );}

Now we can make a successful transfer. The FMT chunk standard of the wav header is 24 bytes, but it requires 28 bytes. I wonder if it is caused by byte alignment.

Download the complete Demo project.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.