与大家讨论如何用opencore amr在iOS上decode(已解决)

2023-11-18

两周前空闲的时候编译了opencore for iOS, 如何编译的请参看这一篇文章。今天又有空,所以就试着去用了一下这个库,我想把.amr的文件decode为.wav格式的。在test目录下有简单的例子,教大家如何用这个库,于是我就照着里面的代码,写了一个for iOS在xcode里跑,结果大失所望, 转化出来的文件只有4K大小。

首先我说说我的方法。

新建了一个iOS的工程,然后把编译好的lib与include文件拖到工程里,然后修改wav.cpp后缀为wav.mm,并修改它的内容如下:

#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);             // Format
	writeInt16(channels);      // Channels
	writeInt32(sampleRate);    // Samplerate
	writeInt32(bytesPerSec);   // Bytes per sec
	writeInt16(bytesPerFrame); // Bytes per frame
	writeInt16(bitsPerSample); // Bits per sample

	writeString("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;
}

其实只修改了一处代码,就是fopen时的第一个参数。


准备工程都做好了,现在开始decode了,于是我写了一个方法:

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);
}

注意要引入相应的头文件

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

哎,以为会成功,结果转化不成功啊。有没有大侠知道原因???

最开始我以为是wav头的问题,于是去研究了一下wav头,参看这张图,结果发现WavWriter这个类并没有错。后来,我以为是for iOS编译的问题,于是我直接在mac上编译了一份for mac的版本,然后建了一个mac工程测试,得到与ios上一样的结果。 最后得出结论,可能是用法错了。如何用呢,网上的资料太少了,有没有大侠用过的呀?

我把WAV头格式的图拿了过来,好东西要听鲁讯的,拿来主义,呵呵。



欢迎大家一起讨论。

我已把工程上传网盘,有空的人帮着研究一下。

http://115.com/file/bhiqw3xd#
amrDemoForiOS.zip

在cocoachina上问了高人,叫我参看:http://www.cublog.cn/u3/112227/showart_2233739.html

照着http://www.cublog.cn/u3/112227/showart_2233739.html的代码用了一下,这回正确了, 两个不同之处就是计算frame大小的时候那个数组不同,官方的sample却不能用,真是郁闷呀,官方的那个数组成员都小于20,有人知道为何官方那么写吗?想不通,不过这回成功转化为wav文件了。呵呵,吃了午饭,回来比较了一下代码,发现是因为我的amr文件有错误帧造成的,所以在读取的时候遇到错误帧的时候要丢弃错误帧,但不能结束处理,因为后面还有正确帧。所以我修改了一下转化函数如下:

- (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)   //丢弃错误帧,处理正确帧
			{
				n = fread(&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[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);
}


这下就可以正常转amr为wav文件了。官方的那个数组是优化过的!!

请下载Demo Project 。有网友向我反映该Demo第一个button的转化不能成功,第二个button的转化能成功。我运行了一下,的确有这个问题。第一个button的转化我是参考opencore amr的sample写的,当时没有太在意,因为我测试第二button的转化成功了,于是就没有接着测第一个。今天研究了一下,发现主要原因是由对wav头没处理。标准的wav头会有一些定节对齐的问题,只需要将wav.mm里writeHeader(int length)函数修改如下就行了。

void WavWriter::writeHeader(int length) {
	writeString("RIFF");
	writeInt32(4 + 8 + 20 + 8 + length);  //将16改为20
	writeString("WAVE");

	writeString("fmt ");
	writeInt32(20);

	int bytesPerFrame = bitsPerSample/8*channels;
	int bytesPerSec = bytesPerFrame*sampleRate;
	writeInt16(1);             // Format
	writeInt16(channels);      // Channels
	writeInt32(sampleRate);    // Samplerate
	writeInt32(bytesPerSec);   // Bytes per sec
	writeInt16(bytesPerFrame); // Bytes per frame
	writeInt16(bitsPerSample); // Bits per sample
	
	writeInt32(0);             //这儿需要字节对齐  nExSize

	writeString("data");
	writeInt32(length);
}

现在就可以成功转化了,大功告成。wav头FMT chunk标准是24字节,这儿却需要28字节,不知道是不是字节对齐的原因造成的。

请下载完整Demo project。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与大家讨论如何用opencore amr在iOS上decode(已解决) 的相关文章

  • 线程 1:信号 SIGABRT - AppDelegate.h

    main m Journey Created by Julian Buscema on 2014 07 13 Copyright c 2014 Julian Buscema All rights reserved import
  • Objective C UIImagePNGRepresentation内存问题(使用ARC)

    我有一个基于 ARC 的应用程序 它从 Web 服务加载大约 2 000 个相当大 1 4MB 的 Base64 编码图像 它将 Base64 解码后的字符串转换为 png图像文件并将其保存到磁盘 这一切都是在一个循环中完成的 我不应该有任
  • 我什么时候应该对 IBOutlet 使用弱或强限定符? [复制]

    这个问题在这里已经有答案了 可能的重复 ARC 下 IBOutlets 应该强还是弱 https stackoverflow com questions 7678469 should iboutlets be strong or weak
  • C# 中的接口继承

    我试图解决我在编写应用程序时遇到的相当大的 对我来说 问题 请看这个 为了简单起见 我将尝试缩短代码 我有一个名为的根接口IRepository
  • 退出导航控制器

    我试图离开初始视图控制器 并进入空白视图控制器 这很好 但是这会使空白视图控制器也成为导航控制器的一部分 这不是我想要的 我想脱离视图控制器 在视图控制器中 我尝试退出 它会自行弹出 当我尝试视图中的方法时 将出现目标视图控制器 self
  • 显示键盘时如何在 TextView 下方添加更多填充

    当我在 ScrollView 中有 TextField 并点击它时 键盘会按预期显示 但似乎 TextField 已向上移动到足以显示输入区域 但我希望移动到足够的位置 以便整体可见 否则它看起来像是被剪裁了的 我找不到改变这种行为的方法
  • 在 UIMenuItem 上设置accessibilityLabel

    我正在尝试设置accessibilityLabel of a UIMenuItem而且似乎没有效果 无论如何 VoiceOver 只是读取项目的标题 let foo UIMenuItem title foo action selector
  • ResponseSerializer“无法使用 Swift 3 调用非函数类型“NSHTTPURLResponse”的值?

    我一直在使用以下代码 没有出现任何问题 直到更新到 Xcode 8 beta 6 它类似于这个例子 https github com Alamofire Alamofire generic response object serializa
  • 设置 TableView setEditing 时无法选择 UITableViewCell

    我希望能够选择多行 如下所示的默认邮件应用程序 我有一个名为编辑的按钮 可以调用 self myTableView setEditing YES animated YES 编辑按钮成功显示单元格左侧的圆圈 如上所示的邮件应用程序 但是 当我
  • SpriteKit 碰撞检测中 SKSpriteNode 之间的间隙

    我已经尝试解决这个问题很长一段时间了 我有一个具有简单平台物理原理的游戏 其中玩家跌倒在一个方块上 这可以阻止他跌倒 这是可行的 但是玩家停止的位置和实际对象 精灵节点的位置之间存在明显的差距 这是一个屏幕截图 它应该是不言自明的 clas
  • Ios Swift制作字体切换粗体、斜体、boldItalic、正常而不改变其他属性

    我很惊讶 在 Swift 中简单地为现有字体设置粗体和斜体是如此复杂 我只是想通过在字体类上使用以下方法来简化事情 我希望将以下方法添加到已设置字体系列和字体大小的现有字体中 我需要保留这些并仅更改以下内容 setBold Shud 保留斜
  • ARC 可以与 Core Graphics 对象一起使用吗?

    我最近开始了一个使用自动引用计数 ARC 的新项目 当我分配 CALayer 的内容时 UIView view UIImage image view layer contents image CGImage 我收到一个错误 ARC 不允许将
  • 在 cocoa touch 中以编程方式将视图位置设置为右上角

    我需要确保一个视图 A 尺寸 200x200 始终与第二个视图 B 全屏尺寸 内的右上角对齐 我想确保无论设备方向如何 视图 A 都保留在该位置 事实是 当使用界面生成器来定位视图时 我对此没有任何问题 但我需要以编程方式构建它 我想我应该
  • iPhone UI 带有 Tableview 或 Scrollview? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 正确的标头 php mysql blob 显示图像

    我正在尝试在我的 PHP 页面中显示来自 mysql blob 的图像 我知道这不是最佳实践 然后我会将其引入我的 iOS 应用程序中 我在设置页面标题时遇到问题 我认为需要将其设置为图像 所以 这显示了图像 但我不相信页眉是正确的 hea
  • iOS Safari 通过单击按钮触发扫描信用卡

    您好 我目前正在创建一个测试应用程序 当用户单击文本字段名称或卡号时 扫描信用卡功能对我有用 我的问题是 我希望当用户单击 button1 时发生同样的情况 这应该打开相机来扫描卡并填充现有的文本字段 即名称 卡号和到期日期 额外的好处是
  • APNS(Apple 推送通知服务器)的反馈服务

    我们正在使用Java作为推送通知提供商APNS I我能够将消息发送到APNS但我不知道如何获得该消息的反馈 请帮忙 反馈服务具有类似于用于发送推送通知的接口的二进制接口 您可以通过以下方式访问生产反馈服务feedback push appl
  • iTunes Connect 中缺少应用内购买部分

    我有一个应用程序处于准备提交状态 我还有该应用程序的应用程序内购买项目 上周我将它们链接到我的应用程序并提交以供审核 周末 开发商拒绝了它 现在我想重新提交修复后的版本 但 IAP 部分完全丢失 我怎样才能让它再次可见 我遇到过同样的问题
  • 一旦 webapp 添加到主屏幕,是否可以强制 iphone/ipod 更新 apple-touch-icon?

    我使用 safari 的所有推荐链接和元标记创建了一个网络应用程序 例如
  • 在 PDFView 表单字段中配置键盘设置

    我正在开发一个应用程序 它从服务器加载 PDF 文件并在 PDFView 中显示这些 PDF 文件 这些文件包含用户要在其中键入内容的表单字段 这很好用 PDF 文件将用于教育环境 其中拼写应not可以自动更正并且预测文本应该not能得到的

随机推荐

  • substance designer中的warp节点分析

    一直觉得warp挺适用 但是用起来有时候效果挺奇怪的 还有directional warp貌似两个产生的效果截然不同 于是尝试用u3d 材质实现下 directional warp 这个节点主要功能是用灰度信息推开像素 常常用来分割连续贴图
  • Ubuntu安装配置tftp服务器

    Ubuntu安装配置tftp服务器 实验环境 ubuntu 1604 x64 一 安装配置 建立tftp目录 sudo mkdir tftpboot sudo chmod 777 tftpboot 安装客户端和服务器 sudo apt ge
  • 大数据量的冒泡排序 (计次数)

    题目描述 给定一个包含从0到n 1各一次的数组 若使用冒泡排序将其排为升序 问其中需要进行多少次交换 输入 测试数据有多组 每组由两行组成 第一行包含正整数n n lt 5000 下一行包含从0到n 1的n个整数的序列 输出 对于每组测试数
  • [论文分享] Adversarial Training for Raw-Binary Malware Classifiers

    Adversarial Training for Raw Binary Malware Classifiers USENIX 2023 Keane Lucas Carnegie Mellon University Samruddhi Pai
  • 面试题61. 扑克牌中的顺子(java+python)

    从若干副扑克牌中随机抽 5 张牌 判断是不是一个顺子 即这5张牌是不是连续的 2 10为数字本身 A为1 J为11 Q为12 K为13 而大 小王为 0 可以看成任意数字 A 不能视为 14 示例 1 输入 1 2 3 4 5 输出 Tru
  • hex文件格式学习记录

    hex文件 hex文件是什么 hex文件的数据结构 按照记录类型具体分析 Mermaid Flowchart hex文件是什么 它是由一行行符合Intel HEX 文件格式的文本所构成的ASCII 文本文件 每一行包含一 个 HEX 记录
  • Java实验三 基于GUI的网络通信程序设计【代码构建逻辑】【双向通信】【超多细节优化!!】

    写在前面 这次实验代码的构建主要是更加熟练的运用socket网络编程 文件输入输出流 GUI设计 容器的使用 多线程的运用等等多方面的知识 是综合类题型 做完受益身心的类型 题目如下 编写程序完成以下功能 1 设计一个基于GUI的客户 服务
  • iOS的几个特效实现思路

    最近看一个app的源码 发现基本没有用第三方的开源组件 但是特效也做得不错 总结一下实现的思路 简单的抽屉效果 效果如图 这种抽屉效果很常见 开源组件也很多 但是一般开源组件都对Controller的结构有要求 有时候不是很方便 原理主要是
  • qmake手册(Qt5.9.3)

    qmake手册 qmake手册 概观 描述一个项目 建立一个项目 使用第三方库 预编译头文件 入门 从简单的开始 使应用程序可调试 添加平台特定的源文件 如果文件不存在停止qmake 检查多个条件 创建项目文件 项目文件元素 变量 注释 内
  • 小程序报错:Unexpected end of JSON input

    报错原因 跳转页面传参内包含英文 let data aaa 你学会了吗 由于参数内携带英文 所以报错 wx navigateTo url home home data encodeURI JSON stringify data 解决方法 先
  • ECCV20 - OCRNet:聚合对象上下文特征用于语义分割《Object-Contextual Representations for Semantic Segmentation》

    文章目录 原文地址 论文阅读方法 初识 相知 主要技术 部分实验 回顾 原文地址 原文 论文阅读方法 三遍论文法 初识 对于语义分割这类密集预测任务 上下文特征是非常重要的信息 在早期的一些工作中 主要着重于捕获多尺度特征 空间维度 比如P
  • Golang学习笔记 结构体和指针

    Golang是一门很特殊的语言 虽然它出生比较晚 但是在很多地方却和现在的编程语言有所不同 现在的编程语言要么是函数式的 要么是面向对象的 而Go语言却有指针 结构体这些概念 并解决了C语言的一些坑 从这个角度上说 Golang可以看做C语
  • 【mysql】-【innodb数据存储结构】

    文章目录 数据库的存储结构 页 磁盘与内存交互基本单位 页 页结构概述 页的大小 页的上层结构 页的内部结构 File Header 文件头部 和File Trailer 文件尾部 File Header 文件头部 38字节 File Tr
  • Vue3-导出excel表格

    安装xlsx和file saver yarn add file saver xlsx 或 npm install file saver S npm install xlsx S 页面引入xlsx和file saver import as X
  • 不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事...

    文章授权转载自视觉志 版权归原作者所有 作者 不一 2009年 19岁的孙玲和朋友一起 坐上了前往深圳的绿皮火车 经过14个小时的疲倦车程 她有了一个全新的身份 工厂车间 流水线上的一名女工 那个时候 如果有人告诉孙玲 你以后会成为一名优秀
  • 无限创建gmail邮箱账号

    文章目录 说明 准备工作 操作方法 1 用户名之间加 2 用户名后面加 3 把后缀变为http googlemail com 说明 国外很多网站都需要gmail邮箱注册 如果能有很多gmail账号 那么就能做很多事 自己体会哈 注册地址 h
  • Python 模块与包

    1 模块 一个模块就是一个包含python代码的文件 后缀名称是 py就可以 模块就是个python文件 为什么我们用模块 程序太大 编写维护非常不方便 需要拆分 模块可以增加代码重复利用的方法 当作命名空间使用 避免命名冲突 如何定义模块
  • Bluetooth(HC)与STM32的连接通讯(在手机端通过蓝牙控制STM32板子小灯)

    一 说明 写本文的目的是抛弃一些比较专业的描述 从一些刚接触的小白角度出发 帮助此类朋友解决一些问题 所以内容都是用比较通俗的白话来写 网上已经有许多的比较专业的描述教程 若你想要一些比较专业性的描述 本人建议你看其他朋友的 二 概论 本文
  • 云孚科技助力第三届中国情感计算大会(CCAC 2023)胜利召开

    中国情感计算大会CCAC 2023年6月30日 7月2日 由中国中文信息学会情感计算专委会主办 西安交通大学承办的第三届中国情感计算大会 The Third Chinese Conference on Affective Computing
  • 与大家讨论如何用opencore amr在iOS上decode(已解决)

    两周前空闲的时候编译了opencore for iOS 如何编译的请参看这一篇文章 今天又有空 所以就试着去用了一下这个库 我想把 amr的文件decode为 wav格式的 在test目录下有简单的例子 教大家如何用这个库 于是我就照着里面