[Laszlo-checkins] r11035 - in openlaszlo/trunk: WEB-INF/lps/lfc/kernel/swf9 WEB-INF/lps/lfc/views examples/music
bargull@openlaszlo.org
bargull at openlaszlo.org
Wed Sep 17 06:45:17 PDT 2008
Author: bargull
Date: 2008-09-17 06:45:08 -0700 (Wed, 17 Sep 2008)
New Revision: 11035
Added:
openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAsset.as
Modified:
openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/Library.lzs
openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAudioKernel.lzs
openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzSprite.as
openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloView.lzs
openlaszlo/trunk/examples/music/music.lzx
openlaszlo/trunk/examples/music/music.mp3
Log:
Change 20080915-bargull-3QU by bargull at dell--p4--2-53 on 2008-09-15 19:28:40
in /home/Admin/src/svn/openlaszlo/trunk
for http://svn.openlaszlo.org/openlaszlo/trunk
Summary: implement swf9 audio
New Features: LPP-6983
Bugs Fixed:
Technical Reviewer: max
QA Reviewer: promanik
Doc Reviewer: (pending)
Documentation:
Release Notes:
Details:
implement sprite audio capability, LzAudioKernel still needs some tweaks for global sound.
LzSprite:
- set "resource" after calling "unload()" otherwise you'll set "resource" to null
- set "mouseEnabled" to true for the canvas sprite, so top-level context-menus will work (LPP-6980)
- update "setDefaultContextMenu"
- all other changes concern the audio implementation
Changed "music.lzx" to load mp3 unproxied for swf8, so we can read the ID3-Tag.
Needed to re-tag "music.mp3" because Flash requires UTF-8 ID3-Tags.
Tests:
examples/music/music.lzx?lzr=swf9 works
Modified: openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/Library.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/Library.lzs 2008-09-17 13:33:12 UTC (rev 11034)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/Library.lzs 2008-09-17 13:45:08 UTC (rev 11035)
@@ -37,6 +37,7 @@
#include "kernel/swf9/LzScreenKernel.as"
#include "kernel/swf9/LzContextMenuKernel.lzs"
#include "kernel/swf9/LzAudioKernel.lzs"
+#include "kernel/swf9/LzAsset.as"
//#include "kernel/swf9/dojo/Library.lzs"
#include "kernel/swf9/DojoExternalInterface.as"
Added: openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAsset.as
Property changes on: openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAsset.as
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Modified: openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAudioKernel.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAudioKernel.lzs 2008-09-17 13:33:12 UTC (rev 11034)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzAudioKernel.lzs 2008-09-17 13:45:08 UTC (rev 11035)
@@ -13,66 +13,98 @@
* @access private
*
*/
-class LzAudioKernel{
-/*
-{
-#pragma "passThrough=true"
-LzAudioKernel.globalSound = new Sound();
+class LzAudioKernel {
+ #passthrough (toplevel:true) {
+ import flash.display.Sprite;
+ import flash.media.Sound;
+ import flash.media.SoundChannel;
+ import flash.media.SoundMixer;
+ import flash.media.SoundTransform;
+ }#
+
+ /**
+ * Sets the current sound resource and starts playing it.
+ * @param String snd: Name of a sound resource to play
+ * @param LzSprite t: Sprite for sound to act upon (optional)
+ */
+ static function playSound (snd:String, t:LzSprite = null){
+ if (t == null) {
+ if (LzAsset.isSoundAsset(snd)) {
+ //TODO: check for swf8 compatibility
+ var sound:Sound = new (LzResourceLibrary[snd])['assetclass']() cast Sound;
+ sound.play();
+ }
+ } else {
+ t.setResource(snd);
+ }
+ }
+
+ /**
+ * Stop playing the current sound
+ * @param LzSprite t: Sprite for sound to act upon (optional)
+ */
+ static function stopSound (t:LzSprite = null) :void {
+ if (t == null) {
+ //TODO: check for swf8 compatibility
+ SoundMixer.stopAll();
+ } else {
+ t.stop();
+ }
+ }
+
+ /**
+ * Start playing the current sound
+ * */
+ static function startSound (t:Sprite = null) {
+ //TODO: check for swf8 compatibility
+ }
+
+ /**
+ * @access private
+ */
+ static function getSoundObject (t:LzSprite) :* {
+ return t ? t.soundChannel ? t.soundChannel : t : SoundMixer;
+ }
+
+ /**
+ * Get the global volume
+ * @return Number: volume from 0 to 100 (0 is silent).
+ * @param LzSprite t: Sprite for sound to act upon (optional)
+ */
+ static function getVolume (t:LzSprite = null) :Number {
+ return getSoundObject(t).soundTransform.volume * 100;
+ }
+
+ /**
+ * Set the global volume.
+ * @param Number v: linear volume from 0 to 100 (0 is silent).
+ * @param LzSprite t: Sprite for sound to act upon (optional)
+ */
+ static function setVolume (v:Number, t:LzSprite = null) :void {
+ var soundObj:* = getSoundObject(t);
+ var sndTransform:SoundTransform = soundObj.soundTransform;
+ sndTransform.volume = (v < 0 ? 0 : v > 100 ? 100 : v) / 100;
+ soundObj.soundTransform = sndTransform;
+ }
+
+ /**
+ * Get the global pan.
+ * @return Number: linear pan from -100 to +100 (left to right)
+ * @param LzSprite t: Sprite for sound to act upon (optional)
+ */
+ static function getPan (t:LzSprite = null) :Number {
+ return getSoundObject(t).soundTransform.pan * 100;
+ }
+
+ /**
+ * Set the global pan.
+ * @param Number p: linear pan from -100 to +100 (left to right)
+ * @param LzSprite t: Sprite for sound to act upon (optional)
+ */
+ static function setPan (p:Number, t:LzSprite = null) :void {
+ var soundObj:* = getSoundObject(t);
+ var sndTransform:SoundTransform = soundObj.soundTransform;
+ sndTransform.pan = (p < -100 ? -100 : p > 100 ? 100 : p) / 100;
+ soundObj.soundTransform = sndTransform;
+ }
}
-LzAudioKernel.globalSound.$xxx = "globsnd" ;
-*/
-
-/**
- * Sets the current sound resource and starts playing it.
- * @param String snd: Name of a sound resource to play
- */
-static function playSound( snd , ...t ){
-}
-
-/**
- * Stop playing the current sound
- * */
-static function stopSound( ...t ){
-}
-
-/**
- * Start playing the current sound
- * */
-static function startSound(...t) {
-}
-
-/**
- * @access private
- */
-static function getSoundObject(...t) {
-}
-
-/**
- * Get the global volume
- * @param Object t: MovieClip for sound to act upon
- * @return Number: volume from 0 to 100 (0 is silent).
- */
-static function getVolume(...t) {
-}
-
-/**
- * Set the global volume.
- * @param Number v: linear volume from 0 to 100 (0 is silent).
- */
-static function setVolume(v, ...t) {
-}
-
-/**
- * Get the global pan.
- * @return Number: linear pan from -100 to +100 (left to right)
- */
-static function getPan(...t) {
-}
-
-/**
- * Set the global pan.
- * @param Number p: linear pan from -100 to +100 (left to right)
- */
-static function setPan(p, ...t) {
-}
-}
Modified: openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzSprite.as
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzSprite.as 2008-09-17 13:33:12 UTC (rev 11034)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/kernel/swf9/LzSprite.as 2008-09-17 13:45:08 UTC (rev 11035)
@@ -19,6 +19,12 @@
import flash.utils.*;
import mx.controls.Button;
import flash.net.URLRequest;
+ import flash.media.Sound;
+ import flash.media.SoundChannel;
+ import flash.media.SoundMixer;
+ import flash.media.SoundTransform;
+ import flash.media.SoundLoaderContext;
+ import flash.media.ID3Info;
}#
#passthrough {
@@ -58,7 +64,13 @@
var resourceCache:Array = null;
public var resourceLoaded:Boolean = false;
-
+
+ /* private */ static const soundLoaderContext:SoundLoaderContext = new SoundLoaderContext(1000, true);
+ /* private */ static const MP3_FPS:Number = 30;
+ /* private */ var sound:Sound = null;
+ /* private */ var soundChannel:SoundChannel = null;
+ /* private */ var soundLoading:Boolean = false;
+
//@field Boolean _setrescwidth: If true, the view does not set its
//resource to the width given in a call to
//<method>setAttribute</method>. By default, views do not scale their
@@ -97,8 +109,10 @@
if (owner == null) return;
if (isroot) {
this.isroot = true;
+ this.mouseEnabled = true;// @devnote: see LPP-6980
+ } else {
+ this.mouseEnabled = false;
}
- this.mouseEnabled = false;
}
public function init (v:Boolean = true):void {
@@ -152,7 +166,7 @@
*/
public function setResource (r:String):void {
if (this.resource == r) return;
- if ( r.indexOf('http:') == 0 || r.indexOf('https:') == 0){
+ if (r.indexOf('http:') == 0 || r.indexOf('https:') == 0) {
this.skiponload = false;
this.setSource( r );
return;
@@ -168,33 +182,56 @@
// frames: ["lps/components/lz/resources/focus/focus_bot_rt_shdw.png"], width: 9, height: 9};
- //Debug.write('setting resource', r);
- this.resource = r;
+ //Debug.write('setting resource', r);
- var res = LzResourceLibrary[r];
+ var res:Object = LzResourceLibrary[r];
if (! res) {
if ($debug) {
Debug.warn('Could not find resource', r);
}
return;
}
-
- this.resourcewidth = res.width;
- this.resourceheight = res.height;
- if (this.owner != null) {
- this.owner.resourceevent('totalframes', res.frames.length);
+
+ if (LzAsset.isBitmapAsset(r)
+ || LzAsset.isMovieClipAsset(r)
+ || LzAsset.isMovieClipLoaderAsset(r)) {
+ this.resourcewidth = res.width;
+ this.resourceheight = res.height;
+ if (this.owner != null) {
+ this.owner.resourceevent('totalframes', res.frames.length);
+ }
+ if (imgLoader) {
+ this.unload();
+ } else if (this.isaudio) {
+ // unload previous sound-resource
+ this.unloadSound();
+ }
+
+ if (this.resourceObj == null) {
+ this.createResourceBitmap()
+ }
+
+ this.resource = r;
+ // instantiate resource at frame 1
+ this.stop(1);
+ // send events, but skip onload
+ sendResourceLoad(true);
+ } else if (LzAsset.isSoundAsset(r)) {
+ // unload previous image-resource and sound-resource
+ this.unload();
+ this.resource = r;
+
+ this.sound = new res['assetclass']() as Sound;
+ this.owner.resourceevent('totalframes', Math.floor(this.sound.length * 0.001 * MP3_FPS));
+
+ // TODO: add condition on this
+ this.startPlay()
+
+ // send events, but skip onload
+ this.sendResourceLoad(true);
+ } else if ($debug) {
+ Debug.warn('Unhandled asset: ', LzAsset.getAssetType(r) + " " + r);
}
- if (imgLoader) {
- this.unload();
- }
- if (this.resourceObj == null) {
- this.createResourceBitmap()
- }
-
- // instantiate resource at frame 1
- this.stop(1);
- // send events, but skip onload
- sendResourceLoad(true);
}
@@ -205,31 +242,54 @@
o Loads and displays media from the specified url
o Uses the resourceload callback method when the resource finishes loading
*/
- public function setSource (url:String, cache = null, headers = null, filetype = null):void {
- if (url == null || url == 'null') {
- return;
- }
-
- this.resource = url;
- if (! imgLoader) {
- if (this.resourceObj) this.unload();
- imgLoader = new Loader();
- this.resourceObj = imgLoader;
- this.addChildAt(imgLoader, IMGDEPTH);
- var info:LoaderInfo = imgLoader.contentLoaderInfo;
- info.addEventListener(Event.INIT, loaderInitHandler);
- info.addEventListener(IOErrorEvent.IO_ERROR, loaderEventHandler);
+ public function setSource (url:String, cache:String = null, headers:String = null, filetype:String = null) :void {
+ if (url == null || url == 'null') {
+ return;
+ }
+
+ if (getFileType(url, filetype) == "mp3") {
+ // unload previous image-resource and sound-resource
+ this.unload();
+ this.resource = url;
+ this.loadSound(url);
} else {
- //TODO [20080911 anba] cancel current load?
- // imgLoader.close();
+ if (this.isaudio) {
+ // unload previous sound-resource
+ this.unloadSound();
+ }
+
+ if (! imgLoader) {
+ if (this.resourceObj) {
+ this.unload();
+ }
+ imgLoader = new Loader();
+ this.resourceObj = imgLoader;
+ this.addChildAt(imgLoader, IMGDEPTH);
+ var info:LoaderInfo = imgLoader.contentLoaderInfo;
+ info.addEventListener(Event.INIT, loaderInitHandler);
+ info.addEventListener(IOErrorEvent.IO_ERROR, loaderEventHandler);
+ } else {
+ //TODO [20080911 anba] cancel current load?
+ // imgLoader.close();
+ }
+
+ this.resource = url;
+ var res = this.resourceObj;
+ if (res) {
+ res.scaleX = res.scaleY = 1.0;
+ }
+ //Debug.write('sprite setsource load ', url);
+ imgLoader.load(new URLRequest(url));
}
-
- var res = this.resourceObj;
- if (res) {
- res.scaleX = res.scaleY = 1.0;
+ }
+
+ private function getFileType (url:String, filetype:String = null) :String {
+ if (filetype != null) {
+ return filetype.toLowerCase();
+ } else {
+ var si = url.lastIndexOf(".");
+ return si != -1 ? url.substring(si + 1).toLowerCase() : null;
}
- //Debug.write('sprite setsource load ', url);
- imgLoader.load(new URLRequest(url));
}
public function loaderInitHandler(event:Event):void {
@@ -277,7 +337,7 @@
}
} else if (event.type == ProgressEvent.PROGRESS) {
var ev:ProgressEvent = event as ProgressEvent;
- var lr = ev.bytesLoaded / ev.bytesTotal;
+ var lr:Number = ev.bytesLoaded / ev.bytesTotal;
if (! isNaN(lr)) {
this.owner.resourceevent('loadratio', lr);
}
@@ -290,8 +350,169 @@
}
}
+ /**
+ * <code>true</code> if a sound is attached to this sprite.
+ */
+ public function get isaudio () :Boolean {
+ return this.sound != null;
+ }
+
+ /**
+ * Load/Stream a sound from an URL.
+ */
+ private function loadSound (url:String) :void {
+ this.sound = new Sound();
+ this.sound.addEventListener(Event.OPEN, soundLoadHandler);
+ this.sound.addEventListener(Event.COMPLETE, soundLoadHandler);
+ this.sound.addEventListener(ProgressEvent.PROGRESS, soundLoadHandler);
+ this.sound.addEventListener(IOErrorEvent.IO_ERROR, soundLoadHandler);
+
+ this.sound.load(new URLRequest(url), LzSprite.soundLoaderContext);
+
+ // TODO: add condition on this
+ this.startPlay();
+ }
+
+ /**
+ * Stop current playback and unload sound
+ */
+ private function unloadSound () :void {
+ if (this.playing) {
+ // stop playing
+ this.stopPlay();
+ }
+ if (this.sound) {
+ if (this.soundLoading) {
+ // stop streaming sound
+ this.sound.close();
+ this.soundLoading = false;
+ }
+ this.sound = null;
+ }
+ }
+
+ /**
+ * Start sound playback and tracking
+ * @param Number frame: frame/secs to start at playing
+ * @param Boolean isFrame: if set to false, treat 'frame' as seconds
+ */
+ private function startPlay (frame:Number = 0, isFrame:Boolean = true) :void {
+ var pos:Number = (isFrame ? (frame / MP3_FPS) : frame) * 1000;
+
+ this.playing = true;
+ this.owner.playing = true;
+ this.soundChannel = this.sound.play(pos, 0, this.soundTransform);
+ this.addEventListener(Event.ENTER_FRAME, soundFrameHandler);
+ this.soundChannel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
+ }
+
+ /**
+ * Stop sound playback and tracking
+ * @return Number: the current frame when playback was stopped
+ */
+ private function stopPlay () :Number {
+ var frame:Number = Math.floor(this.soundChannel.position * 0.001 * MP3_FPS);
+
+ this.playing = false;
+ this.owner.playing = false;
+ this.removeEventListener(Event.ENTER_FRAME, soundFrameHandler);
+ this.soundChannel.stop();
+ this.soundChannel = null;
+
+ return frame;
+ }
+
+ /**
+ * Update play status
+ */
+ private function updatePlay (play:Boolean, framenumber:*, rel:Boolean) :void {
+ var fr:Number;
+ if (this.playing) {
+ // stop previous playback
+ fr = this.stopPlay();
+ } else {
+ // TODO: this.frame is initialized with 1, which
+ // means we currently skip 33ms at the beginning
+ fr = this.frame;
+ }
+
+ if (framenumber != null) {
+ framenumber += rel ? fr : 0;
+ } else {
+ framenumber = fr;
+ }
+
+ if (play) {
+ this.startPlay(framenumber);
+ } else {
+ this.frame = framenumber;
+ this.owner.resourceevent('frame', framenumber);
+ }
+ }
+
+ /**
+ * Progress sound loading
+ */
+ private function soundLoadHandler (event:Event) :void {
+ try {
+ if (event.type == Event.OPEN) {
+ this.soundLoading = true;
+ this.owner.resourceevent('loadratio', 0);
+ } else if (event.type == Event.COMPLETE) {
+ this.soundLoading = false;
+ this.owner.resourceevent('loadratio', 1);
+ this.owner.resourceevent('totalframes', Math.floor(this.sound.length * 0.001 * MP3_FPS));
+
+ // send events, including onload
+ this.sendResourceLoad();
+ } else if (event.type == ProgressEvent.PROGRESS) {
+ var ev:ProgressEvent = event as ProgressEvent;
+ var lr:Number = ev.bytesLoaded / ev.bytesTotal;
+ if (! isNaN(lr)) {
+ this.owner.resourceevent('loadratio', lr);
+ }
+ } else if (event.type == IOErrorEvent.IO_ERROR) {
+ this.soundLoading = false;
+ this.owner.resourceevent('loadratio', 0);
+ this.owner.resourceloaderror( (event as IOErrorEvent).text );
+ }
+ } catch (error:Error) {
+ trace(event.type + " " + error);
+ }
+ }
+
+ /**
+ * Track playback
+ */
+ private function soundFrameHandler (event:Event = null) :void {
+ // Event.ENTER_FRAME
+ var fr:Number = Math.floor(this.soundChannel.position * 0.001 * MP3_FPS);
+ this.frame = fr;
+ this.owner.resourceevent('frame', fr);
+
+ var tfr:Number = Math.floor(this.sound.length * 0.001 * MP3_FPS);
+ this.owner.resourceevent('totalframes', tfr);
+ }
+
+ /**
+ * Sound complete
+ */
+ private function soundCompleteHandler (event:Event) :void {
+ // Event.SOUND_COMPLETE
+ if (this.playing) {
+ // call manually to update 'frame'
+ this.soundFrameHandler();
+ // SoundChannel.position does not stop exactly at Sound.length,
+ // there are a few ms difference between both values.
+ // So instead of comparing 'frame' == 'totalframes',
+ // we'll send the 'lastframe'-event when playback stopped.
+ this.owner.resourceevent('lastframe', null, true);
+ this.stopPlay();
+ }
+ }
+
//// Mouse event trampoline
- public function attachMouseEvents(dobj:DisplayObject) {
+ public function attachMouseEvents(dobj:DisplayObject) :void {
dobj.addEventListener(MouseEvent.CLICK, __mouseEvent, false);
dobj.addEventListener(MouseEvent.DOUBLE_CLICK, handleMouse_DOUBLE_CLICK, false);
dobj.addEventListener(MouseEvent.MOUSE_DOWN, __mouseEvent, false);
@@ -300,7 +521,7 @@
dobj.addEventListener(MouseEvent.MOUSE_OUT, __mouseEvent, false);
}
- public function removeMouseEvents(dobj:DisplayObject) {
+ public function removeMouseEvents(dobj:DisplayObject) :void {
dobj.removeEventListener(MouseEvent.CLICK, __mouseEvent, false);
dobj.removeEventListener(MouseEvent.DOUBLE_CLICK, handleMouse_DOUBLE_CLICK, false);
dobj.removeEventListener(MouseEvent.MOUSE_DOWN, __mouseEvent, false);
@@ -309,13 +530,13 @@
dobj.removeEventListener(MouseEvent.MOUSE_OUT, __mouseEvent, false);
}
- public function handleMouse_DOUBLE_CLICK (event:MouseEvent) {
+ public function handleMouse_DOUBLE_CLICK (event:MouseEvent) :void {
LzMouseKernel.__sendEvent( owner, 'ondblclick');
event.stopPropagation();
}
// called by LzMouseKernel when mouse goes up on another sprite
- public function __globalmouseup( e:MouseEvent ){
+ public function __globalmouseup( e:MouseEvent ) :void {
if (this.__mousedown) {
this.__mouseEvent(e);
this.__mouseEvent(new MouseEvent('mouseupoutside'));
@@ -323,7 +544,7 @@
LzMouseKernel.__lastMouseDown = null;
}
- public function __mouseEvent( e:MouseEvent ){
+ public function __mouseEvent( e:MouseEvent ) :void {
var skipevent = false;
var eventname = 'on' + e.type.toLowerCase();
@@ -381,7 +602,7 @@
this.clickable = c;
this.buttonMode = c;
this.tabEnabled = false;
- this.mouseEnabled = c;
+ this.mouseEnabled = c || this.isroot;// @devnote: see LPP-6980
attachMouseEvents(this);
var cb:SimpleButton = this.clickbutton;
//trace('sprite setClickable' , c, 'cb',cb);
@@ -461,7 +682,6 @@
}
-
/** setWidth( Number:width )
o Sets the sprite to the specified width
*/
@@ -569,9 +789,16 @@
o Plays a multiframe resource starting at the specified framenumber
o Plays from the current frame if framenumber is null
*/
- public function play( framenumber:Number = 1, rel:Boolean = false ):void {
- // TODO [hqm 2008-04] what to do about playing movies?
- stop(framenumber);
+ public function play (framenumber:* = null, rel:Boolean = false) :void {
+ if (! this.isaudio) {
+ // TODO [hqm 2008-04] what to do about playing movies?
+ stop(framenumber);
+ } else {
+ // audio-resource is attached
+ this.updatePlay(true, framenumber, rel);
+
+ this.owner.resourceevent('play', null, true);
+ }
}
@@ -579,57 +806,69 @@
o Stops a multiframe resource at the specified framenumber
o Stops at the current frame if framenumber is null
*/
- public function stop( fn:Number = 1, rel:Boolean = false ):void {
- if (this.resource == null || imgLoader) {
- return;
- }
- var resinfo = LzResourceLibrary[this.resource];
-
- // Frames are one based not zero based
- var frames = resinfo.frames;
- if (fn > frames.length) {
- fn = frames.length
- }
- this.frame = fn;
- var framenumber = fn - 1;
-
- var assetclass;
- // single frame resources get an entry in LzResourceLibrary which has
- // 'assetclass' pointing to the resource Class object.
- if (resinfo.assetclass is Class) {
- assetclass = resinfo.assetclass;
+ public function stop (fn:* = null, rel:Boolean = false) :void {
+ if (! this.isaudio) {
+ if (this.resource == null || imgLoader) {
+ return;
+ }
+
+ var resinfo = LzResourceLibrary[this.resource];
+
+ // Frames are one based not zero based
+ var frames = resinfo.frames;
+ if (fn == null) {
+ fn = 1;
+ }
+ if (fn > frames.length) {
+ fn = frames.length;
+ }
+ this.frame = fn;
+ var framenumber = fn - 1;
+
+ var assetclass;
+ // single frame resources get an entry in LzResourceLibrary which has
+ // 'assetclass' pointing to the resource Class object.
+ if (resinfo.assetclass is Class) {
+ assetclass = resinfo.assetclass;
+ } else {
+ // Multiframe resources have an array of Class objects in frames[]
+ assetclass = frames[framenumber];
+ }
+
+ if (! assetclass) return;
+ if (this.resourceCache == null) {
+ this.resourceCache = [];
+ }
+ var asset:DisplayObject = this.resourceCache[framenumber];
+ if (asset == null) {
+ //Debug.write('CACHE MISS, new ',assetclass);
+ asset = new assetclass();
+ asset.scaleX = 1.0
+ asset.scaleY = 1.0;
+ this.resourceCache[framenumber] = asset;
+ }
+
+ var oRect:Rectangle = asset.getBounds( asset );
+ if (oRect.width == 0 || oRect.height == 0) {
+ // it can take a while for new resources to show up. Call back until we have a valid size.
+ setTimeout(this.__resetframe, 50);
+ return;
+ }
+
+ var res = this.resourceObj;
+ var rect = new Rectangle(0, 0, this.resourcewidth, this.resourceheight);
+ res.bitmapData.fillRect(rect, 0x00000000);
+ copyBitmap(asset, this.resourcewidth, this.resourceheight, res.bitmapData);
+ //Debug.write('set resource to', asset, oRect);
+
+ this.applyStretchResource();
} else {
- // Multiframe resources have an array of Class objects in frames[]
- assetclass = frames[framenumber];
+ // audio-resource is attached
+ var p:Boolean = this.playing;
+ this.updatePlay(false, fn, rel);
+
+ if (p) this.owner.resourceevent('stop', null, true);
}
-
- if (! assetclass) return;
- if (this.resourceCache == null) {
- this.resourceCache = [];
- }
- var asset:DisplayObject = this.resourceCache[framenumber];
- if (asset == null) {
- //Debug.write('CACHE MISS, new ',assetclass);
- asset = new assetclass();
- asset.scaleX = 1.0
- asset.scaleY = 1.0;
- this.resourceCache[framenumber] = asset;
- }
-
- var oRect:Rectangle = asset.getBounds( asset );
- if (oRect.width == 0 || oRect.height == 0) {
- // it can take a while for new resources to show up. Call back until we have a valid size.
- setTimeout(this.__resetframe, 50);
- return;
- }
-
- var res = this.resourceObj;
- var rect = new Rectangle(0, 0, this.resourcewidth, this.resourceheight);
- res.bitmapData.fillRect(rect, 0x00000000);
- copyBitmap(asset, this.resourcewidth, this.resourceheight, res.bitmapData);
- //Debug.write('set resource to', asset, oRect);
-
- this.applyStretchResource();
}
public function __resetframe():void {
@@ -745,9 +984,10 @@
o if recursive is true, the sprite destroys all its children as well
*/
public function destroy( ):void {
- //PBR
- if (parent)
- parent.removeChild(this);
+ //PBR
+ if (parent) {
+ parent.removeChild(this);
+ }
}
@@ -834,10 +1074,11 @@
public function unload() {
if (this.resourceObj) this.removeChild(this.resourceObj);
+ if (this.isaudio) this.unloadSound();
// clear out cached values
this.lastreswidth = this.lastresheight = this.resourcewidth = this.resourceheight = 0;
this.resource = null;
- imgLoader = null;
+ this.imgLoader = null;
this.resourceObj = null;
}
@@ -871,11 +1112,10 @@
* Install menu items for the right-mouse-button
* @param LzContextMenu cmenu: LzContextMenu to install on this view
*/
- function setContextMenu ( lzmenu ){
+ function setContextMenu ( lzmenu ){
if (lzmenu == null) {
this.__contextmenu = null;
} else {
- // For back compatibility, we accept either LzContextMenu or (Flash primitive) ContextMenu
this.__contextmenu = lzmenu;
var cmenu:ContextMenu = lzmenu.kernel.__LZcontextMenu();
@@ -883,6 +1123,8 @@
// where it checks for a resource or bgcolor sprite, in order to make the clickable region
// match what the user expects.
+ // TODO: [20080914 anba] blocked by LPP-6980
+
// "contextMenu" is a swf9 property on flash.display.Sprite
this.contextMenu = cmenu;
}
@@ -890,9 +1132,10 @@
function setDefaultContextMenu ( cmenu ){
if (cmenu != null) {
-// LPP-5868
-// Generates: Error #2071: The Stage class does not implement this property or method
-// LFCApplication.stage.contextMenu = cmenu.kernel.__LZcontextMenu();
+ // even though Stage extends flash.display.InterativeObject, you cannot attach
+ // a context-menu to it, instead we attach the context-menu to application-sprite
+ // LFCApplication.stage.contextMenu = cmenu.kernel.__LZcontextMenu();
+ LFCApplication._sprite.contextMenu = cmenu.kernel.__LZcontextMenu();
}
}
@@ -948,42 +1191,97 @@
LzMouseKernel.restoreCursorLocal();
}
- function setVolume (v) {
- trace('setVolume not currently implemented in swf9.');
+ function setVolume (v:Number) :void {
+ LzAudioKernel.setVolume(v, this);
}
- function getVolume () {
- trace('getVolume not currently implemented in swf9.');
+ function getVolume () :Number {
+ return LzAudioKernel.getVolume(this);
}
- function setPan (v) {
- trace('setPan not currently implemented in swf9.');
+ function setPan (p:Number) :void {
+ LzAudioKernel.setPan(p, this);
}
- function getPan () {
- trace('getPan not currently implemented in swf9.');
+ function getPan () :Number {
+ return LzAudioKernel.getPan(this);
}
+
+ /**
+ * @param Number secs:
+ * @param Boolean playing:
+ */
+ function seek (secs:Number, doplay:Boolean) :void {
+ if (this.isaudio) {
+ var pos:Number = Math.max(this.getCurrentTime() + secs, 0);
+ if (this.playing) {
+ this.stopPlay();
+ }
+ if (doplay) {
+ this.startPlay(pos, false);
+ } else {
+ var fr:Number = Math.floor(pos * MP3_FPS);
+ this.frame = fr;
+ this.owner.resourceevent('frame', fr);
+ }
+ }
+ }
+
+ /**
+ * @return Number: time elapsed (in seconds)
+ */
+ function getCurrentTime () :Number {
+ if (this.isaudio) {
+ if (this.playing) {
+ // use SoundChannel if possible, it is more accurate
+ return this.soundChannel.position * 0.001;
+ } else {
+ return (this.frame / MP3_FPS);
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @return Number: length of the current sound (in seconds)
+ */
+ function getTotalTime () :Number {
+ return this.isaudio ? this.sound.length * 0.001 : 0;
+ }
+
+ /**
+ * @return ID3Info: id3-info of the current sound
+ */
+ function getID3 () :ID3Info {
+ return this.isaudio ? this.sound.id3 : null;
+ }
/**
-
- */
+ *
+ */
function setShowHandCursor ( s:* ){
this.showhandcursor = s;
}
function setAAActive(s) {
+ trace('LzSprite.setAAActive not yet implemented');
}
function setAAName(s) {
+ trace('LzSprite.setAAName not yet implemented');
}
function setAADescription(s) {
+ trace('LzSprite.setAADescription not yet implemented');
}
function setAATabIndex(s) {
+ trace('LzSprite.setAATabIndex not yet implemented');
}
function setAASilent(s) {
+ trace('LzSprite.setAASilent not yet implemented');
}
function updateResourceSize(skipsend = null){
Modified: openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloView.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloView.lzs 2008-09-17 13:33:12 UTC (rev 11034)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloView.lzs 2008-09-17 13:45:08 UTC (rev 11035)
@@ -2714,7 +2714,11 @@
*
* @param String source: The URL from which to load the resource for this view.
* @param String cache: If set, controls caching behavior. Choices are
- * <code>none</code> , <code>clientonly</code> , <code>serveronly</code> , <code>both</code> (the default for Flash). DHTML applications cache media on the client only. DHTML supports the <code>memorycache</code> option which enables in-memory resource caching. This enhances performance when swapping resources quickly, and is used internally for multi-frame resources. Media loaded with <code>memorycache</code> will remain in memory until the page is unloaded or unload() is called.
+ * <code>none</code> , <code>clientonly</code> , <code>serveronly</code> , <code>both</code> (the default for Flash).
+ * DHTML applications cache media on the client only. DHTML supports the <code>memorycache</code> option which
+ * enables in-memory resource caching. This enhances performance when swapping resources quickly, and is used
+ * internally for multi-frame resources. Media loaded with <code>memorycache</code> will remain in memory until
+ * the page is unloaded or unload() is called.
* @param String headers: Headers to send with the request, if any.
* @param String filetype: Filetype, e.g. 'mp3' or 'jpg'. If not specified, it will be derived from the URL.
*/
@@ -2781,7 +2785,7 @@
/** @access private */
-function $lzc$set_play(b) {
+function $lzc$set_play (b:Boolean) :void {
if ( b ) {
this.play();
} else {
@@ -2794,7 +2798,7 @@
* @param Boolean b: If true, starts playing, otherwise stops
* @deprecated Use setAttribute('play', ...) instead.
*/
-function setPlay (b){
+function setPlay (b:Boolean) :void {
if ($debug) Debug.deprecated(this, arguments.callee, this.setAttribute);
this.$lzc$set_play(b);
}
@@ -2803,9 +2807,8 @@
* Get a reference to the control mc - may be overridden by loader
* @access private
*/
-function getMCRef () {
+function getMCRef () :* {
return this.sprite.getMCRef();
- //return this.__LZmovieClipRef;
}
/**
@@ -2819,7 +2822,7 @@
* begin playing at the current frame.
* @param Boolean rel: If true, f is relative to the current frame. Otherwise f is relative to the beginning of the resource.
*/
-function play (f = null, rel = null){
+function play (f/*:Number?*/ = null, rel:Boolean = false) :void {
this.sprite.play(f, rel);
}
@@ -2829,7 +2832,7 @@
* stop at the current frame.
* @param Boolean rel: If true, f is relative to the current frame. Otherwise it is relative to the start position of the resource.
*/
-function stop (f = null, rel = null){
+function stop (f/*:Number?*/ = null, rel:Boolean = false) :void {
this.sprite.stop(f, rel);
}
@@ -2837,7 +2840,7 @@
* Set the volume of the attached resource
* @param Integer v: A number from 0 to 100 representing a volume level
*/
-function setVolume (v) {
+function setVolume (v:Number) :void {
if (this.capabilities.audio) {
this.sprite.setVolume(v);
} else if ($debug) {
@@ -2849,7 +2852,7 @@
* Get the volume of the attached resource
* @return Integer: A number from 0 to 100 representing a volume level
*/
-function getVolume () {
+function getVolume () :Number {
if (this.capabilities.audio) {
return this.sprite.getVolume();
} else if ($debug) {
@@ -2861,7 +2864,7 @@
* Set the pan of the attached resource
* @param Integer p: A number from -100 to 100 representing a pan level
*/
-function setPan (p) {
+function setPan (p:Number) :void {
if (this.capabilities.audio) {
this.sprite.setPan(p);
} else if ($debug) {
@@ -2873,7 +2876,7 @@
* Get the pan of the attached resource
* @return Integer: A number from -100 to 100 representing a pan level
*/
-function getPan () {
+function getPan () :Number {
if (this.capabilities.audio) {
return this.sprite.getPan();
} else if ($debug) {
@@ -2897,16 +2900,16 @@
*
* @param Integer secs: Number of seconds to skip forward or backward (if negative)
*/
-function seek ( secs ){
- var m = this.getMCRef();
+function seek (secs:Number) :void {
+ var m:* = this.getMCRef();
if (m.isaudio == true) {
m.seek(secs, this.playing)
} else {
- var f = secs*canvas.framerate;
- if ( this.playing ) {
- this.play( f , true);
+ var f:Number = secs * canvas.framerate;
+ if (this.playing) {
+ this.play(f, true);
} else {
- this.stop( f , true);
+ this.stop(f, true);
}
}
}
@@ -2916,8 +2919,8 @@
* @return Number: The number of seconds of media between the current frame and the
* first frame
*/
-function getCurrentTime ( ){
- var m = this.getMCRef();
+function getCurrentTime () :Number {
+ var m:* = this.getMCRef();
if (m.isaudio == true) {
return m.getCurrentTime();
} else {
@@ -2926,8 +2929,8 @@
}
/** @access private */
-function $lzc$getCurrentTime_dependencies ( who, self ){
- return [ self , "frame" ];
+function $lzc$getCurrentTime_dependencies ( who, self ) :Array {
+ return [ self, "frame" ];
}
@@ -2935,8 +2938,8 @@
* Returns the total amount of time the resource would take to play.
* @return Number: Seconds of media controlled by this view.
*/
-function getTotalTime ( ){
- var m = this.getMCRef();
+function getTotalTime () :Number {
+ var m:* = this.getMCRef();
if (m.isaudio == true) {
return m.getTotalTime();
} else {
@@ -2944,8 +2947,8 @@
}
}
/** @access private */
-function $lzc$getTotalTime_dependencies ( who, self ){
- return [ self , "load" ];
+function $lzc$getTotalTime_dependencies ( who, self ) :Array {
+ return [ self, "load" ];
}
/**
@@ -2953,8 +2956,8 @@
* with proxy == false;
* @return Object: Object containind id3 tag data, if available.
*/
-function getID3 ( ){
- var m = this.getMCRef();
+function getID3 () :Object {
+ var m:* = this.getMCRef();
if (m.isaudio == true) {
return m.getID3();
}
Modified: openlaszlo/trunk/examples/music/music.lzx
===================================================================
--- openlaszlo/trunk/examples/music/music.lzx 2008-09-17 13:33:12 UTC (rev 11034)
+++ openlaszlo/trunk/examples/music/music.lzx 2008-09-17 13:45:08 UTC (rev 11035)
@@ -14,61 +14,83 @@
<canvas bgcolor="#EAEAEA" width="640" height="140" >
- <view id="audioplayer" play="true" y="40"
- resource="http:music.mp3"
- oninit="new LzDelegate( this, 'lastoff', this, 'onlastframe');
- new LzDelegate( this, 'stopped', this, 'onstop')">
- <attribute name="vol" value="100" />
+ <view id="audioplayer" play="true" y="40" >
+
+ <handler name="oninit" >
+ if ($swf8) {
+ // load mp3 unproxied, so we can get the ID3-Tag
+ this.setProxyPolicy(function (url) {return false;});
+ }
+ this.setAttribute("resource", "http:music.mp3");
+ </handler>
+
+ <handler name="onload" >
+ var info:String;
+ var id3:Object = this.getID3();
+ if (!! id3) {
+ info = "Song: " + id3.TIT2 + "\nArtist: " + id3.TPE1 + "\nAlbum: " + id3.TALB;
+ } else {
+ info = "no id3 info available"
+ }
+ this.id3info.setAttribute("text", info );
+ </handler>
+
+ <handler name="onlastframe" >
+ Debug.write("Got last");
+ </handler>
+
+ <handler name="onstop" >
+ Debug.write("Got stopped");
+ </handler>
+
+ <simplelayout axis="x" spacing="10"/>
+
<view>
<simplelayout axis="x" spacing="-1" />
<button width="40" onclick="audioplayer.play(1);" >
- <view resource="icons/rewind_all.png" y="6" align="center"/>
+ <view resource="icons/rewind_all.png" y="7" align="center"/>
</button>
- <button width="40" onclick="audioplayer.seek( -2 );" >
- <view resource="icons/rewind.png" y="6" align="center"/>
+ <button width="40" onclick="audioplayer.seek(-2);" >
+ <view resource="icons/rewind.png" y="7" align="center"/>
</button>
<button width="40" onclick="audioplayer.stop()" >
- <view resource="icons/stop.png" y="6" align="center"/>
+ <view resource="icons/stop.png" y="7" align="center"/>
</button>
<button width="40" onclick="audioplayer.play()" >
- <view resource="icons/play.png" y="5" align="center"/>
+ <view resource="icons/play.png" y="6" align="center"/>
</button>
- <button width="40" onclick="audioplayer.seek( 2 );" >
- <view resource="icons/fastfwd.png" y="6" align="center"/>
+ <button width="40" onclick="audioplayer.seek(2);" >
+ <view resource="icons/fastfwd.png" y="7" align="center"/>
</button>
</view>
<view>
<simplelayout axis="x" spacing="-1" />
- <button width="40" onclick="audioplayer.setVolume( audioplayer.getVolume() -10)">
- <view resource="icons/quieter.png" y="5" align="center"/>
- </button>
- <button width="40" onclick="audioplayer.setVolume( audioplayer.getVolume() +10)">
- <view resource="icons/louder.png" y="5" align="center"/>
- </button>
- <button width="40" onclick="audioplayer.setPan( audioplayer.getPan() - 10 );" >
- <view resource="icons/pan_left.png" y="5" align="center"/>
+ <button width="40" onclick="audioplayer.setVolume(audioplayer.getVolume() - 10)">
+ <view resource="icons/quieter.png" y="7" align="center"/>
</button>
- <button width="40" onclick="audioplayer.setPan( audioplayer.getPan() + 10 );" >
- <view resource="icons/pan_right.png" y="5" align="center"/>
+ <button width="40" onclick="audioplayer.setVolume(audioplayer.getVolume() + 10)">
+ <view resource="icons/louder.png" y="7" align="center"/>
</button>
+ <button width="40" onclick="audioplayer.setPan(audioplayer.getPan() - 10);" >
+ <view resource="icons/pan_left.png" y="7" align="center"/>
+ </button>
+ <button width="40" onclick="audioplayer.setPan(audioplayer.getPan() + 10);" >
+ <view resource="icons/pan_right.png" y="7" align="center"/>
+ </button>
</view>
- <simplelayout axis="x" spacing="10"/>
- <method name="lastoff" args="v">
- Debug.write("Got last");
- </method>
- <method name="stopped" args="v">
- Debug.write("Got stopped");
- </method>
+ <text name="id3info" y="-10" height="80" width="200"/>
+
<view name="background" bgcolor="black" width="202" height="15" y="50" options="ignorelayout;">
<view name="loadbar" resource="icons/audio_scrubtrack.png"
- width="${audioplayer.loadperc * 200}" >
+ width="${audioplayer.loadratio * 200}" >
<view name="playbar" resource="icons/audio_scrubber.png" y="1"
x="${Math.round(180 * audioplayer.frame/audioplayer.totalframes)}" />
</view>
</view>
</view>
+
</canvas>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2001-2008 Laszlo Systems, Inc. All Rights Reserved. *
Modified: openlaszlo/trunk/examples/music/music.mp3
===================================================================
(Binary files differ)
More information about the Laszlo-checkins
mailing list