I Love China

技术·人生

技术服务生活
繁體
6月 11th, 2007

[as3]访问声音的原始数据并制作一个有趣的波形图

方法SoundMixer.computeSpectrum()可以读取正在播放的声音的波形数据。如果多个SoundChannel对象正在播放,SoundMixer.computeSpectrum()方法显示每个SoundChannel对象混合在一起的声音数据。

声音数据将以一个包含512字节数据的ByteArray对象返回,每个字节包含一个介于-1和1之间的浮点值。这些值表示正在播放的声音波形的振幅。这些数据按每组256字节分成两组,第一组是左声道的,第二组是右声道的。

如果FFTMode属性设置为true,那么方法SoundMixer.computeSpectrum()返回的是频谱数据而不是波形数据。频谱显示的是声音的频率决定的振幅,从最低频到最高频。傅立叶变换(FFT)用来将波形数据转换成频谱数据。频谱数据的取值范围是从0到近似于1.414(2的平方根)。

下图比较了当FFTMode设为truefalse时方法computeSpectrum返回的数据。这个图中的声音左声道有一个高的低音,右声道有一个打击音。

computespectrum.png

computeSpectrum()方法也可以返回一个重新取样的低比特率数据。通常,这将会以牺牲声音细节为代价返回一个比较平滑的波形数据或者频谱数据。属性stretchFactor控制computeSpectrum方法的取样率。当stretchFactor设置为0,也就是默认值的时候,声音的取样率是44.1kHZ.steretchFactor参数的值每增加1,采样率会降低一倍,因此,当设置为1时,取样率是22.05kHz,设置为2指定采样率为11.025kHz,依此类推.当把stretchFactor设置为一个高值时,方法computeSpectrum()依然会每个声道返回256字节的数据.

SoundMixer.computeSpectrum()方法有些局限性:

  • 因为从麦克风或者RTMP流获得的数据不会通过全局SoundMixer对象,因此,SoundMixer.computeSpectrum()方法不会返回这些声音的数据.
  • 如果正在播放的声音在当前内容沙箱之外,安全限制会导致SoundMixer.computeSpectrum()方法抛出一个错误.

构建一个简单的声音观察仪

下面的代码当动画每进入一帧时使用SoundMixer.computeSpectrum()方法展示声音的波形图:

ActionScript Code:
  1. import flash.display.Graphics;
  2. import flash.events.Event;
  3. import flash.media.Sound;
  4. import flash.media.SoundChannel;
  5. import flash.media.SoundMixer;
  6. import flash.net.URLRequest;
  7.  
  8. const PLOT_HEIGHT:int = 200;
  9. const CHANNEL_LENGTH:int = 256;
  10.  
  11. var snd:Sound = new Sound();
  12. var req:URLRequest = new URLRequest("bigSound.mp3");
  13. snd.load(req);
  14.  
  15. var channel:SoundChannel;
  16. channel = snd.play();
  17. addEventListener(Event.ENTER_FRAME, onEnterFrame);
  18. snd.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
  19.  
  20. var bytes:ByteArray = new ByteArray();
  21.  
  22. function onEnterFrame(event:Event):void
  23. {
  24.     SoundMixer.computeSpectrum(bytes, false, 0);
  25.    
  26.     var g:Graphics = this.graphics;
  27.    
  28.     g.clear();
  29.     g.lineStyle(0, 0x6600CC);
  30.     g.beginFill(0x6600CC);
  31.     g.moveTo(0, PLOT_HEIGHT);
  32.    
  33.     var n:Number = 0;
  34.        
  35.     // left channel
  36.     for (var i:int = 0; i < CHANNEL_LENGTH; i++) 
  37.     {
  38.         n = (bytes.readFloat() * PLOT_HEIGHT);
  39.         g.lineTo(i * 2, PLOT_HEIGHT - n);
  40.     }
  41.     g.lineTo(CHANNEL_LENGTH * 2, PLOT_HEIGHT);
  42.     g.endFill();
  43.    
  44.     // right channel
  45.     g.lineStyle(0, 0xCC0066);
  46.     g.beginFill(0xCC0066, 0.5);
  47.     g.moveTo(CHANNEL_LENGTH * 2, PLOT_HEIGHT);
  48.    
  49.     for (i = CHANNEL_LENGTH; i > 0; i--) 
  50.     {
  51.         n = (bytes.readFloat() * PLOT_HEIGHT);
  52.         g.lineTo(i * 2, PLOT_HEIGHT - n);
  53.     }
  54.     g.lineTo(0, PLOT_HEIGHT);
  55.     g.endFill();
  56. }
  57.  
  58. function onPlaybackComplete(event:Event)
  59. {
  60.     removeEventListener(Event.ENTER_FRAME, onEnterFrame);
  61. }

这段代码首先加载并播放一个声音,然后监听Event.ENTER_FRAME事件,也就是在声音播放时触发onEnterFrame()函数. onEnterFrame()首先调用SoundMixer.computeSpectrum()方法,把波形数据保存在ByteArray对象bytes中.

然后使用矢量绘图API绘制声音的波形.第一个for循环遍历前256个左声道的数据, 并使用Graphics.lineTo()方法从一个点绘到另一个点.第二个for循环遍历接下来的256个右声道的数据,这次以相反的次序绘图,从右到左.最后的波形图可以产生一个有趣的镜像效果..

由于使用声音可能涉及版权问题,因此我就不上传SWF了,下面贴张Flash帮助里的效果图

soundvisualizer.png

随机文章:

2 条评论 to “[as3]访问声音的原始数据并制作一个有趣的波形图”

  1. 我在网上,手机没电了,你在线吗?

  2. 怒了,你到底要我怎么做才不生气了?

Leave a Reply