- Intro Screen - Share buttons (Twitter/Facebook) - SoundManager - Add audio files - Some CleanUp
598 lines
No EOL
26 KiB
ActionScript
598 lines
No EOL
26 KiB
ActionScript
/*
|
|
SoundManager 2: Javascript Sound for the Web
|
|
----------------------------------------------
|
|
http://schillmania.com/projects/soundmanager2/
|
|
|
|
Copyright (c) 2007, Scott Schiller. All rights reserved.
|
|
Code licensed under the BSD License:
|
|
http://www.schillmania.com/projects/soundmanager2/license.txt
|
|
|
|
Flash 9 / ActionScript 3 version
|
|
*/
|
|
|
|
package {
|
|
|
|
import flash.external.*;
|
|
import flash.events.*;
|
|
import flash.media.Sound;
|
|
import flash.media.SoundChannel;
|
|
import flash.media.SoundLoaderContext;
|
|
import flash.media.SoundTransform;
|
|
import flash.media.SoundMixer;
|
|
import flash.net.URLRequest;
|
|
import flash.utils.ByteArray;
|
|
import flash.utils.getTimer;
|
|
import flash.net.NetConnection;
|
|
import flash.net.NetStream;
|
|
|
|
public class SoundManager2_SMSound_AS3 extends Sound {
|
|
|
|
public var sm: SoundManager2_AS3 = null;
|
|
// externalInterface references (for Javascript callbacks)
|
|
public var baseJSController: String = "soundManager";
|
|
public var baseJSObject: String = baseJSController + ".sounds";
|
|
public var soundChannel: SoundChannel = new SoundChannel();
|
|
public var urlRequest: URLRequest;
|
|
public var soundLoaderContext: SoundLoaderContext;
|
|
public var waveformData: ByteArray = new ByteArray();
|
|
public var waveformDataArray: Array = [];
|
|
public var eqData: ByteArray = new ByteArray();
|
|
public var eqDataArray: Array = [];
|
|
public var usePeakData: Boolean = false;
|
|
public var useWaveformData: Boolean = false;
|
|
public var useEQData: Boolean = false;
|
|
public var sID: String;
|
|
public var sURL: String;
|
|
public var justBeforeFinishOffset: int;
|
|
public var didJustBeforeFinish: Boolean;
|
|
public var didFinish: Boolean;
|
|
public var loaded: Boolean;
|
|
public var connected: Boolean;
|
|
public var failed: Boolean;
|
|
public var paused: Boolean;
|
|
public var finished: Boolean;
|
|
public var duration: Number;
|
|
public var handledDataError: Boolean = false;
|
|
public var ignoreDataError: Boolean = false;
|
|
public var autoPlay: Boolean = false;
|
|
public var autoLoad: Boolean = false;
|
|
public var pauseOnBufferFull: Boolean = true;
|
|
public var loops: Number = 1;
|
|
public var lastValues: Object = {
|
|
bytes: 0,
|
|
position: 0,
|
|
volume: 100,
|
|
pan: 0,
|
|
loops: 1,
|
|
leftPeak: 0,
|
|
rightPeak: 0,
|
|
waveformDataArray: null,
|
|
eqDataArray: null,
|
|
isBuffering: null,
|
|
bufferLength: 0
|
|
};
|
|
public var didLoad: Boolean = false;
|
|
public var useEvents: Boolean = false;
|
|
public var sound: Sound = new Sound();
|
|
|
|
public var cc: Object;
|
|
public var nc: NetConnection;
|
|
public var ns: NetStream = null;
|
|
public var st: SoundTransform;
|
|
public var useNetstream: Boolean;
|
|
public var bufferTime: Number = 3; // previously 0.1
|
|
public var bufferTimes: Array; // an array of integers (for specifying multiple buffers)
|
|
public var lastNetStatus: String = null;
|
|
public var serverUrl: String = null;
|
|
|
|
public var start_time: Number;
|
|
public var connect_time: Number;
|
|
public var play_time: Number;
|
|
public var recordStats: Boolean = false;
|
|
public var checkPolicyFile:Boolean = false;
|
|
|
|
public function SoundManager2_SMSound_AS3(oSoundManager: SoundManager2_AS3, sIDArg: String = null, sURLArg: String = null, usePeakData: Boolean = false, useWaveformData: Boolean = false, useEQData: Boolean = false, useNetstreamArg: Boolean = false, netStreamBufferTime: Number = 1, serverUrl: String = null, duration: Number = 0, autoPlay: Boolean = false, useEvents: Boolean = false, bufferTimes: Array = null, recordStats: Boolean = false, autoLoad: Boolean = false, checkPolicyFile: Boolean = false) {
|
|
this.sm = oSoundManager;
|
|
this.sID = sIDArg;
|
|
this.sURL = sURLArg;
|
|
this.usePeakData = usePeakData;
|
|
this.useWaveformData = useWaveformData;
|
|
this.useEQData = useEQData;
|
|
this.urlRequest = new URLRequest(sURLArg);
|
|
this.justBeforeFinishOffset = 0;
|
|
this.didJustBeforeFinish = false;
|
|
this.didFinish = false; // non-MP3 formats only
|
|
this.loaded = false;
|
|
this.connected = false;
|
|
this.failed = false;
|
|
this.finished = false;
|
|
this.soundChannel = null;
|
|
this.lastNetStatus = null;
|
|
this.useNetstream = useNetstreamArg;
|
|
this.serverUrl = serverUrl;
|
|
this.duration = duration;
|
|
this.recordStats = recordStats;
|
|
this.useEvents = useEvents;
|
|
this.autoLoad = autoLoad;
|
|
if (netStreamBufferTime) {
|
|
this.bufferTime = netStreamBufferTime;
|
|
}
|
|
// Use bufferTimes instead of bufferTime
|
|
if (bufferTimes !== null) {
|
|
this.bufferTimes = bufferTimes;
|
|
} else {
|
|
this.bufferTimes = [this.bufferTime];
|
|
}
|
|
setAutoPlay(autoPlay);
|
|
if (recordStats) {
|
|
this.start_time = getTimer();
|
|
}
|
|
this.checkPolicyFile = checkPolicyFile;
|
|
|
|
writeDebug('SoundManager2_SMSound_AS3: Got duration: '+duration+', autoPlay: '+autoPlay);
|
|
|
|
if (this.useNetstream) {
|
|
|
|
this.cc = new Object();
|
|
this.nc = new NetConnection();
|
|
|
|
// Handle FMS bandwidth check callback.
|
|
// @see onBWDone
|
|
// @see http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html
|
|
// @see http://www.johncblandii.com/index.php/2007/12/fms-a-quick-fix-for-missing-onbwdone-onfcsubscribe-etc.html
|
|
this.nc.client = this;
|
|
|
|
// TODO: security/IO error handling
|
|
// this.nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, doSecurityError);
|
|
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
|
|
|
|
if (this.serverUrl != null) {
|
|
writeDebug('SoundManager2_SMSound_AS3: NetConnection: connecting to server ' + this.serverUrl + '...');
|
|
}
|
|
this.nc.connect(serverUrl);
|
|
} else {
|
|
this.connect_time = this.start_time;
|
|
this.connected = true;
|
|
}
|
|
|
|
}
|
|
|
|
public function netStatusHandler(event:NetStatusEvent):void {
|
|
|
|
if (this.useEvents) {
|
|
writeDebug('netStatusHandler: '+event.info.code);
|
|
}
|
|
|
|
switch (event.info.code) {
|
|
|
|
case "NetConnection.Connect.Success":
|
|
writeDebug('NetConnection: connected');
|
|
try {
|
|
this.ns = new NetStream(this.nc);
|
|
this.ns.checkPolicyFile = this.checkPolicyFile;
|
|
// bufferTime reference: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/net/NetStream.html#bufferTime
|
|
this.ns.bufferTime = getStartBuffer(); // set to 0.1 or higher. 0 is reported to cause playback issues with static files.
|
|
this.st = new SoundTransform();
|
|
this.cc.onMetaData = this.metaDataHandler;
|
|
this.ns.client = this.cc;
|
|
this.ns.receiveAudio(true);
|
|
this.addNetstreamEvents();
|
|
|
|
this.connected = true;
|
|
if (recordStats) {
|
|
this.recordConnectTime();
|
|
}
|
|
if (this.useEvents) {
|
|
writeDebug('firing _onconnect for '+this.sID);
|
|
ExternalInterface.call(this.sm.baseJSObject + "['" + this.sID + "']._onconnect", 1);
|
|
}
|
|
} catch(e: Error) {
|
|
this.failed = true;
|
|
writeDebug('netStream error: ' + e.toString());
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection failed!', event.info.level, event.info.code);
|
|
}
|
|
break;
|
|
|
|
case "NetStream.Play.StreamNotFound":
|
|
this.failed = true;
|
|
writeDebug("NetConnection: Stream not found!");
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Stream not found!', event.info.level, event.info.code);
|
|
break;
|
|
|
|
// This is triggered when the sound loses the connection with the server.
|
|
// In some cases one could just try to reconnect to the server and resume playback.
|
|
// However for streams protected by expiring tokens, I don't think that will work.
|
|
//
|
|
// Flash says that this is not an error code, but a status code...
|
|
// should this call the onFailure handler?
|
|
case "NetConnection.Connect.Closed":
|
|
this.failed = true;
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection closed!', event.info.level, event.info.code);
|
|
writeDebug("NetConnection: Connection closed!");
|
|
break;
|
|
|
|
// Couldn't establish a connection with the server. Attempts to connect to the server
|
|
// can also fail if the permissible number of socket connections on either the client
|
|
// or the server computer is at its limit. This also happens when the internet
|
|
// connection is lost.
|
|
case "NetConnection.Connect.Failed":
|
|
this.failed = true;
|
|
writeDebug("NetConnection: Connection failed! Lost internet connection? Try again... Description: " + event.info.description);
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection failed!', event.info.level, event.info.code);
|
|
break;
|
|
|
|
// A change has occurred to the network status. This could mean that the network
|
|
// connection is back, or it could mean that it has been lost...just try to resume
|
|
// playback.
|
|
|
|
// KJV: Can't use this yet because by the time you get your connection back the
|
|
// song has reached it's maximum retries, so it doesn't retry again. We need
|
|
// a new _ondisconnect handler.
|
|
//case "NetConnection.Connect.NetworkChange":
|
|
// this.failed = true;
|
|
// writeDebug("NetConnection: Network connection status changed");
|
|
// ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Reconnecting...');
|
|
// break;
|
|
|
|
// Consider everything else a failure...
|
|
default:
|
|
this.failed = true;
|
|
writeDebug("NetConnection: got unhandled code '" + event.info.code + "'! Description: " + event.info.description);
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", '', event.info.level, event.info.code);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Set the buffer size on the current NetSream instance to <tt>buffer</tt> secs
|
|
// Only set the buffer if it's different to the current buffer.
|
|
public function setBuffer(buffer: int) : void {
|
|
if (buffer != this.ns.bufferTime) {
|
|
this.ns.bufferTime = buffer;
|
|
writeDebug('set buffer to '+this.ns.bufferTime+' secs');
|
|
}
|
|
}
|
|
|
|
// Return the size of the starting buffer.
|
|
public function getStartBuffer() : int {
|
|
return this.bufferTimes[0];
|
|
}
|
|
|
|
// Return the size of the next buffer, given the size of the current buffer.
|
|
// If there are no more buffers, returns the current buffer size.
|
|
public function getNextBuffer(current_buffer: int) : int {
|
|
var i: int = bufferTimes.indexOf(current_buffer);
|
|
if (i == -1) {
|
|
// Couldn't find the buffer, start from the start buffer size
|
|
return getStartBuffer();
|
|
} else if (i + 1 >= bufferTimes.length) {
|
|
// Last (or only) buffer, keep the current buffer
|
|
return current_buffer;
|
|
} else {
|
|
return this.bufferTimes[i+1];
|
|
}
|
|
}
|
|
|
|
public function writeDebug (s: String, bTimestamp: Boolean = false) : Boolean {
|
|
return this.sm.writeDebug (s, bTimestamp); // defined in main SM object
|
|
}
|
|
|
|
public function metaDataHandler(infoObject: Object) : void {
|
|
if (sm.debugEnabled) {
|
|
var data:String = new String();
|
|
for (var prop:* in infoObject) {
|
|
data += prop+': '+infoObject[prop]+' \n';
|
|
}
|
|
writeDebug('Metadata: '+data);
|
|
}
|
|
this.duration = infoObject.duration * 1000;
|
|
if (!this.loaded) {
|
|
// writeDebug('not loaded yet: '+this.ns.bytesLoaded+', '+this.ns.bytesTotal+', '+infoObject.duration*1000);
|
|
// TODO: investigate loaded/total values
|
|
// ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.ns.bytesLoaded, this.ns.bytesTotal, infoObject.duration*1000);
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.bytesLoaded, this.bytesTotal, (infoObject.duration || this.duration))
|
|
}
|
|
// null this out for the duration of this object's existence.
|
|
// it may be called multiple times.
|
|
this.cc.onMetaData = function(infoObject: Object) : void {}
|
|
|
|
}
|
|
|
|
public function getWaveformData() : void {
|
|
// http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundMixer.html#computeSpectrum()
|
|
SoundMixer.computeSpectrum(this.waveformData, false, 0); // sample wave data at 44.1 KHz
|
|
this.waveformDataArray = [];
|
|
for (var i: int = 0, j: int = this.waveformData.length / 4; i < j; i++) { // get all 512 values (256 per channel)
|
|
this.waveformDataArray.push(int(this.waveformData.readFloat() * 1000) / 1000);
|
|
}
|
|
}
|
|
|
|
public function getEQData() : void {
|
|
// http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundMixer.html#computeSpectrum()
|
|
SoundMixer.computeSpectrum(this.eqData, true, 0); // sample EQ data at 44.1 KHz
|
|
this.eqDataArray = [];
|
|
for (var i: int = 0, j: int = this.eqData.length / 4; i < j; i++) { // get all 512 values (256 per channel)
|
|
this.eqDataArray.push(int(this.eqData.readFloat() * 1000) / 1000);
|
|
}
|
|
}
|
|
|
|
public function start(nMsecOffset: int, nLoops: int) : void {
|
|
this.useEvents = true;
|
|
if (this.useNetstream) {
|
|
|
|
writeDebug("SMSound::start nMsecOffset "+ nMsecOffset+ ' nLoops '+nLoops + ' current bufferTime '+this.ns.bufferTime+' current bufferLength '+this.ns.bufferLength+ ' this.lastValues.position '+this.lastValues.position);
|
|
|
|
this.cc.onMetaData = this.metaDataHandler;
|
|
|
|
// Don't seek if we don't have to because it destroys the buffer
|
|
var set_position:Boolean = this.lastValues.position != null && this.lastValues.position != nMsecOffset;
|
|
if (set_position) {
|
|
// Minimize the buffer so playback starts ASAP
|
|
this.setBuffer(this.getStartBuffer());
|
|
}
|
|
|
|
if (this.paused) {
|
|
writeDebug('start: resuming from paused state');
|
|
this.ns.resume(); // get the sound going again
|
|
if (!this.didLoad) {
|
|
this.didLoad = true;
|
|
}
|
|
this.paused = false;
|
|
} else if (!this.didLoad) {
|
|
writeDebug('start: !didLoad - playing '+this.sURL);
|
|
this.ns.play(this.sURL);
|
|
this.pauseOnBufferFull = false; // SAS: playing behaviour overrides buffering behaviour
|
|
this.didLoad = true;
|
|
this.paused = false;
|
|
} else {
|
|
// previously loaded, perhaps stopped/finished. play again?
|
|
writeDebug('playing again (not paused, didLoad = true)');
|
|
this.ns.play(this.sURL);
|
|
}
|
|
|
|
// KJV seek after calling play otherwise some streams get a NetStream.Seek.Failed
|
|
// Should only apply to the !didLoad case, but do it for all for simplicity.
|
|
// nMsecOffset is in milliseconds for streams but in seconds for progressive
|
|
// download.
|
|
if (set_position) {
|
|
this.ns.seek(this.serverUrl ? nMsecOffset / 1000 : nMsecOffset);
|
|
this.lastValues.position = nMsecOffset; // https://gist.github.com/1de8a3113cf33d0cff67
|
|
}
|
|
|
|
// this.ns.addEventListener(Event.SOUND_COMPLETE, _onfinish);
|
|
this.applyTransform();
|
|
|
|
} else {
|
|
// writeDebug('start: seeking to '+nMsecOffset+', '+nLoops+(nLoops==1?' loop':' loops'));
|
|
this.soundChannel = this.play(nMsecOffset, nLoops);
|
|
this.addEventListener(Event.SOUND_COMPLETE, _onfinish);
|
|
this.applyTransform();
|
|
}
|
|
|
|
}
|
|
|
|
private function _onfinish() : void {
|
|
this.removeEventListener(Event.SOUND_COMPLETE, _onfinish);
|
|
}
|
|
|
|
public function loadSound(sURL: String, bStream: Boolean) : void {
|
|
if (this.useNetstream) {
|
|
this.useEvents = true;
|
|
if (this.didLoad != true) {
|
|
ExternalInterface.call('loadSound(): loading ' + this.sURL);
|
|
this.ns.play(this.sURL);
|
|
this.didLoad = true;
|
|
}
|
|
// this.addEventListener(Event.SOUND_COMPLETE, _onfinish);
|
|
this.applyTransform();
|
|
} else {
|
|
try {
|
|
this.didLoad = true;
|
|
this.urlRequest = new URLRequest(sURL);
|
|
this.soundLoaderContext = new SoundLoaderContext(1000, this.checkPolicyFile); // check for policy (crossdomain.xml) file on remote domains - http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundLoaderContext.html
|
|
this.load(this.urlRequest, this.soundLoaderContext);
|
|
} catch(e: Error) {
|
|
writeDebug('error during loadSound(): ' + e.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
public function setAutoPlay(autoPlay: Boolean) : void {
|
|
if (!this.serverUrl) {
|
|
// don't apply to non-RTMP, netstream stuff.
|
|
this.autoPlay = true;
|
|
this.pauseOnBufferFull = false;
|
|
} else {
|
|
this.autoPlay = autoPlay;
|
|
if (this.autoPlay) {
|
|
this.pauseOnBufferFull = false;
|
|
// writeDebug('ignoring pauseOnBufferFull because autoPlay is on');
|
|
} else if (!this.autoPlay) {
|
|
this.pauseOnBufferFull = true;
|
|
// writeDebug('pausing on buffer full because autoPlay is off');
|
|
}
|
|
}
|
|
}
|
|
|
|
public function setVolume(nVolume: Number) : void {
|
|
this.lastValues.volume = nVolume / 100;
|
|
this.applyTransform();
|
|
}
|
|
|
|
public function setPan(nPan: Number) : void {
|
|
this.lastValues.pan = nPan / 100;
|
|
this.applyTransform();
|
|
}
|
|
|
|
public function applyTransform() : void {
|
|
var st: SoundTransform = new SoundTransform(this.lastValues.volume, this.lastValues.pan);
|
|
if (this.useNetstream) {
|
|
if (this.ns) {
|
|
this.ns.soundTransform = st;
|
|
} else {
|
|
// writeDebug('applyTransform(): Note: No active netStream');
|
|
}
|
|
} else if (this.soundChannel) {
|
|
this.soundChannel.soundTransform = st; // new SoundTransform(this.lastValues.volume, this.lastValues.pan);
|
|
}
|
|
}
|
|
|
|
public function recordPlayTime() : void {
|
|
this.play_time = Math.round(getTimer() - (this.start_time + this.connect_time));
|
|
writeDebug('Play took '+ this.play_time + ' ms');
|
|
// We must now have both stats, call the onstats callback
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onstats", {
|
|
play_time: this.play_time,
|
|
connect_time: this.connect_time
|
|
});
|
|
// Stop tracking any stats for this object
|
|
this.recordStats = false;
|
|
}
|
|
|
|
public function recordConnectTime() : void {
|
|
this.connect_time = Math.round(getTimer() - this.start_time);
|
|
writeDebug('Connect took '+ this.connect_time + ' ms');
|
|
}
|
|
|
|
// Handle FMS bandwidth check callback.
|
|
// @see http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html
|
|
// @see http://www.johncblandii.com/index.php/2007/12/fms-a-quick-fix-for-missing-onbwdone-onfcsubscribe-etc.html
|
|
public function onBWDone() : void {
|
|
// writeDebug('onBWDone: called and ignored');
|
|
}
|
|
|
|
// NetStream client callback. Invoked when the song is complete.
|
|
public function onPlayStatus(info:Object):void {
|
|
writeDebug('onPlayStatus called with '+info);
|
|
switch(info.code) {
|
|
case "NetStream.Play.Complete":
|
|
writeDebug('Song has finished!');
|
|
break;
|
|
}
|
|
}
|
|
|
|
public function doIOError(e: IOErrorEvent) : void {
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onload", 0); // call onload, assume it failed.
|
|
// there was a connection drop, a loss of internet connection, or something else wrong. 404 error too.
|
|
}
|
|
|
|
public function doAsyncError(e: AsyncErrorEvent) : void {
|
|
writeDebug('asyncError: ' + e.text);
|
|
}
|
|
|
|
public function doNetStatus(e: NetStatusEvent) : void {
|
|
|
|
// Handle failures
|
|
if (e.info.code == "NetStream.Failed"
|
|
|| e.info.code == "NetStream.Play.FileStructureInvalid"
|
|
|| e.info.code == "NetStream.Play.StreamNotFound") {
|
|
|
|
this.lastNetStatus = e.info.code;
|
|
writeDebug('netStatusEvent: ' + e.info.code);
|
|
this.failed = true;
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", '', e.info.level, e.info.code);
|
|
return;
|
|
}
|
|
|
|
writeDebug('netStatusEvent: ' + e.info.code); // KJV we like to see all events
|
|
|
|
// When streaming, Stop is called when buffering stops, not when the stream is actually finished.
|
|
// @see http://www.actionscript.org/forums/archive/index.php3/t-159194.html
|
|
if (e.info.code == "NetStream.Play.Stop") {
|
|
|
|
if (!this.useNetstream) {
|
|
// finished playing
|
|
// this.didFinish = true; // will be reset via JS callback
|
|
this.didJustBeforeFinish = false; // reset
|
|
writeDebug('calling onfinish for a sound');
|
|
// reset the sound? Move back to position 0?
|
|
this.sm.checkProgress();
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfinish");
|
|
}
|
|
|
|
} else if (e.info.code == "NetStream.Play.Start" || e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Buffer.Full") {
|
|
|
|
// First time buffer has filled. Print debugging output.
|
|
if (this.recordStats && !this.play_time) {
|
|
this.recordPlayTime();
|
|
}
|
|
|
|
// RTMP case..
|
|
// We wait for the buffer to fill up before pausing the just-loaded song because only if the
|
|
// buffer is full will the song continue to buffer until the user hits play.
|
|
if (this.serverUrl && e.info.code == "NetStream.Buffer.Full" && this.pauseOnBufferFull) {
|
|
this.ns.pause();
|
|
this.paused = true;
|
|
this.pauseOnBufferFull = false;
|
|
// Call pause in JS. This will call back to us to pause again, but
|
|
// that should be harmless.
|
|
writeDebug('Pausing song because buffer is full');
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "'].pause", false);
|
|
}
|
|
|
|
// The buffer is full. Increase its size if possible.
|
|
// Double buffering has not been shown to cause false starts, so this is safe.
|
|
if (e.info.code == "NetStream.Buffer.Full") {
|
|
var next_buffer: int = this.getNextBuffer(this.ns.bufferTime);
|
|
if (next_buffer != this.ns.bufferTime) {
|
|
this.setBuffer(next_buffer);
|
|
}
|
|
}
|
|
|
|
var isNetstreamBuffering: Boolean = (e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Play.Start");
|
|
// assume buffering when we start playing, eg. initial load.
|
|
if (isNetstreamBuffering != this.lastValues.isBuffering) {
|
|
this.lastValues.isBuffering = isNetstreamBuffering;
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onbufferchange", this.lastValues.isBuffering ? 1 : 0);
|
|
}
|
|
|
|
// We can detect the end of the stream when Play.Stop is called followed by Buffer.Empty.
|
|
// However, if you pause and let the whole song buffer, Buffer.Flush is called followed by
|
|
// Buffer.Empty, so handle that case too.
|
|
//
|
|
// Ignore this event if we are more than 5 seconds from the end of the song.
|
|
if (e.info.code == "NetStream.Buffer.Empty" && (this.lastNetStatus == 'NetStream.Play.Stop' || this.lastNetStatus == 'NetStream.Buffer.Flush')) {
|
|
if (this.duration && (this.ns.time * 1000) < (this.duration - 5000)) {
|
|
writeDebug('Ignoring Buffer.Empty because this is too early to be the end of the stream! (sID: '+this.sID+', time: '+(this.ns.time*1000)+', duration: '+this.duration+')');
|
|
} else {
|
|
this.didJustBeforeFinish = false; // reset
|
|
this.finished = true;
|
|
writeDebug('calling onfinish for sound '+this.sID);
|
|
this.sm.checkProgress();
|
|
ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfinish");
|
|
}
|
|
|
|
} else if (e.info.code == "NetStream.Buffer.Empty") {
|
|
|
|
// The buffer is empty. Start from the smallest buffer again.
|
|
this.setBuffer(this.getStartBuffer());
|
|
}
|
|
}
|
|
|
|
// Remember the last NetStatus event
|
|
this.lastNetStatus = e.info.code;
|
|
}
|
|
|
|
// KJV The sound adds some of its own netstatus handlers so we don't need to do it here.
|
|
public function addNetstreamEvents() : void {
|
|
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError);
|
|
ns.addEventListener(NetStatusEvent.NET_STATUS, doNetStatus);
|
|
ns.addEventListener(IOErrorEvent.IO_ERROR, doIOError);
|
|
}
|
|
|
|
public function removeNetstreamEvents() : void {
|
|
ns.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError);
|
|
ns.removeEventListener(NetStatusEvent.NET_STATUS, doNetStatus);
|
|
ns.addEventListener(NetStatusEvent.NET_STATUS, dummyNetStatusHandler);
|
|
ns.removeEventListener(IOErrorEvent.IO_ERROR, doIOError);
|
|
// KJV Stop listening for NetConnection events on the sound
|
|
nc.removeEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
|
|
}
|
|
|
|
// Prevents possible 'Unhandled NetStatusEvent' condition if a sound is being
|
|
// destroyed and interacted with at the same time.
|
|
public function dummyNetStatusHandler(e: NetStatusEvent) : void {
|
|
// Don't do anything
|
|
}
|
|
}
|
|
} |