处理动态生成的音频

Flash Player 10 和更高版本,Adobe AIR 1.5 和更高版本

注: 从 Flash Player 10 和 Adobe AIR 1.5 开始可以动态生成音频。

可以动态生成音频数据,而不是加载或流式传输现有声音。在为 Sound 对象的 sampleData 事件分配事件侦听器时,可以生成音频数据。(sampleData 事件在 flash.events 包的 SampleDataEvent 类中定义。)在这种情况下,Sound 对象不从文件中加载声音数据。相反,该对象将用作声音数据的套接字,声音数据通过使用您分配给此事件的函数流入该对象。

在您将 sampleData 事件侦听器添加到 Sound 对象后,该对象将定期请求数据以添加到声音缓冲区。此缓冲区包含 Sound 对象要播放的数据。在调用 Sound 对象的 play() 方法时,它会在请求新的声音数据时调度 sampleData 事件。(只有在 Sound 对象尚未从文件加载 mp3 数据时,此操作才生效。)

SampleDataEvent 对象包含 data 属性。在事件侦听器中,将 ByteArray 对象写入此 data 对象。写入此对象的字节数组将添加到 Sound 对象播放的缓冲声音数据中。缓冲区中的字节数组是由从 -1 到 1 的浮点值组成的流。各浮点值均代表声音样本的一个声道(左声道或右声道)的幅度。声音按每秒 44,100 个样本进行采样。每个样本均包含左声道和右声道,在字节数组中以浮点数据的形式交错排列。

在处理函数中,使用 ByteArray.writeFloat() 方法写入 sampleData 事件的 data 属性。例如,以下代码将生成正弦波:

var mySound:Sound = new Sound(); 
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, sineWaveGenerator); 
mySound.play(); 
function sineWaveGenerator(event:SampleDataEvent):void 
{ 
    for (var i:int = 0; i < 8192; i++) 
    { 
        var n:Number = Math.sin((i + event.position) / Math.PI / 4); 
        event.data.writeFloat(n); 
        event.data.writeFloat(n); 
    } 
}

当您调用 Sound.play() 时,该应用程序将开始调用事件处理函数,并请求声音样本数据。在播放声音时,应用程序将继续发送事件,直至您停止提供数据或调用 SoundChannel.stop()

事件的滞后时间因平台而异,在以后的 Flash Player 和 AIR 版本中也可能改变。请不要依赖某个特定的滞后时间;而应计算出相应的滞后时间。若要计算滞后时间,请使用以下公式:

(SampleDataEvent.position / 44.1) - SoundChannelObject.position

向 SampleDataEvent 对象的 data 属性提供 2048 到 8192 个样本(对于每次事件侦听器调用)。为了获得最佳性能,请尽可能多地提供样本(最多可达 8192 个样本)。提供的样本越少,在播放过程中就越有可能出现单击和弹出事件。此行为对于不同的平台会有所不同,并且会在各种情况下发生。例如,在调整浏览器的大小时。在仅提供了 2048 个样本时,工作在某一个平台上的代码可能在运行于其他不同平台时将不能很好地工作。若要尽可能缩短滞后时间,请考虑允许用户选择数据量。

如果提供的样本少于 2048 个(每次 sampleData 事件侦听器调用),则应用程序将在播放完剩余的样本后停止。然后,SoundChannel 对象调度 SoundComplete 事件。

对源自 mp3 数据的声音数据进行修改

使用 Sound.extract() 方法提取 Sound 对象中的数据。可以使用(和修改)该数据,将其写入另一个 Sound 对象的动态流以进行播放。例如,以下代码使用加载的 MP3 文件的字节,并通过过滤函数 upOctave() 进行传递:

var mySound:Sound = new Sound(); 
var sourceSnd:Sound = new Sound(); 
var urlReq:URLRequest = new URLRequest("test.mp3"); 
sourceSnd.load(urlReq); 
sourceSnd.addEventListener(Event.COMPLETE, loaded); 
function loaded(event:Event):void 
{ 
    mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, processSound); 
    mySound.play(); 
} 
function processSound(event:SampleDataEvent):void 
{ 
        var bytes:ByteArray = new ByteArray(); 
        sourceSnd.extract(bytes, 8192); 
        event.data.writeBytes(upOctave(bytes)); 
} 
function upOctave(bytes:ByteArray):ByteArray 
{ 
    var returnBytes:ByteArray = new ByteArray(); 
    bytes.position = 0; 
    while(bytes.bytesAvailable > 0) 
    { 
        returnBytes.writeFloat(bytes.readFloat()); 
        returnBytes.writeFloat(bytes.readFloat()); 
        if (bytes.bytesAvailable > 0) 
        { 
            bytes.position += 8; 
        } 
    } 
    return returnBytes; 
}

有关生成声音的限制

sampleData 事件侦听器与 Sound 对象一起使用时,启用的其他 Sound 方法仅包括 Sound.extract()Sound.play()。调用任何其他方法或属性将导致异常。仍启用 SoundChannel 对象的所有方法和属性。