I am working on a multiplayer game in AS3/AIR for mobile (both android & iOS) and I want to implement a local multiplayer (on same wifi network/bluetooth) functionality in the game. What could be the best option to do this ??
it would be great if it is cross platform (android <-> iOS)
if i use peer-to-peer and Adobe Cirrus service (RTMFP) for local multiplayer, can I get into any problems, since the project is in beta ??
I already know about this post : http://forum.starling-framework.org/topic/google-play-game-services-ane-1#post-74449
I have also gone through this post : http://www.as3gamegears.com/category/multiplayer/ But some of the options that I like uses RTMFP which I am doubtful about !! So please suggest me if its ok to use ??
can I use Datagram Sockets class provided by the AIR sdk..?? Although I am not sure but is there any connection between RTMFP and TCP/Datagram sockets ??
If I any more details are required I can specify !!!
Thanks
I use Adobe Cirrus and it's work well. I used available docs and it takes me about 30 min to start communication between two devices. In my opinion it is best choice.
You can use RTMFP without Cirrus using the LAN NetGroup method.
This method is described in the documentation for NetConnection/connect():
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetConnection.html#connect()
A good example of this can be found here:
http://tomkrcha.com/?p=1803
private var connection:NetConnection;
private var group:NetGroup;
private function connect():void {
connection = new NetConnection();
connection.connect("rtmfp:");
connection.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
}
private function netStatus(event:NetStatusEvent):void {
switch(event.info.code){
case "NetConnection.Connect.Success":
joinGroup();
break;
case "NetGroup.Posting.Notify":
receive(event.info.message);
break;
}
}
private function joinGroup():void{
var groupspec:GroupSpecifier = new GroupSpecifier("myGroup/groupOne");
groupspec.postingEnabled = true;
groupspec.ipMulticastMemberUpdatesEnabled = true;
groupspec.addIPMulticastAddress("225.225.0.1:30303");
group = new NetGroup(connection, groupspec.groupspecWithAuthorizations());
group.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
}
public function send(text:String):void {
var message:Object = {
sender: connection.nearId,
text: text
}
group.post(message);
}
public function receive(message:Object):void {
trace("Received message from " + message.sender + ": " + message.text);
}
This is in my experienced the easiest way to do multi-user apps on a local network, because it requires no server (even to discover neighbors) and just the Flash Player.
You can try to use Bluetooth Low Energy in AIR for iOS(and OSX).
Demonstration here: http://www.youtube.com/watch?v=tiRfYjq4wh0&index=1&list=PLw76-mHQ5mhdmHPJy05n424-DKde1XM8G . ANE library here: http://flashpress.ru/blog/ane/bluetooth/?lang=en
CentralManager:
package
{
import flash.display.Sprite;
import ru.flashpress.ane.queue.FPQueueData;
import ru.flashpress.ane.queue.FPQueueTypes;
import ru.flashpress.bluetooth.FPBluetooth;
import ru.flashpress.bluetooth.constants.FPbtState;
import ru.flashpress.bluetooth.data.FPBluetoothOptions;
import ru.flashpress.bluetooth.events.FPBluetoothEvent;
import ru.flashpress.bluetooth.events.FPCentralManagerEvent;
import ru.flashpress.bluetooth.events.FPCharacteristicEvent;
import ru.flashpress.bluetooth.events.FPPeripheralEvent;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristic;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicValueFormat;
import ru.flashpress.bluetooth.helpers.characteristic.stream.FPStreamIn;
import ru.flashpress.bluetooth.helpers.peripheral.FPPeripheral;
import ru.flashpress.bluetooth.helpers.service.FPService;
import ru.flashpress.bluetooth.helpers.service.FPServiceEvent;
import ru.flashpress.bluetooth.managers.central.FPCentralManager;
import ru.flashpress.bluetooth.managers.central.FPcmScanOptions;
public class CentralView extends Sprite
{
public static const SERVICE_UUID:String = 'E20A39F4-73F5-4BC4-A12F-17D1AD07A961';
public static const CHARACTERISTIC_UUID:String = '08590F7E-DB05-467E-8757-72F6FAEB13D4';
//
public function CentralView()
{
var options:FPBluetoothOptions = new FPBluetoothOptions();
options.nativeLogEnabled = true;
FPBluetooth.init(options);
//
startBluetooth();
}
// Bluetooth Methods ****************
private var cm:FPCentralManager;
private function startBluetooth():void
{
trace('startBluetooth');
var queue:FPQueueData = new FPQueueData('ru.flashpress.chat.central', FPQueueTypes.SERIAL);
cm = new FPCentralManager(null, queue);
cm.addEventListener(FPBluetoothEvent.UPDATE_STATE, updateStateHandler);
cm.addEventListener(FPCentralManagerEvent.PERIPHERAL_DISCOVER, discoverPeripheralHandler);
}
private function updateStateHandler(event:FPBluetoothEvent):void
{
trace('updateStateHandler', event.state);
if (event.state != FPbtState.POWERED_ON) {
return;
}
var options:FPcmScanOptions = new FPcmScanOptions(true, null);
cm.startScan(options, SERVICE_UUID);
}
private var peripheral:FPPeripheral;
private function discoverPeripheralHandler(event:FPCentralManagerEvent):void
{
trace('discoverPeripheralHandler', event.peripheral);
if (peripheral) return;
peripheral = event.peripheral;
trace(' name:', peripheral.advertisementInited ? peripheral.advertisementInited.localName : null);
//
peripheral.addEventListener(FPPeripheralEvent.CONNECTED, peripheralConnectedHandler);
peripheral.addEventListener(FPPeripheralEvent.DISCONNECT, peripheralDisconnectedHandler);
peripheral.connect();
}
private function peripheralConnectedHandler(event:FPPeripheralEvent):void
{
trace('peripheralConnectedHandler');
peripheral.discoverServiceUUIDs(SERVICE_UUID);
peripheral.addEventListener(FPPeripheralEvent.DISCOVER_SERVICES, discoverServicesHandler);
}
private function peripheralDisconnectedHandler(event:FPPeripheralEvent):void
{
trace('peripheralDisconnectedHandler');
peripheral.removeEventListener(FPPeripheralEvent.CONNECTED, peripheralConnectedHandler);
peripheral.removeEventListener(FPPeripheralEvent.DISCONNECT, peripheralDisconnectedHandler);
peripheral = null;
}
private var service:FPService;
private function discoverServicesHandler(event:FPPeripheralEvent):void
{
trace('discoverServicesHandler');
if (event.error) {
trace('discoverServicesHandler, error:', event.error);
return;
}
//
service = peripheral.services.list[0];
trace(' service:', service);
//
service.discoverCharacteristicUUIDs(CHARACTERISTIC_UUID);
service.addEventListener(FPServiceEvent.DISCOVER_CHARACTERISTICS, discoverCharacteristicsHandler);
}
public var characteristic:FPCharacteristic;
private var streamIn:FPStreamIn;
private function discoverCharacteristicsHandler(event:FPServiceEvent):void
{
trace('discoverCharacteristicsHandler');
if (event.error) {
trace('discoverCharacteristicsHandler, error:', event.error);
return;
}
characteristic = service.characteristics.list[0];
trace(' characteristic:', characteristic);
characteristic.initValueFormat(FPCharacteristicValueFormat.MESSAGES);
streamIn = characteristic.streamIn;
characteristic.setNotify(true);
characteristic.addEventListener(FPCharacteristicEvent.UPDATE_NOTIFICATION, updateNotificationHandler);
//
characteristic.addEventListener(FPCharacteristicEvent.UPDATE_MESSAGES, updateMessageHandler);
}
private function updateNotificationHandler(event:FPCharacteristicEvent):void
{
trace('updateNotificationHandler:', event.isNotifying);
}
private function updateMessageHandler(event:FPCharacteristicEvent):void
{
trace('updateMessageHandler');
trace(' error:', event.error);
trace(' messages:', event.messages);
}
}
}
Peripheral Manager:
package
{
import flash.display.Sprite;
import ru.flashpress.bluetooth.FPBluetooth;
import ru.flashpress.bluetooth.constants.FPbtState;
import ru.flashpress.bluetooth.data.FPAdvertisementData;
import ru.flashpress.bluetooth.data.FPBluetoothOptions;
import ru.flashpress.bluetooth.events.FPBluetoothEvent;
import ru.flashpress.bluetooth.events.FPCharacteristicEvent;
import ru.flashpress.bluetooth.events.FPPeripheralManagerEvent;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristic;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicPermissions;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicProperties;
import ru.flashpress.bluetooth.helpers.characteristic.FPCharacteristicValueFormat;
import ru.flashpress.bluetooth.helpers.service.FPService;
import ru.flashpress.bluetooth.managers.peripheral.FPPeripheralManager;
public class PeripheralView extends Sprite
{
public static const SERVICE_UUID:String = 'E20A39F4-73F5-4BC4-A12F-17D1AD07A961';
public static const CHARACTERISTIC_UUID:String = '08590F7E-DB05-467E-8757-72F6FAEB13D4';
//
public function PeripheralView()
{
var options:FPBluetoothOptions = new FPBluetoothOptions();
options.nativeLogEnabled = true;
FPBluetooth.init(options);
//
startBluetooth();
}
// Bluetooth Methods ****************
private var pm:FPPeripheralManager;
private function startBluetooth():void
{
trace('startBluetooth');
pm = new FPPeripheralManager();
pm.addEventListener(FPBluetoothEvent.UPDATE_STATE, updateStateHandler);
pm.addEventListener(FPPeripheralManagerEvent.ADD_SERVICE, addServiceHandler);
}
private var characteristic:FPCharacteristic;
private var service:FPService;
private function updateStateHandler(event:FPBluetoothEvent):void
{
trace('updateStateHandler:', event.state);
if (event.state != FPbtState.POWERED_ON) {
return;
}
//
var properties:uint = FPCharacteristicProperties.NOTIFY;
var permissions:uint = FPCharacteristicPermissions.WRITEABLE;
var valueFormat:uint = FPCharacteristicValueFormat.MESSAGES;
characteristic = new FPCharacteristic(CHARACTERISTIC_UUID, properties, permissions, valueFormat);
characteristic.addEventListener(FPCharacteristicEvent.SUBSCRIBED_TO_CENTRAL, subscribedToCentralHandler);
characteristic.addEventListener(FPCharacteristicEvent.UNSUBSCRIBED_FROM_CENTRAL, unsubscribedFromCentralHandler);
characteristic.notifyMTU = 50;
//
service = new FPService(SERVICE_UUID, true);
service.addCharacteristic(characteristic);
//
pm.addService(service);
}
private function addServiceHandler(event:FPPeripheralManagerEvent):void
{
trace('addServiceHandler');
if (event.error) {
trace(' error:', event.error);
return;
}
if (!pm.isAdvertising) {
var uuids:Vector. = new [SERVICE_UUID];
var advertisementData:FPAdvertisementData = new FPAdvertisementData('My Local name', uuids);
pm.startAdvertising(advertisementData);
}
}
private function unsubscribedFromCentralHandler(event:FPCharacteristicEvent):void
{
trace('unsubscribedFromCentralHandlers', event.central);
}
private function subscribedToCentralHandler(event:FPCharacteristicEvent):void
{
trace('subscribedToCentralHandler', event.central);
//
characteristic.sendMessage('send my message');
}
}
}
Related
I am planing a project that involves a cross platform (Android and I.O.S) mobile app that logs in using Facebook. I have no experience with the face book API and cant find any use full material for newbies. I want to use air for its cross platform capabilities so want to avoid multiple solutions for each platform. I have done many searches for help but haven't found much. Can any of you point me to resources you found use full starting off with this sort of thing.
The AS3 Facebook API is all you need. ( http://code.google.com/p/facebook-actionscript-api/ ) Maybe you will have to change a few things (like the JSON methods in there) but otherwise it seems to work alright. You can download several examples from there as well, you can see the usage for different types of environment.
Also, read this article from Tom Krcha http://www.adobe.com/devnet/games/articles/getting-started-with-facebooksdk-actionscript3.html
If you have more specific questions, ask. This one is too generic.
EDIT:
Here is a class I wrote some time ago for a small project
package com.company.social {
import com.facebook.graph.FacebookMobile;
import com.company.AppConst;
import com.company.IDestroyable;
import com.company.Main;
import com.company.displayassets.WebViewCloseStripe;
import com.company.events.FacebookControllerEvent;
import com.company.events.TwitterControllerEvent;
import flash.display.BitmapData;
import flash.display.PNGEncoderOptions;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.StageWebView;
import flash.utils.ByteArray;
import flash.utils.clearTimeout;
import flash.utils.setTimeout;
public class FacebookController extends EventDispatcher implements IDestroyable {
private static const APP_ID:String = "1234512345"; // Your App ID.
private static const SITE_URL:String = "some_url";
//Extended permission to access other parts of the user's profile that may be private, or if your application needs to publish content to Facebook on a user's behalf.
private var _extendedPermissions:Array = ["publish_stream","user_website","user_status","user_about_me"];
private var _stage:Stage;
private var _webView:StageWebView;
private var _topStripe:WebViewCloseStripe;
private var _activity:String;
private var _timeoutID:uint;
public static const ACTIVITY_LOGIN:String = "login";
public static const ACTIVITY_POST:String = "post";
public function FacebookController(stage:Stage) {
_stage = stage;
init();
}
private function init():void {
_activity = ACTIVITY_LOGIN;
startTimeout();
FacebookMobile.init(APP_ID, onHandleInit, null);
}
private function onHandleInit(response:Object, fail:Object):void {
if (response) {
stopTimeout();
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.LOGIN_COMPLETE));
//FacebookMobile.api("/me", handleUserInfo);
}
else {
/*trace("no response, login -->");
for(var prop in fail["error"]) {
trace(prop+": "+fail["error"][prop]);
}*/
loginUser();
}
}
private function startTimeout():void {
trace("timeout start");
clearTimeout(_timeoutID);
_timeoutID = setTimeout(timeout, AppConst.TIMEOUT_TIME);
}
private function timeout():void {
trace("timed out");
clearTimeout(_timeoutID);
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.TIMEOUT));
}
private function stopTimeout():void {
trace("timeout stop");
clearTimeout(_timeoutID);
}
private function loginUser():void {
stopTimeout();
_topStripe = new WebViewCloseStripe();
_topStripe.getCloseButton().addEventListener(MouseEvent.CLICK, closeClickHandler);
_stage.addChild(_topStripe);
_webView = new StageWebView();
_webView.viewPort = new Rectangle(0, _topStripe.height, _stage.fullScreenWidth, _stage.fullScreenHeight - _topStripe.height);
FacebookMobile.login(handleLogin, _stage, _extendedPermissions, _webView);
}
private function handleLogin(response:Object, fail:Object):void {
if(_topStripe) {
_topStripe.getCloseButton().removeEventListener(MouseEvent.CLICK, closeClickHandler);
_topStripe.destroy();
_stage.removeChild(_topStripe);
_topStripe = null;
}
if(_webView) {
_webView = null;
}
if(response) {
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.LOGIN_COMPLETE));
//FacebookMobile.api('/me', handleUserInfo);
}
else {
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.LOGIN_ERROR));
}
}
private function closeClickHandler(e:MouseEvent):void {
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.CLOSE));
}
private function handleUserInfo(response:Object, fail:Object):void {
if (response) {
for(var prop in response) {
trace(prop+": "+response[prop]);
}
}
}
private function handleUploadImage(result:Object, fail:Object):void {
stopTimeout();
if(result) {
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.POST_COMPLETE));
}
else {
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.POST_ERROR));
}
}
public function postWithImage(message:String, imageData:BitmapData):void {
_activity = ACTIVITY_POST;
var byteArray:ByteArray = imageData.encode(new Rectangle(0, 0, imageData.width, imageData.height), new PNGEncoderOptions());
var params: Object = new Object;
params.image = byteArray;
params.fileName = "image.png";
params.message = message;
startTimeout();
FacebookMobile.api("/me/photos", handleUploadImage, params, "POST");
}
public function reset():void {
FacebookMobile.logout(handleReset, SITE_URL);
}
public function handleReset(response:Object):void {
dispatchEvent(new FacebookControllerEvent(FacebookControllerEvent.RESET));
}
public function destroy():void {
if(_webView) {
_webView.dispose();
_webView = null;
}
if(_topStripe) {
_topStripe.getCloseButton().removeEventListener(MouseEvent.CLICK, closeClickHandler);
_topStripe.destroy();
_stage.removeChild(_topStripe);
_topStripe = null;
}
_stage = null;
}
}
}
Alternatively you can use a native extension like this:
http://www.milkmangames.com/blog/tools/#iosgv
There are free versions from other publishers available as well.
I'm trying to receive a stream of a NetGroup (television) but it fails at some point. Here is the code (reduced to a testcase):
package lib
{
import flash.events.NetStatusEvent;
import flash.external.ExternalInterface;
import flash.media.SoundTransform;
import flash.media.Video;
import flash.net.GroupSpecifier;
import flash.net.NetConnection;
import flash.net.NetGroup;
import flash.net.NetStream;
import flash.net.NetStreamPlayOptions;
import mx.core.FlexGlobals;
public class player
{
private var connection:NetConnection;
private var group:NetGroup;
private var group_option:GroupSpecifier;
private var self:Object;
private var stream:NetStream;
private var stream_option:NetStreamPlayOptions;
private var video:Video;
private function _connect():void
{
self.connection = new NetConnection();
self.connection.addEventListener(NetStatusEvent.NET_STATUS, self._event_net_status);
self.connection.connect('rtmfp://p2p.peer-stream.com');
}
private function _event_net_status(event:NetStatusEvent):void
{
if(event.info.code === '')
return;
ExternalInterface.call('echo', 'status', event.info.code, false);
switch(event.info.code)
{
case 'NetConnection.Connect.Success':
self._p2p_start();
break;
case 'NetGroup.Connect.Success':
self._stream_init();
break;
case 'NetStream.Connect.Success':
self._stream_start();
break;
}
}
private function _p2p_start():void
{
self.group_option = new GroupSpecifier('G:0101010c0b0e70726f2073696562656e00');
self.group_option.ipMulticastMemberUpdatesEnabled = true;
self.group_option.multicastEnabled = true;
self.group_option.objectReplicationEnabled = true;
self.group_option.postingEnabled = true;
self.group_option.routingEnabled = true;
self.group_option.serverChannelEnabled = true;
self.group = new NetGroup(self.connection, self.group_option.groupspecWithAuthorizations());
self.group.addEventListener(NetStatusEvent.NET_STATUS, self._event_net_status);
}
private function _stream_init():void
{
self.stream = new NetStream(self.connection, self.group_option.groupspecWithAuthorizations());
self.stream.addEventListener(NetStatusEvent.NET_STATUS, self._event_net_status);
}
private function _stream_start():void
{
self.video.attachNetStream(self.stream);
self.stream.soundTransform = new SoundTransform();
self.stream_option = new NetStreamPlayOptions();
self.stream_option.streamName = 'G:0101010c0b0e70726f2073696562656e00';
self.stream.play2(self.stream_option);
}
public function player():void
{
super();
FlexGlobals.topLevelApplication.stage.quality = 'BEST';
self = this;
self.video = new Video(640, 480);
FlexGlobals.topLevelApplication.video_box.addChild(self.video);
self.video.smoothing = true;
self._connect();
}
}
}
The ExternalInterface call there shows me just the passed info in a textfield (I'm testing this in a browser). Here is the result of this output:
NetConnection.Connect.Success
NetGroup.Connect.Success
NetStream.Connect.Success
NetStream.Play.Reset
NetStream.Play.Start
But nothing happens on the player. Here are my thoughts:
I'm using the same string as GroupSpecifier and streamName but I'm assuming this is wrong. If so how can I get the stream name of the NetGroup?
I'm enabling everything from the GroupSpecifier as I don't know what are the minimum requirements to receive a stream and pass it then to another clients. Maybe enabling all makes some troubles here.
Maybe somebody can tell me how I can solve this problem to finally see a stream.
I was able to solve the problem: The streamName was indeed wrong and I could manually figure out the correct name. Also instead of using new GroupSpecifier() I must pass the known group-string directly to the NetGroup and NetStream object.
Hello I am trying to record the user sound using AS3 Worker everything works well when it comes to communication between the worker and the main scene but I think the record functionnality have and issue here I am using the Record Clash from bytearray.org (http://www.bytearray.org/?p=1858)
and here's is my code am I missing something :
package objects
{
import flash.events.Event;
import flash.system.MessageChannel;
import flash.system.Worker;
import flash.system.WorkerDomain;
import flash.utils.ByteArray;
import starling.display.Button;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.Event;
public class RecordComponent extends Sprite
{
private var audioContainer:Image;
private var recButton:Button;
private var playButton:Button;
private var stopButton:Button;
//background thread for recording
private var worker:Worker;
private var wtm:MessageChannel;
private var mtw:MessageChannel;
private var output:ByteArray;
public function RecordComponent()
{
super();
worker = WorkerDomain.current.createWorker(Workers.RecordWorker);
wtm = worker.createMessageChannel(Worker.current);
mtw = Worker.current.createMessageChannel(worker);
worker.setSharedProperty("wtm",wtm);
worker.setSharedProperty("mtw",mtw);
worker.start();
wtm.addEventListener(flash.events.Event.CHANNEL_MESSAGE,onChannelMessage);
this.addEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
}
protected function onChannelMessage(event:flash.events.Event):void
{
output = wtm.receive();
}
protected function onAddedToStage(event:starling.events.Event):void
{
this.removeEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
drawScreen();
}
private function drawScreen():void
{
audioContainer = new Image( Assets.getAtlas().getTexture("audioContainer") );
addChild(audioContainer);
//record button
recButton = new Button( Assets.getAtlas().getTexture("recordButton") );
recButton.x=Math.ceil(194/2 - recButton.width/2);
recButton.y=5;
addChild(recButton);
//stop button
stopButton = new Button( Assets.getAtlas().getTexture("stopButton") );
stopButton.x=recButton.x+10;
stopButton.y=10;
stopButton.visible = false;
addChild(stopButton);
//play button
playButton = new Button( Assets.getAtlas().getTexture("playButton") );
playButton.x=Math.ceil(194 - playButton.width)-25;
playButton.y=10;
playButton.visible = false;
addChild(playButton);
this.addEventListener(starling.events.Event.TRIGGERED, buttons_triggeredHandler);
}
private function buttons_triggeredHandler(event:starling.events.Event):void
{
var currentButton:Button = event.target as Button;
switch(currentButton)
{
case recButton:
startRecording();
break;
case stopButton:
stopRecording();
break;
case playButton:
playRecording();
break;
}
}
private function playRecording():void
{
mtw.send("RECORD_PLAY");
}
private function stopRecording():void
{
stopButton.visible = false;
recButton.x = 25;
playButton.visible = true;
recButton.visible = true;
mtw.send("RECORD_STOP");
//
}
private function startRecording():void
{
recButton.visible = false;
stopButton.visible = true;
playButton.visible = false;
mtw.send("RECORD_START");
}
}
}
RecordWorker
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Microphone;
import flash.system.MessageChannel;
import flash.system.Worker;
import org.as3wavsound.WavSound;
import org.bytearray.micrecorder.MicRecorder;
import org.bytearray.micrecorder.encoder.WaveEncoder;
import org.bytearray.micrecorder.events.RecordingEvent;
public class RecordWorker extends Sprite
{
private var wtm:MessageChannel;
private var mtw:MessageChannel;
// volume in the final WAV file will be downsampled to 50%
private var volume:Number = .75;
// we create the WAV encoder to be used by MicRecorder
private var wavEncoder:WaveEncoder = new WaveEncoder( volume );
// we create the MicRecorder object which does the job
private var recorder:MicRecorder = new MicRecorder( wavEncoder,Microphone.getEnhancedMicrophone() );
public function RecordWorker()
{
wtm = Worker.current.getSharedProperty("wtm");
mtw = Worker.current.getSharedProperty("mtw");
mtw.addEventListener(Event.CHANNEL_MESSAGE,command_Handler);
}
//message received from the main component
protected function command_Handler(event:Event):void
{
switch(mtw.receive())
{
case "RECORD_START":
trace("recording.....");
recorder.record();
recorder.addEventListener(RecordingEvent.RECORDING, onRecording);
recorder.addEventListener(flash.events.Event.COMPLETE, onRecordComplete);
break;
case "RECORD_STOP":
trace("record stopped....");
recorder.stop();
break;
case "RECORD_PLAY":
if(recorder.output.length>0)
{
var player:WavSound = new WavSound(recorder.output);
player.play();
}
break;
}
}
private function onRecording(event:RecordingEvent):void
{
trace ( event.time );
}
private function onRecordComplete(event:flash.events.Event):void
{
}
}
}
anny help will be appriciated
Thanks,
Khaled
Looking at this write-up on Adobe's AS3 reference, it seems that because each created worker is "a virtual instance of the Flash runtime", it has no direct connection to any of the core runtime's input or output channels. The Microphone class is one such cut-off interface, which is why it can't reach the system mic inside a worker.
I made an AIR application that sends voice between two computers, this is the code:
package
{
import fl.controls.List;
import flash.events.ProgressEvent;
import flash.events.SampleDataEvent;
import flash.events.ServerSocketConnectEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.net.ServerSocket;
import flash.net.Socket;
import flash.utils.ByteArray;
import flash.system.System;
public class VoiceCommunication
{
private var soundBytes:ByteArray;
private var mic:Microphone;
private var voiceReceiver:ServerSocket;
private var voiceSender:Socket;
private var mLocalIP:String;
private var mRemoteIP:String;
private var mPort:uint;
private var sound:Sound;
private var sample:Number;
public function VoiceCommunication(localIP:String, port:uint)
{
mLocalIP = localIP;
mPort = port;
}
public function startSendingVoice(remoteIP:String = null):void
{
mic = Microphone.getMicrophone();//2-null
mic.gain = 100;
mic.rate = 44;
voiceSender = new Socket();//3-null
mRemoteIP = remoteIP;//5-null
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);//1-remove eventlistener
}
protected function micSampleDataHandler(event:SampleDataEvent):void
{
if(mRemoteIP != null)
{
voiceSender.connect(mRemoteIP, mPort);
while(event.data.bytesAvailable)
{
sample = event.data.readFloat();
voiceSender.writeFloat(sample);
}
voiceSender.flush();
}
}
public function startReceivingVoice():void
{
voiceReceiver = new ServerSocket;//2
voiceReceiver.bind(mPort, mLocalIP);
voiceReceiver.addEventListener(ServerSocketConnectEvent.CONNECT, handleConnection);//1
voiceReceiver.listen();
}
protected function handleConnection(event:ServerSocketConnectEvent):void
{
var s:Socket = event.socket as Socket;
s.addEventListener(ProgressEvent.SOCKET_DATA, handleData);
}
protected function handleData(event:ProgressEvent):void
{
event.target.removeEventListener(ProgressEvent.SOCKET_DATA, handleData);
var s:Socket = event.target as Socket;
soundBytes = null;
soundBytes = new ByteArray();//3
s.readBytes(soundBytes);
playSound();
s = null;
}
private function playSound():void
{
soundBytes.position = 0;
sound = null;
sound = new Sound();//5
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);//4
sound.play();
}
protected function playbackSampleHandler(event:SampleDataEvent):void
{
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++)
{
sample = soundBytes.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
}
System.gc();
}
public function destroySender():void
{
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
mic = null;
voiceSender = null;
mRemoteIP = null;
System.gc();
}
public function destroyReceiver():void
{
voiceReceiver.removeEventListener(ServerSocketConnectEvent.CONNECT, handleConnection);//1
voiceReceiver = null;
soundBytes = null;
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
sound = null;
System.gc();
}
}
}
This works fine and when I tried it, it did send the voice, but the problem is that sending the voice is too slow and gets split. So is there a better way to do it?
I rewrote it using datagram socket but that didn't work at all, the code:
package
{
import fl.controls.List;
import flash.events.DatagramSocketDataEvent;
import flash.events.ProgressEvent;
import flash.events.SampleDataEvent;
import flash.events.ServerSocketConnectEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.net.DatagramSocket;
import flash.net.ServerSocket;
import flash.net.Socket;
import flash.system.System;
import flash.utils.ByteArray;
public class VoiceComm
{
private var soundBytes:ByteArray;
private var mic:Microphone;
private var socket:DatagramSocket
private var mList:List;
private var mLocalIP:String;
private var mRemoteIP:String;
private var mPort:uint;
private var sound:Sound;
private var sample:Number;
private var bytesToBeSent:ByteArray;
public function VoiceComm(localIP:String, port:uint)
{
socket = new DatagramSocket();
mLocalIP = localIP;
mPort = port;
}
public function startSendingVoice(remoteIP:String = null):void
{
mic = Microphone.getMicrophone();
mic.gain = 100;
mic.rate = 44;
mRemoteIP = remoteIP;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
protected function micSampleDataHandler(event:SampleDataEvent):void
{
var sample:Number;
if(mRemoteIP != null)
{
while(event.data.bytesAvailable)
{
sample = event.data.readFloat();
bytesToBeSent = null;
bytesToBeSent = new ByteArray();
bytesToBeSent.writeFloat(sample);
socket.send(bytesToBeSent, 0, 0, mRemoteIP, mPort);
}
}
}
public function startReceivingVoice():void
{
socket.bind(mPort, mLocalIP);
socket.addEventListener(DatagramSocketDataEvent.DATA, handleData);
socket.receive();
}
protected function handleData(event:DatagramSocketDataEvent):void
{
trace("Data inthere!");
soundBytes = null;
soundBytes = new ByteArray();//3
event.data.readBytes(soundBytes);
playSound();
}
private function playSound():void
{
soundBytes.position = 0;
sound = null;
sound = new Sound();//5
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);//4
sound.play();
}
protected function playbackSampleHandler(event:SampleDataEvent):void
{
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++)
{
sample = soundBytes.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
}
System.gc();
}
public function destroySender():void
{
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
mic = null;
mList = null;
mRemoteIP = null;
System.gc();
}
public function destroyReceiver():void
{
soundBytes = null;
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
sound = null;
System.gc();
}
}
}
**Update:
The code after modification:
package
{
import fl.controls.List;
import flash.events.DatagramSocketDataEvent;
import flash.events.SampleDataEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.net.DatagramSocket;
import flash.net.Socket;
import flash.system.System;
import flash.utils.ByteArray;
public class VoiceComm
{
private var mic:Microphone;
private var soundBytes:ByteArray;
private var sample:Number;
private var sound:Sound;
private var socket:DatagramSocket;
private var mLocalIP:String;
private var mPort:uint;
private var mRemoteIP:String;
private var receivedBytes:ByteArray;
public function VoiceComm(localIP:String, port:uint)
{
socket = new DatagramSocket();
mLocalIP = localIP;
mPort = port;
}
//destroyers:
public function destroySender():void
{
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
mic = null;
soundBytes = null;
System.gc();
}
public function destroyReceiver():void
{
socket.removeEventListener(DatagramSocketDataEvent.DATA, playSound);
receivedBytes = null;
System.gc();
}
//sending part
public function startSendingVoice(ip:String = null):void
{
mRemoteIP = ip;
mic = Microphone.getMicrophone();
mic.rate = 44;
mic.gain = 100;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
}
protected function sampleData(event:SampleDataEvent):void
{
soundBytes = new ByteArray();
while(event.data.bytesAvailable)
{
var sample:Number = event.data.readFloat();
soundBytes.writeFloat(sample);
}
if(mRemoteIP != null)
{
event.data.position = 0;
socket.send(soundBytes, 0, event.data.length, mRemoteIP, mPort);
}
}
//receiving part
public function startReceivingVoice():void
{
socket.bind(mPort, mLocalIP);
socket.addEventListener(DatagramSocketDataEvent.DATA, playSound);
socket.receive();
}
protected function playSound(event:DatagramSocketDataEvent):void
{
trace("got data!");
receivedBytes = new ByteArray();
event.data.readBytes(receivedBytes);
receivedBytes.position = 0;
var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);
sound.play();
}
protected function playbackSampleHandler(event:SampleDataEvent):void
{
trace("Playing Sound...");
for (var i:int = 0; i < 8192 && receivedBytes.bytesAvailable > 0; i++)
{
var sample:Number = receivedBytes.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
System.gc();
}
}
}
}
This is most likely the source of the issue: http://forums.adobe.com/message/3111816. The TCP Nagle Algorithm. What this basically does is, when you're writing a bunch of small chunks of data to the socket, instead of sending them all out individually it will hold them back and pool them together in order to send a single "full" packet rather than tons of small ones (which leads to a greater, constant load on the network).
If possible, re-write using Datagram sockets instead. To my knowledge there is currently no way to disable the Nagle algorithm in Flash.
Update
A couple things after looking at your code in depth: The first is that you're writing each byte one at a time to the TCP socket. Don't do that, write the entire bytearray and flush it.
if(mRemoteIP != null)
{
voiceSender.connect(mRemoteIP, mPort);
voiceSender.writeBytes(event.data, 0, event.data.length);
voiceSender.flush();
}
That may solve the issues you're having. If not, the reason why your UDP version is for two reasons. The first is that again, you're writing one byte at a time and sending but more important, you're telling it not to write the data to the socket at all. This line is the culprit:
socket.send(bytesToBeSent, 0, 0, mRemoteIP, mPort);
Note the third paramter, "0". That parameter is the amount of data from the supplied bytearray that should be written to the socket. So you're essentially saying, don't send anything. Change the code like so:
if(isConnected == true)
{
event.data.position = 0;
localSendingSocket.send(event.data, 0, event.data.length, remoteIP, remotePort);
}
Let me know if this works out for you. In the meantime I'm re-writing your code based on these suspicions so if I manage to get a project compiled and working before you, I'll post the project files here.
Where not use Adobe Cirrus (formerly Stratus to create a peer2peer application instead of the socket?
use the (OS) Red5 server.
http://www.red5.org/
the blurb:
"Red5 Media Server 1.0 delivers a powerful video streaming and multi-user solution to the ©Adobe ©Flash Player and other exciting client technologies. Based on Java and some of the most powerful open source frameworks, Red5 stands as a solid solution for business of all sizes including the enterprise.
Red5 includes support for the latest multi-user API’s including NetConnection, NetStream and SharedObject’s while providing a powerful RTMP / Servlet implementation. In addition to support for the RTMP protocol, the application server has an embedded Tomcat Servlet container for Java EE Web Applications. Application development draws additional benefits from the Spring Framework and Scope based event driven services.
By using the Open Source Red5 Media Server, you are developing with a truly open and extensible platform that can be used in Video Conferences, Multi-User Gaming and Enterprise Application Software.
Happy Coding and enjoy our powerful free community server"
My application will need some windows but I can't use the popUpManager to handle custom components so I'm thinking to implement a new window system.
But I still don't know how to merge it with my custom components. Any ideas?
My code:
My login skin:
<s:Skin(...)>
<s:Group>
(...login components...)
</s:Group>
</s:Skin>
package com.totty.app.components.login {
import com.totty.app.TottysBrain;
import com.totty.app.components.window.Window;
import com.totty.app.events.LoginSuccessEvent;
import com.totty.tottysBrain.components.BrainyDynComponent;
import com.totty.tottysBrain.components.BrainyWindow;
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.managers.PopUpManager;
import spark.components.Button;
import spark.components.CheckBox;
import spark.components.Group;
import spark.components.Label;
import spark.components.TextInput;
import spark.components.supportClasses.SkinnableComponent;
[Event(name="loginSuccess", type="com.totty.app.events.LoginSuccessEvent")]
public class Login extends BrainyDynComponent {
[SkinPart(required="true")]
public var email:TextInput;
[SkinPart(required="true")]
public var password:TextInput = new TextInput();
[SkinPart(required="false")]
public var rememberMe:CheckBox;
[SkinPart(required="false")]
public var notification:Label;
[SkinPart(required="true")]
public var submit:Button = new Button();
private var _currentState:uint = 0;
private var _states:Array = ['default', 'logging', 'loginFailure', 'loginSuccess'];
private var _default:Boolean = false;
private var _logging:Boolean = false;
private var _loggingSuccess:Boolean = false;
private var _loggingFailure:Boolean = false;
public function Login() {
super();
defaultCommand = 'users.login';
defaultSkin = LoginSkin;
_setCurrentState(0);
onPartAdded('submit', function():void {
submit.addEventListener(MouseEvent.CLICK, _submitLoginButton_click);
});
onPartRemoved('submit', function():void {
submit.removeEventListener(MouseEvent.CLICK, _submitLoginButton_click);
});
onPartAdded('window', function():void{
//window.addEventListener('close', _window_close);
//window.title = 'Login';
});
}
private function _window_close(evt:Event):void{
dispatchEvent(evt);
var parentAsGroup:Group = parent as Group
parentAsGroup.removeElement(this);
}
protected function _submitLogin():void {
_setCurrentState(1);
submitData({email:email.text, password:password.text, rememberMe:rememberMe.selected});
}
override protected function _onSuccess(result:*):void {
Alert.show(result);
_setCurrentState(3);
if(result) {
dispatchEvent(new LoginSuccessEvent(email.text, rememberMe.selected));
_loggingSuccess = true;
}else{
_setCurrentState(4);
_loggingSuccess = false;
}
}
override protected function _onFailure(result:*):void {
Alert.show(result);
_setCurrentState(4);
_loggingSuccess = false;
}
private function _submitLoginButton_click(evt:MouseEvent):void {
_submitLogin();
}
private function _setCurrentState(n:uint):void{
if(_currentState == n) return;
_currentState = n;
invalidateProperties();
}
override protected function commitProperties():void{
super.commitProperties();
switch(_currentState){
case 0:
// default
//window.footerText = '';
break;
case 1:
// logging
//window.footerText = 'logging';
break;
case 2:
// loginFailure
//window.footerText = 'loginFailure';
break;
case 3:
// loginSuccess
//window.footerText = 'loginSuccess';
//parentBrain.removeElement(this);
//var parentAsGroup:Group = parent as Group
//PopUpManager.removePopUp(this);
//parentAsGroup.removeElement(this);
break;
}
}
}
}`
Now I have the main application to add this component in a window. I would prefer to be able to be able to see the window while I'm designing the interface. That means that the window component must be added to the component. Not to use the but something like to contain all the contents of the component.
Maybe you already saw this, but its a good place to start, anyway :)
http://code.google.com/p/flexmdi/