Flash Player 10.1 终实现本地线上录制MP3
2010-06-26 21:55 hbb 阅读(3845) 评论(8) 收藏 举报Flash Player 10.1 可以获取麦克风数据了,所以可以实现本地录音。(不知道啥时候Adobe能开发本路录像,估计怕FMS卖不掉?)
但光凭FP10.1,只能实现即录即放,没法保存。如果要保存,又是另外一个课题了——音频编码。
adobe这篇文章里提供了一个WAVWriter,可以把ByteArray转成WAV文件。执行起来是同步的,效率上还不错,因为wav本身就是无损格式,也正因为如此,保存成wav文件太大,总归不太好。理想的是MP3...
Thibault Imbert利用AIR2的新特性,可以运行一个本地程序,这样调用鼎鼎大名的Lame来转MP3就OK了。
借用AIR还不够让人兴奋,因为这和在线项目无缘。那么利用Alchemy打包Lame呢?这个想法近似疯狂,但却衍生出一个靠谱的方法。
是用Alchemy打包了一个叫8hz-MP3的轻量级MP3编码器,这样一来就能够脱离AIR实现WAV转MP3了。
作者提供了swc,加上之前adobe提供的WAVWriter,组合起来写了一个保存录音的demo。
package
{
import com.adobe.audio.format.WAVWriter;
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.ProgressEvent;
import flash.events.SampleDataEvent;
import flash.media.Microphone;
import flash.media.Sound;
import flash.text.TextField;
import flash.utils.ByteArray;
import flash.utils.getTimer;
import fr.kikko.lab.ShineMP3Encoder;
/**
* ...
* @author hbb
*/
public class Main extends Sprite
{
private var _mic:Microphone;
private var _voice:ByteArray;
private var _mp3Encoder:ShineMP3Encoder;
private var _state:String;
private var _timer:int;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
init();
}
private function init():void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.stageFocusRect = false;
if (!setupMicrophone()) return;
stage.addEventListener(KeyboardEvent.KEY_UP, commandHandler);
_state = 'pre-recording';
log('press <key>: <r>ecord');
}
private function setupMicrophone():Boolean
{
_mic = Microphone.getMicrophone();
if (!_mic) { log('no michrophone!'); return false; }
_mic.rate = 44;
_mic.setSilenceLevel(0, 1000);
_mic.setLoopBack(true);
_mic.setUseEchoSuppression(true);
return true;
}
private function commandHandler(e:KeyboardEvent):void
{
switch(String.fromCharCode(e.charCode))
{
case 'r':
startRecording();
break;
case 'e':
stopAndEncodeRecording();
break;
case 's':
saveMP3();
break;
}
}
private function saveMP3():void
{
if (_state != 'encoded') return;
_mp3Encoder.saveAs();
_state = 'pre-recording';
}
private function startRecording():void
{
if (_state != 'pre-recording' && _state != 'encoded') return;
log('press <key>: stop and <e>ncode recording');
_state = 'pre-encoding';
_voice = new ByteArray();
_mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onRecord);
}
private function stopAndEncodeRecording():void
{
if (_state != 'pre-encoding') return;
_state = 'encoding';
_mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onRecord);
_voice.position = 0;
log('encode start...synchronous convert to a WAV first');
convertToMP3();
}
private function convertToMP3():void
{
var wavWrite:WAVWriter = new WAVWriter();
wavWrite.numOfChannels = 1;
wavWrite.sampleBitRate = 16;
wavWrite.samplingRate = 44100;
var wav:ByteArray = new ByteArray();
_timer = getTimer();
wavWrite.processSamples(wav, _voice, 44100, 1);
log('convert to a WAV used: ' + (getTimer() - _timer) + 'ms');
wav.position = 0;
log('WAV size:' + wav.bytesAvailable + ' bytes');
log('Asynchronous convert to MP3 now');
_timer = getTimer();
_mp3Encoder = new ShineMP3Encoder( wav );
_mp3Encoder.addEventListener(Event.COMPLETE, onEncoded);
_mp3Encoder.addEventListener(ProgressEvent.PROGRESS, onEncoding);
_mp3Encoder.addEventListener(ErrorEvent.ERROR, onEncodeError);
_mp3Encoder.start();
}
private function onEncoded(e:Event):void
{
_state = 'encoded';
log('encode MP3 complete used: ' + (getTimer() - _timer) + 'ms');
_mp3Encoder.mp3Data.position = 0;
log('MP3 size:' + _mp3Encoder.mp3Data.bytesAvailable + ' bytes');
log('press <key>: <s>ave to MP3 or <r>ecord again');
}
private function onEncoding(e:ProgressEvent):void
{
log('encoding MP3... ' + Number(e.bytesLoaded / e.bytesTotal * 100).toFixed(2) + '%', true);
}
private function onEncodeError(e:ErrorEvent):void
{
log('encode MP3 error ' + e.text);
}
private function onRecord(e:SampleDataEvent):void
{
_voice.writeBytes( e.data );
var str:String = '';
for (var i:int = _mic.activityLevel; i--;)
{
str += '*';
}
log('recoding ' + str, true);
}
private function log(msg:String, updateInPlace:Boolean = false):void
{
var txt:TextField = getChildByName('logTxt') as TextField;
if (txt)
{
if (updateInPlace)
{
if (txt.text.substr( -1, 1) == '\r')
{
txt.text = txt.text.substr( 0, txt.text.lastIndexOf('\r', txt.text.length - 2) );
}
txt.appendText('\n' + msg + '\n');
}
else
{
txt.appendText((txt.text.substr(-1,1) == '\r' ? '' : '\n') + msg);
}
txt.scrollV = txt.maxScrollV;
}
else
{
txt = new TextField();
txt.name = 'logTxt';
txt.multiline = true;
txt.border = true;
txt.borderColor = 0x666666;
txt.width = stage.stageWidth - 20;
txt.height = stage.stageHeight - 20;
txt.x = txt.y = 10;
txt.text = msg;
addChild(txt);
}
}
}
}
用下来,性能上没问题,转WAV虽然是同步的,但不实现压缩,效率上可以接受。录2分钟、12M大小的WAV,比转个800x600的JPG要快多了。转MP3是异步的,效率上没问题,只是目前没法更好的进行压缩设置。
如此,本地线上录音并保存MP3的功能,Flash已经可以做到了~~~
有些朋友问我要源码,虽然都贴出来了,不过想象几个外部类包的调用和配置还是比较无聊的。[点此下载]
浙公网安备 33010602011771号