Character Dialogue Asset Management - actionscript-3

Just working on a class that deals with sound files in ActionScript for my Starling project. I would like your opinions on the implementation and whether it would perform well. (This file will be getting huge with embedded files).
This is another thing, if it is embedded, is this a bad thing? I mean if I embedded so many voice files, even though they weren't going to be used would this affect performance?
Anyway here is my implementation:
package assets
{
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.utils.Dictionary;
/**
* ...
* #author Shaun Stone
*/
public class CharacterDialogueSoundAssets
{
// This will return the voice file is exists
private static var _soundAssetsDictionary:Dictionary= new Dictionary();
[Embed(source = "../../../media/sounds/dialogue/voice_file_1.mp3")]
public static const VOICE_FILE_1:Class;
/**
* Get voice file from dictionary
*
* #param name
* #return
*/
public static function getVoiceFile(name:String):Sound
{
if (_soundAssetsDictionary[name] == undefined)
{
var voiceFile:Sound = new CharacterDialogueSoundAssets.name() as Sound;
_soundAssetsDictionary[name] = voiceFile;
}
return _soundAssetsDictionary[name];
}
public static function disposeOfVoiceFile(name:String):void
{
if (_soundAssetsDictionary[name] == undefined)
{
return;
}
//dispose for garbage collection
_soundAssetsDictionary[name] = undefined;
}
}
}

Related

embedded font displays no text (as3)

using flash develop. so i downloaded "source sans" font from here: https://fonts.google.com/specimen/Source+Sans+Pro?selection.family=Source+Sans+Pro
it has a bunch of ttf files, "bold, italic, etc" im guessing i only need one, so i copied the regular one to my src folder, renamed it to "SourceSansPro", right clicked on my src folder and add new font library. i named it "SourceSansPro". and heres my code now:
main class:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFormat;
public class Main extends Sprite
{
private var format:TextFormat = new TextFormat("SourceSansPro");
private var text:TextField = new TextField;
public function Main()
{
text.embedFonts = true;
text.setTextFormat(format);
text.text = "abcdefg";
addChild(text);
}
}
}
the font library thing:
/**
Suggested workflow:
- create a fontLibrary subfolder in your project (NOT in /bin or /src)
- for example: /lib/fontLibrary
- copy font files in this location
- create a FontLibrary class in the same location
- one font library can contain several font classes (duplicate embed and registration code)
FlashDevelop QuickBuild options: (just press Ctrl+F8 to compile this library)
#mxmlc -o bin/SourceSansPro.swf -static-link-runtime-shared-libraries=true -noplay
*/
package
{
import flash.display.Sprite;
import flash.text.Font;
/**
* Font library
* #author 111
*/
public class SourceSansPro extends Sprite
{
/*
Common unicode ranges:
Uppercase : U+0020,U+0041-U+005A
Lowercase : U+0020,U+0061-U+007A
Numerals : U+0030-U+0039,U+002E
Punctuation : U+0020-U+002F,U+003A-U+0040,U+005B-U+0060,U+007B-U+007E
Basic Latin : U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E
Latin I : U+0020,U+00A1-U+00FF,U+2000-U+206F,U+20A0-U+20CF,U+2100-U+2183
Latin Ext. A: U+0100-U+01FF,U+2000-U+206F,U+20A0-U+20CF,U+2100-U+2183
Latin Ext. B: U+0180-U+024F,U+2000-U+206F,U+20A0-U+20CF,U+2100-U+2183
Greek : U+0374-U+03F2,U+1F00-U+1FFE,U+2000-U+206f,U+20A0-U+20CF,U+2100-U+2183
Cyrillic : U+0400-U+04CE,U+2000-U+206F,U+20A0-U+20CF,U+2100-U+2183
Armenian : U+0530-U+058F,U+FB13-U+FB17
Arabic : U+0600-U+06FF,U+FB50-U+FDFF,U+FE70-U+FEFF
Hebrew : U+05B0-U+05FF,U+FB1D-U+FB4F,U+2000-U+206f,U+20A0-U+20CF,U+2100-U+2183
About 'embedAsCFF' attribute:
- is Flex 4 only (comment out to target Flex 2-3)
- is 'true' by default, meaning the font is embedded for the new TextLayout engine only
- you must set explicitely to 'false' for use in regular TextFields
More information:
http://help.adobe.com/en_US/Flex/4.0/UsingSDK/WS2db454920e96a9e51e63e3d11c0bf69084-7f5f.html
*/
[Embed(source="SourceSansPro.ttf"
,fontFamily ='SourceSansPro'
,fontStyle ='normal' // normal|italic
,fontWeight ='normal' // normal|bold
,unicodeRange='U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E'
,embedAsCFF='false'
)]
public static const fontClass:Class;
public function SourceSansPro()
{
Font.registerFont(fontClass);
}
}
}
so in main, if text.embedFonts is false it will show the default font, if its true it will show up blank.
any help?
edit - new code
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.Font;
public class Main extends Sprite
{
private var format:TextFormat = new TextFormat("libel");
private var text:TextField = new TextField;
public function Main()
{
[Embed(source="libel.ttf"
,fontFamily ='libel'
,fontStyle ='normal' // normal|italic
,fontWeight ='normal' // normal|bold
,unicodeRange='U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E'
,embedAsCFF='false'
)]
Font.registerFont();
text.embedFonts = true;
text.setTextFormat(format);
text.text = "abcdefg";
addChild(text);
}
}
}
This is what you probably want.
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.Font;
public class Main extends Sprite
{
// After the [Embed] tag you need
// a variable definition it is linked to.
[Embed(source="libel.ttf", fontFamily='libel')]
private var Libel:Class;
public function Main()
{
// In order to share the font with the whole application,
// you need to provide its class to the method.
Font.registerFont(Libel);
var aFormat:TextFormat = new TextFormat;
aFormat.font = "libel";
// ... other format properties here.
var aField:TextField = new TextField;
// This way you will see that
// TextField even if fonts don't render.
aField.border = true;
// Setting the default text format is a good idea here.
aField.embedFonts = true;
aField.setTextFormat(aFormat);
aField.defaultTextFormat = aFormat;
aField.text = "abcdefg";
addChild(aField);
}
}
}

AS3 UrlLoader does not fire Event.COMPLETE

Everything work, except the Event.Complete... (that means that I retrieve the good content at the server side, and the progress event work as expected)
I have a simple code that process an upload. I use this class : https://github.com/Nek-/Multipart.as/blob/master/src/com/jonas/net/Multipart.as
And my code:
package com.foo.http
{
import com.jonas.net.Multipart;
import flash.net.*;
import flash.events.*;
import flash.utils.ByteArray;
import flash.external.ExternalInterface;
public class RequestManager
{
private var request:Multipart;
private var loader:URLLoader;
/**
* The url can be http://foobar:952/helloworld
* #param url
*/
public function RequestManager(url:String)
{
this.loader = new URLLoader();
this.request = new Multipart(url);
// This is needed, if we don't set it, the complete event will never be trigger
// We retrieve some text
this.loader.dataFormat = URLLoaderDataFormat.TEXT;
// Events
this.attachEvents();
}
public function getRequest():Multipart
{
return this.request;
}
public function send():void
{
this.loader.load(this.request.request);
}
private function attachEvents():void
{
this.loader.addEventListener(Event.COMPLETE, this.requestCompleted);
this.loader.addEventListener(ProgressEvent.PROGRESS, this.requestProgress);
this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.securityError);
this.loader.addEventListener(IOErrorEvent.IO_ERROR, this.networkError);
}
// Of course there is also listener methods (with a trace inside and call to JS, nothing more)
}
}
Any idea what it comes from ?
It looks like my problem was not from actionscript or flash itself but from my javascript and the flash debugger on firefox.

Where must be custom logic in pureMVC (as3)?

I tried to write small as3 program based on framework pureMVC.
I understood basic principles of it, but I can't understand, where I must place custom logic.
For example, I must load 10 images. I created command, that init Proxy.
package app.controller
{
import app.model.GalleryProxy;
import dicts.Constants;
import org.puremvc.interfaces.INotification;
public class LoadFilesCommand extends BaseCommand
{
public function LoadFilesCommand() { }
override public function execute(note:INotification):void
{
facade.registerProxy(new GalleryProxy(Constants.FILES_LIST));
}
}
}
And Proxy is:
package app.model
{
import dicts.Constants;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLRequest;
import org.puremvc.interfaces.IProxy;
import org.puremvc.patterns.proxy.Proxy;
public class GalleryProxy extends Proxy implements IProxy
{
public function GalleryProxy(list:Vector.<String>)
{
super(Constants.PROXY_GALLERY);
_fileList = list;
_total = _fileList.length;
load();
}
public function get currentImage():Bitmap
{
return _images[_index];
}
//--------------------------------------------------------------------------
// PRIVATE SECTION
//--------------------------------------------------------------------------
private var _fileList:Vector.<String>;
private var _total:uint;
private var _loaded:uint = 0;
private var _images:Array = [];
private var _index:int;
private function load():void
{
var loader:Loader;
for (var i:int = 0; i < _total; i++)
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoadHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
loader.load(new URLRequest(_fileList[i]));
}
}
private function imageLoadHandler(event:Event):void
{
var info:LoaderInfo = LoaderInfo(event.currentTarget);
_images[Constants.FILES_LIST.indexOf(info.url)] = info.content;
info.removeEventListener(Event.COMPLETE, imageLoadHandler);
info.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
_loaded++;
if (_loaded >= _total)
sendNotification(Constants.COMMAND_SHOW_MAIN);
}
private function errorHandler(event:ErrorEvent):void
{
throw new Error("bad link or internet disconnect");
}
}
}
Now my Proxy is loading images independently (functions load() and imageLoadHandler)
Is it correct?
Or I must move this logic to Command class?
Or I must create some LoadService.as, which will contains this logic?
What is the correct variant for pureMVC?
Do you want to load your 10 images on application startup? If not, make load() public and call it from a Mediator, responding to a UI event.
If so, what you have will work fine. One alternative would be writing GalleryProxy so it doesn't call load() in the constructor - instead, you could have the Command register the proxy, load the image list, and call proxy.load(images[i]) in a loop.

Passing a variable between classes

I am trying to pass a variable "budget" from my DocumentClass of a flash file, to a class.
Currently I have :
(DocumentClassv5 , this is the code thats attached to the flash file in the properties panel, some code ommited)
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.*;
import flash.ui.Keyboard;
import flash.display.Stage;
import flash.text.TextFieldType;
import flash.media.Sound;
import flash.media.SoundChannel;
import miniGameOne;
import floorTileMC;
import flash.display.Loader;
import flash.net.URLRequest;
public class DocumentClassv5 extends MovieClip
{
/*#################################
## Defining Variables ##
#################################*/
public var budget:int = 0;
var gameOne:miniGameOne = new miniGameOne();
/*#################################
## Constructor ##
#################################*/
public function DocumentClassv5()
{
/*#################################
## Adding Event Listeners ##
#################################*/
trace("Document class loaded");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*###################################################
## Begins the mini game ##
###################################################*/
public function begin(evt: MouseEvent)
{
beginGame.visible = false;
beginGame.removeEventListener(MouseEvent.CLICK, begin);
budget = 500;
cleanListeners();
gameOne.loadGame();
trace(gameOne.testVar);
trace(floorTile.testVar2);
/*#################################
## Adding Event Listeners ##
#################################*/
trace("Game started");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
Then I have the miniGameOne class file, which at the moment does nothing
I also have another class file, called tileFloorMC. This is attached to a symbol.
package
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import DocumentClassv5;
public class floorTileMC extends MovieClip
{
var propertyA:Number;
//var hackerClass:DocumentClassv5 = new DocumentClassv5;
public var testVar2:int = 50;
public function floorTileMC()
{
this.propertyA = randomRange(100, 500);
this.addEventListener(MouseEvent.ROLL_OVER, manageMouseOver, false, 0, true);
this.addEventListener(MouseEvent.ROLL_OUT, manageMouseOut, false, 0, true);
}
private function manageMouseOver(evt: MouseEvent)
{
this.gotoAndStop(2);
//trace(mainClass.budget);
}
private function manageMouseOut(evt: MouseEvent)
{
this.gotoAndStop(1);
//mainClass.budget += 1;
}
private function randomRange(minNum:Number, maxNum:Number):Number
{
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
}
}
Now, essentially I will need to be able to pass budget from DocumentClassv5 TO floorTileMC, and then BACK to DocumentClassv5. At the moment, I can pass anything from floorTileMC and anything from miniGameOne into DocumentClassv5, but when i try and pass from floorTileMC to DocumentClassv5, I get error
Error #2136: The SWF file file:///yadayada/GameV5.swf contains invalid data.
More specifically, as soon as I uncomment //var hackerClass:DocumentClassv5 = new DocumentClassv5;
Any help would be greatly appreciated!
Thanks,
Tiffany
You're trying to instantiate your document class in a subclass:
var hackerClass:DocumentClassv5 = new DocumentClassv5();
You want access to access the existing instance, not create a new one.
One thing you can do, is create a static reference to your document class. (see code sample below)
Static references can get ugly though, and you may just want to pass a reference of your doc class to your other classes when you instantiate them.
Both method below:
In your document class:
//instead of the line below:
var gameOne:miniGameOne = new miniGameOne(); //It's a bad idea to instantiate non primitive objects before the constructor of your document class runs.
//just declare it, and instantiate it in the constructor
var gameOne:miniGameOne;
//if you want to use a static reference:
public static var me:DocumentClassv5;
public function DocumentClassv5()
{
/*#################################
## Adding Event Listeners ##
#################################*/
//if using the static var me, set it's value to this (the instance of the document class):
me = this;
trace("Document class loaded");
gameOne = new miniGameOne(this); //pass a reference to the document class if NOT using the static var me
}
If using the static var me, you access it by doing the following from any class:
DocumentClassV5.me.budget;
Another cleaner alternative (if the values you need access to aren't really directly tied to any class, eg. global preferences), is make a whole new class that is just static (doesn't get instantiated) to hold your preferences.
package {
public class Global {
public static var budget:Number = 50;
}
}
Then you'd access budget by importing your Global class and doing Global.budget = 5

Record h.264 from webcam as f4v from flash

Using Flash Media Server, I have an pre-made application that records from webcam and microphone and publishes it to the FMS server as an FLV file.
This works fine, but I now have a need to stream this video that's been recorded to an iOS device. I've got FMS working and it can stream the hls-record/sample.f4v.m3u8 file to an iOS / quicktime device.
I've read some tutorials online and I've changed the publish(filename, "record") to publish("mp4:" + filename + ".f4v", record)
It records stores the file on the FMS server (i see it there, with the filename i gave it and as a .f4v file), it has content. When i goto play it though (via quicktime or safari) i see the length of the video (4 seconds as a test) but no video. The tracking bar doesn't move and the wait icon, waits.
FMS version 4.5 and Flash 11.3 is installed on my Mac. (CentOS 5.8 server)
I must be missing something.
package com
{
import fl.controls.ProgressBar;
import fl.controls.ProgressBarMode;
import flash.display.MovieClip;
import flash.events.Event;
import com.NetConnector
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.media.Camera;
import flash.media.Microphone;
import flash.media.Video;
import flash.net.navigateToURL;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.text.TextField;
import flash.utils.setTimeout;
import flash.utils.Timer;
import flash.media.H264Level;
import flash.media.H264Profile;
import flash.media.H264VideoStreamSettings;
/**
* ...
* #author Alexander (flash2you) < >
*/
public class Recorder extends MovieClip
{
private var dataHolder:DataHolder = DataHolder.getInstance()
public var layer:MovieClip
public var activityLevel_pb:ProgressBar
public var aguja:MovieClip
public var aguja2:MovieClip
public var publishButton:MovieClip
public var timer_txt:TextField
public var recordStatus:MovieClip
public var recordBtn:MovieClip
private var netStream:NetStream
private var microphone:Microphone = Microphone.getMicrophone()
private var camera:Camera = Camera.getCamera()
public var video:Video
private var timer:Timer = new Timer(100)
private var clockTimer:Timer = new Timer(1000)
public var published:Boolean = false
private var isRecording:Boolean = false
private var minutero = 0;
private var crono = 0;
private var records = 0;
public var settings_mc:MovieClip
public static var recorder:Recorder
public var settings_icon:MovieClip
private var limitTimer:Timer
public function Recorder()
{
Recorder.recorder = this;
timer.addEventListener(TimerEvent.TIMER, on$timer)
clockTimer.addEventListener(TimerEvent.TIMER, on$clockTimer)
//visible = false
recordBtn.buttonMode = true
recordBtn.addEventListener(MouseEvent.CLICK , recordBtn$click)
recordBtn.addEventListener(MouseEvent.MOUSE_OVER, recordBtn$over)
recordBtn.addEventListener(MouseEvent.MOUSE_OUT, recordBtn$out)
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage)
limitTimer = new Timer(dataHolder.timelimit * 1000);
limitTimer.addEventListener(TimerEvent.TIMER, onLimitTimerHandler)
}
private function onLimitTimerHandler(e:TimerEvent):void
{
stopPublish()
}
/*
* when we comes to second frame
* */
private function onAddedToStage(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
init()
}
/*
* function for set up camera from settings module
* */
public function setCamera(_camera:Camera) {
camera = _camera
addCameraSettings()
video.attachCamera(camera)
if (netStream){
netStream.attachCamera(camera)
}
}
public function setMicrophone(mic:Microphone) {
microphone = mic;
if (netStream){
netStream.attachAudio(microphone)
}
addMicSettings()
}
private function addMicSettings() {
microphone.setUseEchoSuppression(true);
microphone.setSilenceLevel(1)
}
private function addCameraSettings():void
{
camera.setQuality(90000, 90);
camera.setMode(320, 240, 30, true);
camera.setKeyFrameInterval(15);
//camera.setMode(dataHolder.cameraWidth, dataHolder.cameraHeight, dataHolder.cameraFPS)
//camera.setQuality(0, dataHolder.cameraQuality)
}
public function init() {
startConnect()
}
/*
* main function for connection
* */
private function startConnect() {
visible = true
timer_txt.htmlText = "<b>00:00</b>";
initCamera()
initMicropone()
var nc:NetConnection = new NetConnection()
nc.connect(null)
netStream = new NetStream(nc)
netStream.attachAudio(microphone)
video.attachCamera(camera)
layer.visible = false
publishButton.gotoAndStop(1);
activityLevel_pb.mode = ProgressBarMode.MANUAL;
recordStatus.gotoAndStop("noRecord")
timer.start()
connection.addEventListener(NetConnector.CONNECTED, connectionComplete)
connection.startConnection()
}
public function get connection():NetConnector {
return dataHolder.connection
}
private function on$timer(e:TimerEvent) {
activityLevel_pb.setProgress(microphone.activityLevel, 100)
}
/*
* when connection to your stream server done
* */
private function connectionComplete(e:Event = null) {
netStream = new NetStream(connection)
netStream.attachAudio(microphone)
netStream.attachCamera(camera)
}
/*
* add 0 if less then 10secs
* */
private function addLeading(nbr) {
if (nbr<10) {
return ("0"+Math.floor(nbr));
} else {
return (Math.floor(nbr).toString());
}
}
/*
* update visible clock, rotate arrows
* */
private function updateTimer() {
timer_txt.htmlText = "<b>"+addLeading(crono/60)+":"+addLeading(crono%60)+"</b>";
aguja.rotation = aguja.rotation+6;
if (addLeading(crono/60)>minutero) {
aguja2.rotation = aguja2.rotation+6;
++minutero;
}
// end if
++crono;
}
private function on$clockTimer(e:TimerEvent):void
{
updateTimer()
}
private function startClockTimer() {
clockTimer.start()
}
/*
* update graphics and start recording
* */
private function recordBtn$click(e:MouseEvent):void
{
if (!isRecording) {
startRecording()
recordStatus.gotoAndStop("record")
recordBtn.visible = false
}
}
private function recordBtn$over(e:MouseEvent):void
{
if (!isRecording) {
this.gotoAndPlay(65);
}
}
private function recordBtn$out(e:MouseEvent):void
{
if (!isRecording) {
this.gotoAndPlay(61);
}
}
private function startRecording() {
if (connection.connected){
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_3_1);
netStream.videoStreamSettings = h264Settings;
netStream.publish("mp4:" + dataHolder.filename + ".f4v", "record");
var metaData:Object = new Object();
metaData.codec = netStream.videoStreamSettings.codec;
metaData.profile = h264Settings.profile;
metaData.level = h264Settings.level;
metaData.fps = camera.fps;
metaData.bandwith = camera.bandwidth;
metaData.height = camera.height;
metaData.width = camera.width;
metaData.keyFrameInterval = camera.keyFrameInterval;
metaData.copyright = "company";
netStream.send("#setDataFrame", "onMetaData", metaData);
}
isRecording = true
startClockTimer()
publishButton.gotoAndPlay(2)
publishButton.buttonMode = true
publishButton.addEventListener(MouseEvent.CLICK, publishButton$click);
limitTimer.start()
}
/*
* redirect to finishURL that was passed via flashvars
* */
private function publishButton$click(e:MouseEvent):void
{
stopPublish()
var request:URLRequest = new URLRequest(dataHolder.finishURL)
navigateToURL(request, "_self")
}
private function stopPublish():void
{
netStream.close();
connection.close();
limitTimer.stop();
clockTimer.stop();
isRecording = false
recordStatus.gotoAndStop("recordEnd")
updateTimer();
}
/*
* init microphone
* */
private function initMicropone():void
{
microphone = Microphone.getMicrophone()
addMicSettings()
}
/*
* init camera
* */
private function initCamera():void
{
camera = Camera.getCamera()
addCameraSettings()
}
}
}
I've settled on creating a php script that sits in the folder with the FLV files. Via a cron job, this php file checks the mysql database for new videos with a process tag of 1. Finds new video? Execute ffmpeg on the FLV file to convert it to mp4 and set the process tag to 0. This way, I maintain the FLV for desktop browser streaming and an MP4 file for iOS streaming. If I should ever need to create another file type for android or whatever may come out, I can simply do another ffmpeg conversion when the cron job triggers.
This would also cause less overhead on the server. The mp4 is encoded once by ffmpeg - not every time a request is made to stream the file via FMS. Now the MP4 is just another file on the web server.