iOS 画音频波形曲线 根据音频数据版
效果图
DrawView.h
- #import <UIKit/UIKit.h>
- @interface DrawView : UIView
- @property shortshort *drawBuffer;
- @property int dataLen;
- @property floatfloat *outRel;
- @property floatfloat *outImg;
- @property int bias;
- @property int wSize;
- - (void)genKernel;
- @end
DrawView.m
- #import "DrawView.h"
- @implementation DrawView
- #define KSIZE 20
- #define BIAS 10000
- static double fk[KSIZE] = {0};
- static double _filterData[2048];
- - (void)genKernel
- {
- double fc = .05;
- for (int i = 0; i < KSIZE; i++)
- {
- if ((i - KSIZE/2) == 0)fk[i] = 22 * M_PI *fc;
- if ((i - KSIZE/2) != 0 )fk[i] = sin(22 * M_PI * fc * (i - KSIZE/2))/(i - KSIZE/2);
- fk[i] = fk[i] * (0.54 - 0.46 * cos(22 * M_PI * i / KSIZE ));
- }
- double sum = 0;
- for (int m = 0; m < KSIZE; m++)
- sum+=fk[m];
- for (int n = 0; n < KSIZE; n++)
- fk[n]/=sum;
- }
- - (void)improveSpectrum
- {
- memset(_filterData, 0x0, sizeof(double) * 1024);
- short transData[(int)self.wSize];
- memcpy(transData, self.drawBuffer+_bias, _wSize * sizeof(short));
- for (int i = 0; i < _wSize; i++)
- {
- for (int j = 0; j < KSIZE; j++)
- {
- _filterData[i] = _filterData[i] + transData[ i - j] * fk[j];
- }
- }
- }
- - (id)initWithFrame:(CGRect)frame
- {
- self = [super initWithFrame:frame];
- if (self) {
- }
- return self;
- }
- - (void)drawRect:(CGRect)rect
- {
- float delta = 320. / _wSize;
- [self improveSpectrum];
- [[UIColor grayColor] set];
- UIRectFill ([self bounds]);
- CGContextRef currentContext = UIGraphicsGetCurrentContext();
- CGContextBeginPath(currentContext);
- CGContextMoveToPoint(currentContext, 0., 230.);
- CGContextAddLineToPoint(currentContext, 320., 230.);
- [[UIColor blueColor] setStroke];
- CGContextStrokePath(currentContext);
- CGContextBeginPath(currentContext);
- CGContextMoveToPoint(currentContext, 0., 230.);
- for (int i = 0; i < _wSize; i++)
- {
- CGFloat x = i * delta;
- CGFloat y = _filterData[i] / 150.0 + 230.0;
- CGContextAddLineToPoint(currentContext, x, y);
- }
- [[UIColor redColor] setStroke];
- CGContextStrokePath(currentContext);
- }
- @end
ViewController.h
- #import <UIKit/UIKit.h>
- @interface ViewController : UIViewController
- @end
ViewController.m
- #import "ViewController.h"
- #import "DrawView.h"
- struct WavInfo
- {
- int size;
- char *data;
- short channels;
- short block_align;
- short bits_per_sample;
- unsigned long sample_rate;
- unsigned long format_length;
- unsigned long format_tag;
- unsigned long avg_bytes_sec;
- };
- @interface ViewController ()
- @end
- void decodeWaveInfo(const charchar *fname, struct WavInfo *info)
- {
- FILEFILE *fp;
- fp = fopen(fname, "rb");
- if(fp)
- {
- char id[5];
- unsigned long dataSize,size;
- fread(id, sizeof(char), 4, fp);
- id[4]='\0';
- if (!strcmp(id, "RIFF"))
- {
- fread(&size, sizeof(unsigned long), 1, fp);//read file size
- fread(id, sizeof(char), 4, fp);//read wave
- id[4]='\0';
- if (!strcmp(id, "WAVE"))
- {
- fread(id, sizeof(char), 4, fp);
- fread(&info->format_length, sizeof(unsigned long), 1, fp);
- fread(&info->format_tag, sizeof(short), 1, fp);
- fread(&info->channels, sizeof(short), 1, fp);
- fread(&info->sample_rate, sizeof(unsigned long), 1, fp);
- fread(&info->avg_bytes_sec, sizeof(unsigned long), 1, fp);
- fread(&info->block_align, sizeof(short), 1, fp);
- fread(&info->bits_per_sample, sizeof(short), 1, fp);
- fread(id, sizeof(char), 4, fp);
- fread(&dataSize, sizeof(unsigned long), 1, fp);
- info->size = dataSize;
- info->data = ( charchar *)malloc(sizeof(char)*dataSize);
- fread(info->data, sizeof(char), dataSize, fp);
- }
- else
- {
- printf("Error\n");
- }
- }
- else
- {
- printf("Error\n");
- }
- fclose(fp);
- }
- }
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- NSString *path = [[NSBundle mainBundle] pathForResource:@"effect1" ofType:@"wav"];
- struct WavInfo wavInfo;
- decodeWaveInfo([path UTF8String], &wavInfo);
- DrawView *d = (DrawView *)self.view;
- d.drawBuffer = (shortshort *)malloc(sizeof(short) * wavInfo.size / 2 );
- [d genKernel];
- d.dataLen = wavInfo.size / 2;
- d.wSize = 256;
- d.bias = 0;
- int n = 0;
- for (int m = 0; m < wavInfo.size / 2; m++)
- {
- d.drawBuffer[n++] = wavInfo.data[m * 2 + 1] << 8 | wavInfo.data[m * 2];
- }
- UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragView:)];
- [self.view addGestureRecognizer:pan];
- UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchView:)];
- [self.view addGestureRecognizer:pinch];
- }
- #pragma mark -
- #pragma mark Gesture Recognizer callback
- - (void)dragView:(UIPanGestureRecognizer *)recognizer
- {
- DrawView *d = (DrawView *)self.view;
- CGPoint p = [recognizer translationInView:self.view];
- NSLog(@"translate point is : %@",NSStringFromCGPoint(p));
- d.bias -= p.x;
- [self.view setNeedsDisplay];
- }
- - (void)pinchView:(UIPinchGestureRecognizer *)recognizer
- {
- DrawView *d = (DrawView *)self.view;
- if (recognizer.scale > 1.0)
- {
- if (d.wSize > 128)
- {
- d.wSize *= 0.95;
- }
- }
- else
- {
- if (d.wSize < 1024)
- {
- d.wSize *= 1.05;
- }
- }
- [self.view setNeedsDisplay];
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- @end
代码下载:http://pan.baidu.com/s/1ACWXT
参考:
在 iPhone
应用或者是游戏的开发过程中,对声音的支持是必不可少的。在我做过的几个应用中,每个都涉及到音效,所以在这里做个简单的归纳,很多都是引用自
《iPhone Application Programming Guide》(需要有 Apple ID 才能打开链接),加了一些实际使用的经验。
iPhone OS 主要提供以下了几种播放音频的方法:
System Sound Services
AVAudioPlayer 类
Audio Queue Services
OpenAL
1. System Sound Services
System Sound Services 是最底层也是最简单的声音播放服务,调用 AudioServicesPlaySystemSound 这个方法就可以播放一些简单的音频文件,使用此方法只适合播放一些很小的提示或者警告音,因为它有很多限制:
■ 声音长度要小于 30 秒
■ In linear PCM 或者 IMA4 (IMA/ADPCM) 格式的
■ 打包成 .caf, .aif, 或者 .wav 的文件
■ 不能控制播放的进度
■ 调用方法后立即播放声音
■ 没有循环播放和立体声控制
另外,它还可以调用系统的震动功能,方法也很简单。具体的代码可以参考官方的示例 SysSound
,
但是官方的示例只有一些简单的用法,从文档中我们发现可以通过 AudioServicesAddSystemSoundCompletion
方法为音频播放添加 CallBack 函数,有了 CallBack 函数我们可以解决不少问题,比如可以克服 System Sound
Services 本身不支持循环播放的问题。以下代码可以实现一个在程序中循环播放的背景音乐:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
static void completionCallback (SystemSoundID mySSID) { // Play again after sound play completion AudioServicesPlaySystemSound(mySSID);}- (void) playSound { // Get the main bundle for the app CFBundleRef mainBundle; SystemSoundID soundFileObject; mainBundle = CFBundleGetMainBundle (); // Get the URL to the sound file to play CFURLRef soundFileURLRef = CFBundleCopyResourceURL ( mainBundle, CFSTR ("background"), CFSTR ("wav"), NULL ); // Create a system sound object representing the sound file AudioServicesCreateSystemSoundID ( soundFileURLRef, &soundFileObject ); // Add sound completion callback AudioServicesAddSystemSoundCompletion (soundFileObject, NULL, NULL, completionCallback, (void*) self); // Play the audio AudioServicesPlaySystemSound(soundFileObject); } |
2. AVAudioPlayer 类
AVAudioPlayer 是 AVFoundation.framework
中定义的一个类,所以使用要先在工程中引入 AVFoundation.framework。我们可以把 AVAudioPlayer
看作是一个高级的播放器,它支持广泛的音频格式,主要是以下这些格式:
■ AAC
■ AMR(AdaptiveMulti-Rate, aformatforspeech)
■ ALAC(AppleLossless)
■ iLBC(internetLowBitrateCodec, anotherformatforspeech)
■ IMA4(IMA/ADPCM)
■ linearPCM(uncompressed)
■ μ-lawanda-law
■ MP3(MPEG-1audiolayer3
AVAudioPlayer 可以播放任意长度的音频文件、支持循环播放、可以同步播放多个音频文件、控制播放进度以及从音频文件的任意一点开始播放等,更高级的功能可以参考 AVAudioPlayer 的文档。要使用 AVAudioPlayer 的对象播放文件,你只需为其指定一个音频文件并设定一个实现了 AVAudioPlayerDelegate 协议的 delegate 对象。这里举一个简单的例子,和上一个例子一样,实现一直循环播放的背景音乐:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
- (void) playBackgroundSoundEffect { NSString *soundFilePath = [[NSBundle mainBundle] pathForResource: @"background" ofType: @"wav"]; NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath]; AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: nil]; [fileURL release]; self.player = newPlayer; [newPlayer release]; [self.player prepareToPlay]; [self.player setDelegate: self]; self.player.numberOfLoops = -1; // Loop playback until invoke stop method [self.player play];} |
可以看到,只要将 AVAudioPlayer 的 numberOfLoops 属性设为负数,音频文件就会一直循环播放直到调用 stop 方法。
AVAudioPlayer 同样支持 Callback,这是 AVAudioPlayerDelegate 的一个可选 delegate 方法:
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player successfully: (BOOL) flag {
if (player == self.player && flag == YES) {
NSLog(@"Playback finish.");
}
}
另外,你可以随时控制 AVAudioPlayer 对象的播放、暂停以及停止,通过判断对象的状态,分别调用 play、pause 和 stop 方法即可:
- (IBAction) playOrPause: (id) sender {
// if playing, pause
if (self.player.playing) {
[self.player pause];
// if stopped or paused, start playing
} else {
[self.player play];
}
虽
然 AVAudioPlayer 可以播放很多格式,但是我们在实际开发过程中还是最好使用一些没有压缩的格式,比如 WAVE
文件,这样可以减少系统处理单元的资源占用,以便更好的完成程序的其他功能。另外,在使用 AVAudioPlayer 连续播放 mp3
这类经过压缩的音频文件时,在连接处可能出现一定的间隔时间。
3. Audio Queue Services
如果以上两种音频播放的解决方案都无法满足你的需求,那么我想你肯定需要使用 Audio
Queue Services。使用 Audio Queue Services
对音频进行播放,你可以完全实现对声音的控制。例如,你可以在声音数据从文件读到内存缓冲区后对声音进行一定处理再进行播放,从而实现对音频的快速/慢速
播放的功能。
因为 Audio Queue Services 相对复杂很多,Apple 官方已经把它整理为一本书了,具体可以参考 Audio Queue Services Programming Guide 和 SpeakHere的程序示例。
4. OpenAL
OpenAL 是一套跨平台的开源的音频处理接口,与图形处理的 OpenGL 类似,它为音频播放提供了一套更加优化的方案。它最适合开发游戏的音效,用法也与其他平台下相同。
iPhone 支持 OpenAL 1.1,我没有在实际开发中使用过,具体的文档可以参考 OpenAL 的网站 http://openal.org和 oalTouch 的程序示例。
iOS 画音频波形曲线 根据音频数据版的更多相关文章
- 调用CImg库显示WAV格式音频波形
最近在做傅里叶变换和小波变换时经常要通过显示波形来检验算法,但通过visual studio之类显示波形又显得麻烦,而且不能跨平台. CImg是一个跨平台的C++的图像处理库,提供的图像处理等功能十分 ...
- egret 篇——关于ios环境下微信浏览器的音频自动播放问题
前段时间公司突然想用egret(白鹭引擎)做一个金币游戏,大半个月边看文档边写吭哧吭哧也总算是弄完了.期间遇到一个问题,那就是ios环境下微信浏览器的音频自动播放问题. 个人感觉吧,egret自己封装 ...
- 常用音频协议介绍&&有关音频编码的知识与技术参数
(转载)常用音频协议介绍 会议电视常用音频协议介绍及对比白皮书 一.数字化音频原理:声音其实是一种能量波,因此也有频率和振幅的特征,频率对应于时间轴线,振幅对应于电平轴线.通常人耳可以听到的频率在20 ...
- C# NAudio录音和播放音频文件-实时绘制音频波形图(从音频流数据获取,而非设备获取)
NAudio的录音和播放录音都有对应的类,我在使用Wav格式进行录音和播放录音时使用的类时WaveIn和WaveOut,这两个类是对功能的回调和一些事件触发. 在WaveIn和WaveOut之外还有对 ...
- IOS Animation-贝塞尔曲线与Layer简单篇(一)
IOS Animation-贝塞尔曲线与Layer简单篇 swift篇 1.介绍 贝塞尔曲线: 贝塞尔曲线是计算机图形图像造型的基本工具,是图形造型运用得最多的基本线条之一.它通过控制曲线上的四个点( ...
- ios怎样实现快速将显卡中数据读出压缩成视频在cocos2dx扩展开发中
如果解决ios怎样实现快速将显卡中数据读出压缩成视频在cocos2dx扩展开发中 手机平台性能是个关键问题. 压缩视频分成3个步骤: 读取显卡数据, 使用编码器压缩,保存文件. 使用libav 压缩的 ...
- ggplot2在一幅图上画两条曲线
ggplot2在一幅图上画两条曲线 print(data)后的结果是 C BROWN.P MI.P 0 0.9216 0.9282 30 0.9240 0.9282 100 0.9255 0.9282 ...
- HMS Core音频编辑服务3D音频技术,助力打造沉浸式听觉盛宴
2022年6月28日,HDD·HMS Core.Sparkle影音娱乐沙龙在线上与开发者们见面.HMS Core音频编辑服务(Audio Editor Kit)专家为大家详细分享了基于分离的3D音乐创 ...
- 现有新的iOS更新可用,请从iOS12 beta版进行更新.解决方案
问题描述: ios系统一直弹出“现有新的iOS更新可用,请从iOS12 beta版进行更新”的提示,很烦的. 应该只出现在安装测试版ios12的手机上. 解决方案: 删除描述文件无法解决. 有网友机制 ...
随机推荐
- .net中Web.config文件的基本原理及相关设置
11.7 使用web.config配置文件 Web配置文件web.config是Web 应用程序的数据设定文件,它是一份 XML 文件,内含 Web 应用程序相关设定的 XML 标记,可以用来简化 ...
- c - 水仙花数.
#include <stdio.h> #include <math.h> /* *打印出所有的“水仙花数” ,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身. * ...
- 解压版mysql安装--windows系统
1 解压到某个目录 2 配置配置文件 3 执行命令:安装目录/bin/mysqld --install mysql5.6 --defaults-file=指定配置文件位置 "安装目录/bin ...
- C#之重定向输入输出
当我们写完程序,想要在另一个平台上跑我们所写的程序的时候,就需要用到重定向输入输出. 重定向有两中方式,即同步和异步. 下面来讲讲同步 代码: Process process = new Proces ...
- [转]C++智能指针的创建
zero 坐在餐桌前,机械的重复“夹菜 -> 咀嚼 -> 吞咽”的动作序列,脸上用无形的大字写着:我心不在焉.在他的对面坐着 Solmyr ,慢条斯理的吃着他那份午餐,维持着他一贯很有修养 ...
- Linux的/etc/issue、/etc/issue.net和/etc/motd的区别
Linux使用这三个文件/etc/issue./etc/issue.net和/etc/motd 来控制本地及远程登录前后的信息显示,网上很多相互转载,说的都不清楚,自己实际测试了一下,结果记录如下: ...
- Thinkphp 模版
1.显示模版 在Home/Controller/MainController.class.php中写一个方法来显示对应的模版 function text() { //变量输出 $this->as ...
- .net转php laraval框架学习系列(一) 环境搭建
之前也没写过什么博客,可能文章结构比较混乱,想到那写到哪. 主要是把自己学习中的经验写下来. 为什么选择laravel框架,是因为laravel框架目前是Php最流行的框架,深入研究后发现和asp.n ...
- java批量转换图片格式
废话不多直接上代码,代码其实也不多.... package com.qiao.testImage; import java.awt.image.BufferedImage; import java.i ...
- hdu 4535 吉哥系列故事——礼尚往来
http://acm.hdu.edu.cn/showproblem.php?pid=4535 错排公式:a[i]=(i-1)*(a[i-2]+a[i-1]): #include <cstdio& ...