播放声音

播放加载的声音非常简便,您只需为 Sound 对象调用 Sound.play() 方法,如下所示:

var req = new air.URLRequest("smallSound.mp3"); 
var snd = new air.Sound(req); 
snd.play();

声音播放操作

播放声音时,您可以执行以下操作:

  • 从特定起始位置播放声音

  • 暂停声音并稍后从相同位置恢复播放

  • 准确了解何时播放完声音

  • 跟踪声音的播放进度

  • 在播放声音的同时更改音量或声相

若要在播放期间执行这些操作,请使用 SoundChannel、SoundMixer 和 SoundTransform 类。

SoundChannel 类控制一种声音的播放。可以将 SoundChannel.position 属性视为播放头,以指示所播放的声音数据中的当前位置。

当应用程序调用 Sound.play() 方法时,将创建一个新的 SoundChannel 类实例来控制播放。

通过将特定起始位置(以毫秒为单位)作为 Sound.play() 方法的 startTime 参数进行传递,应用程序可以从该位置播放声音。它也可以通过在 Sound.play() 方法的 loops 参数中传递一个数值,指定快速且连续地将声音重复播放固定的次数。

使用 startTime 参数和 loops 参数调用 Sound.play() 方法时,每次将从相同的起始点重复播放声音。以下代码说明了这一过程:

var req = new air.URLRequest("repeatingSound.mp3"); 
var snd = new air.Sound(); 
snd.play(1000, 3);

在此示例中,从声音开始后的 1 秒起连续播放声音三次。

暂停和恢复播放声音

如果应用程序播放很长的声音(如歌曲或播客),您可能需要让用户暂停和恢复播放这些声音。实际上,无法在播放期间暂停声音;而只能将其停止。但是,可以从任何位置开始播放声音。您可以记录声音停止时的位置,并随后从该位置开始重放声音。

例如,假定代码加载并播放一个声音文件,如下所示:

var req = new air.URLRequest("bigSound.mp3"); 
var snd = new air.Sound(req); 
var channel = snd.play();

在播放声音时, channel 对象的 position 属性指示声音文件中当前播放的位置。应用程序可以在停止播放声音之前存储位置值,如下所示:

var pausePosition = channel.position; 
channel.stop();

若要恢复播放声音,请传递以前存储的位置值,以便从声音以前停止的相同位置重新启动声音。

channel = snd.play(pausePosition);

监视播放

应用程序可能需要了解何时停止播放某种声音,然后,它可以开始播放另一种声音,或者清除在以前播放期间使用的某些资源。SoundChannel 类在其声音完成播放时将调度 soundComplete 事件。应用程序可以侦听此事件并执行相应的动作,如下例所示:

var snd = new air.Sound("smallSound.mp3"); 
var channel = snd.play(); 
s.addEventListener(air.Event.SOUND_COMPLETE, onPlaybackComplete); 
 
public function onPlaybackComplete(event) 
{ 
    air.trace("The sound has finished playing."); 
}

SoundChannel 类在播放期间不调度 progress 事件。若要报告播放进度,应用程序可以设置其自己的计时机制并跟踪声音播放头的位置。

若要计算已播放的声音百分比,您可以将 SoundChannel.position 属性值除以所播放的声音数据长度:

var playbackPercent = 100 * (channel.position / snd.length);

但是,仅当在开始播放之前完全加载了声音数据时,此代码才会报告精确的播放百分比。 Sound.length 属性显示当前加载的声音数据的大小,而不是整个声音文件的最终大小。若要跟踪仍在加载的声音流的播放进度,应用程序应估计完整声音文件的最终大小,并在其计算中使用该值。您可以使用 Sound 对象的 bytesLoaded bytesTotal 属性来估计声音数据的最终长度,如下所示:

var estimatedLength = Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal)); 
var playbackPercent = 100 * (channel.position / estimatedLength);

以下代码加载一个较大的声音文件,并使用 setInterval() 函数作为其计时机制来显示播放进度。此代码会定期报告播放百分比,该百分比是由当前位置值除以声音数据的总长度得出的:

var snd = new air.Sound(); 
var url = "http://www.example.com/sounds/test.mp3"; 
var req = new air.URLRequest(url); 
snd.load(req); 
 
var channel = snd.play(); 
var timer = setInterval(monitorProgress, 100); 
snd.addEventListener(air.Event.SOUND_COMPLETE, onPlaybackComplete); 
 
function monitorProgress(event) 
{ 
    var estimatedLength = Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal)); 
    var playbackPercent = Math.round(100 * (channel.position / estimatedLength)); 
    air.trace("Sound playback is " + playbackPercent + "% complete."); 
} 
 
function onPlaybackComplete(event) 
{ 
    air.trace("The sound has finished playing."); 
    clearInterval(timer); 
}

在开始加载声音数据后,此代码会调用 snd.play() 方法,并将生成的 SoundChannel 对象存储在 channel 变量中。然后,此代码会添加 monitorProgress() 方法,该方法将由 setInterval() 函数重复调用。此代码对 SoundChannel 对象使用事件侦听器,以在完成播放时侦听发生的 soundComplete 事件。

monitorProgress() 方法会基于已加载的数据量估计声音文件的总长度。然后,此代码会计算并显示当前播放百分比。

在播放整个声音后,将执行 onPlaybackComplete() 函数。此函数会删除 setInterval() 函数的回调方法,以使播放完成后应用程序不显示进度更新。

停止声音流

在进行流式传输的声音(即,在播放的同时仍在加载声音)的播放过程中,有一个奇怪的现象。当对播放声音流的 SoundChannel 实例调用 stop() 方法时,声音播放将会停止,随后从声音开头重新播放。发生这种情况是因为,声音加载过程仍在进行当中。若要同时停止声音流加载和播放,请调用 Sound.close() 方法。

控制音量和声相

单个 SoundChannel 对象可同时控制声音的左立体声声道和右立体声声道。如果 mp3 声音是单声道声音,SoundChannel 对象的左右两个立体声声道将包含完全相同的波形。

可通过使用 SoundChannel 对象的 leftPeak rightPeak 属性来查明所播放的声音的每个立体声声道的波幅。这些属性显示声音波形本身的峰值波幅。它们并不表示实际播放音量。实际播放音量是声音波形的波幅以及 SoundChannel 对象和 SoundMixer 类中设置的音量值的函数。

在播放期间,可以使用 SoundChannel 对象的 pan 属性为左声道和右声道分别指定不同的音量级别。pan 属性可以具有范围从 -1 到 1 的值。值 -1 表示左声道以最大音量播放而右声道处于静音状态。值 1 表示右声道以最大音量播放而左声道处于静音状态。介于 -1 和 1 之间的值可为左右声道值设置一定比例的值。值 0 表示两个声道以均衡的中音量级别播放。

以下代码示例使用音量值 0.6 和声相值 -1 创建了一个 SoundTransform 对象(左声道为最高音量,右声道没有音量)。它会将 SoundTransform 对象作为参数传递给 play() 方法。 play() 方法将该 SoundTransform 对象应用于为控制播放而创建的新 SoundChannel 对象。

var req = new air.URLRequest("bigSound.mp3"); 
var snd = new air.Sound(req);  
var trans = new air.SoundTransform(0.6, -1); 
var channel = snd.play(0, 1, trans);

可以在播放声音的同时更改音量和声相。设置 SoundTransform 对象的 pan volume 属性,然后将该对象作为 SoundChannel 对象的 soundTransform 属性进行应用。

也可以通过使用 SoundMixer 类的 soundTransform 属性,同时为所有声音设置全局音量和声相值。以下示例说明了这一过程:

SoundMixer.soundTransform = new air.SoundTransform(1, -1);

也可以使用 SoundTransform 对象来设置 Microphone 对象的音量和声相值(请参阅 捕获声音输入 )。

以下示例在播放声音的同时将声音从左声道移到右声道,然后再移回来,并交替进行这一过程:

var snd = new air.Sound();          
var req = new air.URLRequest("bigSound.mp3"); 
snd.load(req); 
 
var panCounter = 0; 
 
var trans = new air.SoundTransform(1, 0); 
var channel = snd.play(0, 1, trans); 
channel.addEventListener(air.Event.SOUND_COMPLETE,  
                            onPlaybackComplete); 
 
var timer = setInterval(panner, 100); 
 
function panner() 
{ 
    trans.pan = Math.sin(panCounter); 
    channel.soundTransform = trans; // or SoundMixer.soundTransform = trans; 
    panCounter += 0.05; 
} 
 
function onPlaybackComplete(event) 
{ 
    clearInterval(timer); 
}

此代码先加载一个声音文件,然后将音量设置为 1(最大音量)并将声相设置为 0(声音在左右两声道之间均衡地平均分布)以创建一个 SoundTransform 对象。接下来,此代码调用 snd.play() 方法,以将 SoundTransform 对象作为参数进行传递。

在播放声音时,将反复执行 panner() 方法。 panner() 方法将使用 Math.sin() 函数生成一个介于 -1 和 1 之间的值。此范围对应于可接受的 SoundTransform.pan 属性值。此代码将 SoundTransform 对象的 pan 属性设置为新值,然后设置声道的 soundTransform 属性以使用更改后的 SoundTransform 对象。

要运行此示例,请用本地 mp3 文件的名称替换文件名 bigSound.mp3。然后,运行该示例。当右声道音量变小时,您应会听到左声道音量变大,反之亦然。

在此示例中,可通过设置 SoundMixer 类的 soundTransform 属性来获得同样的效果。但是,这会影响当前播放的所有声音的声相,而不是只影响此 SoundChannel 对象播放的一种声音。