as3 加载外部声音文件问题详细剖析
admin 2013-03-07 15:58:53
在AS3中使用声音跟AS2有很大的不同。先看下Flash帮助中的一些介绍,然后我们再使用导入到Flash中的声音做个小测试。
理解声音的结构 应用程序可以从4个地方加载声音:
运行时从外部声音文件里加载
内嵌到SWF文件里的声音资源
从连接到用户系统的麦克风上获取声音数据
从远程多媒体服务器,比如Flash Media Server获取声音流
可以等到声音全部加载后再播放,也可以边加载边播放。
ActionScript 3.0和Flash Player支持以mp3格式存储的声音。它们不能直接加载或播放其他格式的声音,比如WAV或AIFF。
使用Adobe Flash CS3 Professional,可以导入WAV或AIFF格式的声音,然后就可以把它们以MP3的格式嵌入到SWF文件中。Flash开发工具可以压缩嵌入的声音文件以减少文件大小,不过这样需要以降低声音质量的代价。更多信息查看 "Importing Sounds" in Using Flash.
ActionScript3.0声音使用flash.media包中的下列类
类 | 描述 |
flash.media.Sound | Sound类用来加载声音,管理声音的基本属性以及使声音开始播放 |
flash.media.SoundChannel | 当程序播放一个声音对象的时候,会创建一个新的SoundChannel对象控制播放。SoundChannel对象同时控制声音的左右频道。每个播放的声音都有自己的SoundChannel对象。 |
flash.media.SoundLoaderContext | SoundLoaderContext类指定当加载一个声音的时候使用多少秒的缓冲区,以及从服务器加载文件的时候FlashPlayer是否查找跨域策略文件。SoundLoaderContext对象作为Sound.load() 方法的参数使用。 |
flash.media.SoundMixer | SoundMixer类控制程序中所有声音的回放及安全属性。实际上,多个声音频道通过一个共同的SoundMixer对象混合在一起,因此,SoundMixer对象的属性会影响正在播放的所有SoundChannel对象。 |
flash.media.SoundTransform | SoundTransform类包含用于控制音量和平衡的值。SoundTransform对象可以应用到一个单独的SoundChannel对象,全局的SoundMixer对象或者一个Microphone对象。 |
flash.media.ID3Info | ID3Info对象包含mp3文件中的ID3元数据信息。 |
flash.media.Microphone | Microphone类表示连接到用户电脑上的麦克风或者其他声音输入设备。从麦克风输入的音频可以传送到本地喇叭上或者远程服务器上。Microphone对象控制它自己声音流的取样率。 |
每个加载和播放的声音都需要它自己的Sound类和SoundChannel类。从多个SoundChannel实例输出的声音在播放中通过全局的SoundMixer混合在一起。
Sound,SoundChannel和SoundMixer类不能用在从麦克风或者流媒体服务器获取的声音上。
加载外部声音文件 Sound类的每个实例都用来加载和回放指定的声音。应用程序不能重复使用一个Sound对象加载多于一个的声音。如果想加载一个新的声音,需要创建一个新的声音对象。
如果加载一个小的声音文件,比如要附加在按钮上的点击声音,可以像下面这样创建一个新的Sound,然后让它自动加载声音文件:
ActionScript Code:
var req:URLRequest = new URLRequest("click.mp3");
var s:Sound = new Sound(req);
Sound()构造函数使用一个URLRequest对象作为第一个参数。当指定了URLRequest参数的时候,Sound对象就开始自动加载指定的声音文件。
除了这个最简单的情况之外,应用程序都应该注意声音的加载过程并监视加载过程中出现的错误。比如,如果这个点击声音特别的大,可能在用户点击按钮触发这个声音的时候这个声音文件还没有完全加载完。试图播放一个未加载完的声音可能导致一个运行期错误。安全的做法是等声音完全加载后再允许用户的那些可能导致声音播放的行为。
在声音加载过程中,Sound对象会发送几个不同的事件。应用程序可以监听这些事件来跟踪加载过程以确保声音在完全加载后才能播放。下表列出了声音对象可能发送的事件。
事件 | 描述 |
open (Event.OPEN) | 在声音加载操作刚开始时发送。 |
progress (ProgressEvent.PROGRESS) | 当从文件或者流中接收到数据的时候周期性的发送。 |
id3 (Event.ID3) | 当一个mp3声音的ID3数据可用时发送。 |
complete (Event.COMPLETE) | 当声音的所有数据加载完后发送。 |
ioError (IOErrorEvent.IO_ERROR) | 如果声音文件不能定位或者加载过程中在所有数据接收完之前被中断的时候发送。 |
下面的代码演示了如何在声音加载完后播放一个声音:
ActionScript Code:
import flash.events.Event;
import flash.media.Sound;
import flash.net.URLRequest;
var s:Sound = new Sound();
s.addEventListener(Event.COMPLETE, onSoundLoaded);
var req:URLRequest = new URLRequest("bigSound.mp3");
s.load(req);
function onSoundLoaded(event:Event):void
{
var localSound:Sound = event.target as Sound;
localSound.play();
}
首先,这段示例代码创建了一个新的Sound对象,但并没有指定URLRequest参数。然后,它监听Sound对象的Event.COMPLETE事件,当所有声音数据加载完后执行onSoundLoaded()方法。接下来,它使用一个新的URLRequest值调用Sound.load()方法。
当声音全部加载完后执行onSoundLoaded()方法。Event对象的target属性是Sound对象的引用。调用Sound对象的play()方法开始播放声音。
监视声音加载过程 声音可能非常大,加载需要相当长的时间。FlashPlayer允许在声音全部加载完前就能播放,你可能想想给用户一个显示声音加载了多少以及播放了多少。
Sound类的两个事件使得显示加载进度变得相当容易: ProgressEvent.PROGRESS和Event.COMPLETE. 下面的代码展示了如何使用这些事件显示声音的加载进度:
ActionScript Code:
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.media.Sound;
import flash.net.URLRequest;
var s:Sound = new Sound();
s.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
s.addEventListener(Event.COMPLETE, onLoadComplete);
s.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
var req:URLRequest = new URLRequest("bigSound.mp3");
s.load(req);
function onLoadProgress(event:ProgressEvent):void
{
var loadedPct:uint = Math.round(100 * (event.bytesLoaded / event.bytesTotal));
trace("The sound is " + loadedPct + "% loaded.");
}
function onLoadComplete(event:Event):void
{
var localSound:Sound = event.target as Sound;
localSound.play();
}
function onIOError(event:IOErrorEvent)
{
trace("The sound could not be loaded: " + event.text);
}
这段代码首先创建了一个Sound对象,然后为事件ProgressEvent.PROGRESS和Event.COMPLETE添加监听器。在调用了Sound.load()方法及收到第一组数据后,开始出现ProgressEvent.PROGRESS事件,触发onSoundLoadProgress() 方法。
声音加载的百分比等于ProgressEvent对象的bytesLoaded属性除以bytesTotal属性。在Sound对象上同样可以获取bytesLoaded和bytesTotal。上面的代码简单展示了声音加载信息,但是你可以使用bytesLoaded和bytesTotal值来控制进度条组件。
这段代码同样展示了如何组织和响应加载中发生的错误。比如,如果无法定位指定的声音文件,会发送Event.IO_ERROR事件。前面代码中当发生错误时会调用onIOError()方法显示简单的错误信息。
使用嵌入的声音 好了,下面我们把声音导入到库里使用。
打开Flash CS3,选择文件->导入到库,选择好声音,然后确定后会导入声音。打开库面板,找到我们导入的声音,右键,选择”linkage…”菜单
< XMLNAMESPACE PREFIX ="V" />
会弹出如下框,选择Export for ActionScript注意观察这个弹出框
它与AS2中明显不同的是,标识符栏(identifier栏)不能填写,而类(Class)和基类(Base class)栏必须填写。类的名字默认是声音的文件名,但是名字中只能能有字母数字,且以字母开头,因此图中的名字01.mp3是不合格的,可以改为mp3或其他符合规则的名字。基类就是flash.media.Sound。我们点确定,会弹出下框:
大意是指定的类”mp3″在类路径下找不到,在SWF文件导出时会自动为你创建一个类。点确定就可以了。
在帧上写如下代码:
ActionScript Code:
var drum:mp3 = new mp3();
var channel:SoundChannel = drum.play();
导出,就可以听到声音了。
[as3]播放声音
播放一个加载的声音可以简单的调用Sound对象的Sound.play()方法,就像下面这样:
ActionScript Code:
var snd:Sound = new Sound(new URLRequest("smallSound.mp3"));
snd.play();
当使用ActionScript3.0播放声音的时候,你可以实现下列操作:
从一个指定的位置播放声音(不一定是最开始)
暂停声音,然后从暂停的位置处重新开始播放
可以精确的知道声音什么时候播放完了
跟踪声音的播放进度 当声音播放的时候改变音量或者均衡
要在播放过程中实现这些效果,使用SoundChannel, SoundMixer, 和 SoundTransform 类.
SoundChannel控制一个单独的声音的播放.SoundChannel.position属性可以被当作播放头,用来指示数据播放的位置.
当程序调用Sound.play()方法时,会创建一个新的SoundChannel实例来控制播放.
程序可以为Sound.play()方法指定它的startTime参数,该参数接收以毫秒为单位的数字,这样就可以使声音从指定的位置开始播放.
如果同时指定了Sound.play()方法的startTime参数和loops参数,那么当声音会从相同的起始位置重复播放就像下面这样:
ActionScript Code:
var snd:Sound = new Sound(new URLRequest("repeatingSound.mp3"));
snd.play(1000, 3);
在这个例子中,声音会接连从离开始1秒的位置播放3次.
暂停和重新开始声音 如果你的程序播放一个长声音,比如一首歌或者播客(podcasts,暂译为播客),你可能想允许用户可以暂停,然后继续播放这个声音.然后声音并不能像字面意思那样实现暂停,它只能停止(从字面意思上来讲,暂停跟停止是不同的,我们想实现暂停效果,但是没有直接的ActionScript语句可以实现暂停).但是,声音可以从任何位置处播放.因此你可以记录下它停止的位置,然后从这个位置后面开始播放,就实现了暂停效果.
比如,首先像下面这样加载并播放一个声音:
ActionScript Code:
var snd:Sound = new Sound(new URLRequest("bigSound.mp3"));
var channel:SoundChannel = snd.play();
当声音播放的时候SoundChannel.position属性显示声音当前的播放位置.程序可以像下面这个例子一样,在停止它之前记录下它的位置:
ActionScript Code:
var pausePosition:int = channel.position;
channel.stop();
要重新播放声音,传递之前记录的播放位置数据来使声音从它停止的地方重新开始播放.
ActionScript Code:
channel = snd.play(pausePosition);
监视播放 你的程序可能需要知道什么时候它停止了播放,以便去播放另一个声音,或者清除在播放过程中使用的一些资源.当声音播放完毕的时候SoundChannel类发送一个Event.SOUND_COMPLETE事件. 你的程序可以监听这个事件并作出适当的动作,就像下面这样:
ActionScript Code:
import flash.events.Event;
import flash.media.Sound;
import flash.net.URLRequest;
var snd:Sound = new Sound("smallSound.mp3");
var channel:SoundChannel = snd.play();
s.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
public function onPlaybackComplete(event:Event)
{
trace("The sound has finished playing.");
}
SoundChannel类在播放中并不发送进度事件.要报告播放进度,你可以自己设置一个时间进制并跟踪播放头的位置.
可以使用SoundChannel.position属性值除以声音长度来计算已播放的百分数:
ActionScript Code:
var playbackPercent:uint = 100 * (channel.position / snd.length);
但是,只有在播放前声音全部加载完了,这段代码才能精确显示播放的百分比.Sound.length属性显示的是当前已加载的声音长度,而不是声音文件的的真实长度.你可以使用Sound对象的bytesLoaded和bytesTotal属性估计声音的长度:
ActionScript Code:
var estimatedLength:int =
Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal));
var playbackPercent:uint = 100 * (channel.position / estimatedLength);
下面的代码加载一个比较大的声音文件,并使用Event.ENTER_FRAME事件作为显示播放进度的时间机制.它周期性的报告播放的百分比,这个百分比是用当前位置除以声音总长度计算出来的:
ActionScript Code:
import flash.events.Event;
import flash.media.Sound;
import flash.net.URLRequest;
var snd:Sound = new Sound();
var req:URLRequest = new
URLRequest("http://av.adobe.com/podcast/csbu_dev_podcast_epi_2.mp3");
snd.load(req);
var channel:SoundChannel;
channel = snd.play();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
snd.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
function onEnterFrame(event:Event):void
{
var estimatedLength:int =
Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal));
var playbackPercent:uint =
Math.round(100 * (channel.position / estimatedLength));
trace("Sound playback is " + playbackPercent + "% complete.");
}
function onPlaybackComplete(event:Event)
{
trace("The sound has finished playing.");
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
在开始加载声音数据后,调用方法snd.play()并把结果保存在类型为SoundChannel,名子为channel的变量中. 然后为主程序添加了Event.ENTER_FRAME事件的监听器,为SoundChannel对象的Event.SOUND_COMPLETE事件也添加了监听器.
在动画中每到达一个新帧的时候,onEnterFrame()方法就会被调用.onEnterFrame()根据已经加载的数据估算声音的总长度,然后显示当前播放的百分数.
当整个声音播放完后,会执行onPlaybackComplete()方法,移除Event.ENTER_FRAME事件的监听器,因为播放完后就不需要在报告播放进度了.
Event.ENTER_FRAME事件每秒会发送好几次.在某些情况下,你可能不想发送的这么频繁.那么,你就可以使用flash.util.Timer类设置自己的时间机制.
停止流声音 流声音(指的是在完全加载之前就播放的声音)在播放过程中有一个特点需要注意.当你的程序调用SoundChannel.stop()方法的时候,声音会停止一帧,而在下一帧它会从开始的地方重新开始播放. 之所以发生这种情况,是因为声音仍然在加载中.要停止加载和播放,使用Sound.close()方法.
解决声音以数据流形式放到时间轴上导出后音质变差的问题
[关键字]Flash 数据流 声音 音质变差 立体声转换成单声道
[问题描述]:将声音放到时间轴上,选择数据流的同步方式,在Flash中听声音没有发现问题,但是导出后音质变的奇差,基本上不能用,虽然将声音同步方式选择开始或者事件可以解决这个问题,但是有时是非要用数据流同步方式不可的(因为要用播放暂停控制,因为有进度条控制)。
[解决办法]:这是由于在导出时,Flash会以MP3的形式压缩声音,一般会把声音压缩成比特率低于22Kbps的声音,同时将立体声转换成了单声道声音,很多声音都是因为将立体声转换成单声道后音质变差的。因此我们要去掉次选项。
如果Flash中有很多声音,那么我们要更改发布设置。
1、打开发布设置,点开”Flash”选项卡,找到音频流设置的项,如下图
2、点开设置按钮,
3、将比特率选择24kbps(或者其他的高于22kbps的项,因为只有这样才能更改”将立体声转换成单声道”)
4、勾掉”将立体声转换成单声道”前面的勾,然后确定就可以了
然后再导出,基本上就没有问题了。
IT时代网(关注微信公众号ITtime2000,定时推送,互动有福利惊喜)所有原创文章版权所有,未经授权,转载必究。
创客100创投基金成立于2015年,直通硅谷,专注于TMT领域早期项目投资。LP均来自政府、互联网IT、传媒知名企业和个人。创客100创投基金对IT、通信、互联网、IP等有着自己独特眼光和丰富的资源。决策快、投资快是创客100基金最显著的特点。