访问原始声音数据

Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本

通过使用 SoundMixer.computeSpectrum() 方法,应用程序可以读取当前所播放的波形的原始声音数据。如果当前播放多个 SoundChannel 对象, SoundMixer.computeSpectrum() 方法将显示混合在一起的每个 SoundChannel 对象的组合声音数据。

声音数据是作为 ByteArray 对象(包含 512 个字节的数据)返回的,其中的每个字节包含一个介于 -1 和 1 之间的浮点值。这些值表示所播放的声音波形中的点的波幅。这些值是分为两个组(每组包含 256 个值)提供的,第一个组用于左立体声声道,第二个组用于右立体声声道。

如果将 FFTMode 参数设置为 true SoundMixer.computeSpectrum() 方法将返回频谱数据,而非波形数据。频谱显示按声音频率(从最低频率到最高频率)排列的波幅。可以使用快速傅立叶变换 (FFT) 将波形数据转换为频谱数据。生成的频谱值范围介于 0 和约 1.414(2 的平方根)之间。

下图比较了将 FFTMode 参数设置为 true false 时从 computeSpectrum() 方法返回的数据。此图所用数据的声音在左声道中包含很大的低音;而在右声道中包含击鼓声。
查看完全大小图形
SoundMixer.computeSpectrum() 方法返回的值
A.
fftMode=true

B.
fftMode=false

computeSpectrum() 方法也可以返回已在较低比特率重新采样的数据。通常,这会产生更平滑的波形数据或频率数据,但会以牺牲细节为代价。 stretchFactor 参数控制 computeSpectrum() 方法数据的采样率。如果将 stretchFactor 参数设置为 0(默认值),则以采样率 44.1 kHz 采集声音数据样本。stretchFactor 参数值每连续增加 1,采样率就减小一半,因此,值 1 指定采样率 22.05 kHz,值 2 指定采样率 11.025 kHz,依此类推。当使用较高的 stretchFactor 值时, computeSpectrum() 方法仍会为每个立体声声道返回 256 个字节。

SoundMixer.computeSpectrum() 方法具有一些限制:

  • 由于来自麦克风或 RTMP 流的声音数据不是通过全局 SoundMixer 对象传递的,因此, SoundMixer.computeSpectrum() 方法不会从这些源返回数据。

  • 如果播放的一种或多种声音来自当前内容沙箱以外的源,安全限制将导致 SoundMixer.computeSpectrum() 方法引发错误。有关 SoundMixer.computeSpectrum() 方法的安全限制的更多详细信息,请参阅 加载和播放声音时的安全注意事项 作为数据访问加载的媒体

但在 AIR 应用程序中,应用程序安全沙箱中的内容(随 AIR 应用程序安装的内容)不受这些安全限制的约束。

构建简单的声音可视化程序

以下示例使用 SoundMixer.computeSpectrum() 方法来显示声音波形图(它对每个帧进行动画处理):

import flash.display.Graphics; 
import flash.events.Event; 
import flash.media.Sound; 
import flash.media.SoundChannel; 
import flash.media.SoundMixer; 
import flash.net.URLRequest; 
 
const PLOT_HEIGHT:int = 200; 
const CHANNEL_LENGTH:int = 256; 
 
var snd:Sound = new Sound(); 
var req:URLRequest = new URLRequest("bigSound.mp3"); 
snd.load(req); 
 
var channel:SoundChannel; 
channel = snd.play(); 
addEventListener(Event.ENTER_FRAME, onEnterFrame); 
snd.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete); 
 
var bytes:ByteArray = new ByteArray(); 
 
function onEnterFrame(event:Event):void 
{ 
    SoundMixer.computeSpectrum(bytes, false, 0); 
     
    var g:Graphics = this.graphics; 
     
    g.clear(); 
    g.lineStyle(0, 0x6600CC); 
    g.beginFill(0x6600CC); 
    g.moveTo(0, PLOT_HEIGHT); 
     
    var n:Number = 0; 
         
    // left channel 
    for (var i:int = 0; i < CHANNEL_LENGTH; i++)  
    { 
        n = (bytes.readFloat() * PLOT_HEIGHT); 
        g.lineTo(i * 2, PLOT_HEIGHT - n); 
    } 
    g.lineTo(CHANNEL_LENGTH * 2, PLOT_HEIGHT); 
    g.endFill(); 
     
    // right channel 
    g.lineStyle(0, 0xCC0066); 
    g.beginFill(0xCC0066, 0.5); 
    g.moveTo(CHANNEL_LENGTH * 2, PLOT_HEIGHT); 
     
    for (i = CHANNEL_LENGTH; i > 0; i--)  
    { 
        n = (bytes.readFloat() * PLOT_HEIGHT); 
        g.lineTo(i * 2, PLOT_HEIGHT - n); 
    } 
    g.lineTo(0, PLOT_HEIGHT); 
    g.endFill(); 
} 
 
function onPlaybackComplete(event:Event) 
{ 
    removeEventListener(Event.ENTER_FRAME, onEnterFrame); 
}

此示例先加载并播放一个声音文件,然后在播放声音的同时侦听将触发 onEnterFrame() 方法的 Event.ENTER_FRAME 事件。 onEnterFrame() 方法先调用 SoundMixer.computeSpectrum() 方法,后者将声音波形数据存储在 bytes ByteArray 对象中。

声音波形是使用矢量绘图 API 绘制的。 for 循环将循环访问第一批 256 个数据值(表示左立体声声道),然后使用 Graphics.lineTo() 方法绘制一条从每个点到下一个点的直线。第二个 for 循环将循环访问下一批 256 个值,此时按相反的顺序(从右到左)对它们进行绘制。生成的波形图可能会产生非常有趣的镜像图像效果,如以下图像中所示。