Flash/Flex/AIR:ActionScript2字幕处理

<meta content="text/html; charset=utf-8" http-equiv="CONTENT-TYPE"> <meta content="OpenOffice.org 2.3 (Linux)" name="GENERATOR"> <style type="text/css">!-- @page { size: 21.59cm 27.94cm; margin: 2cm } P { text-indent: 0.75cm; margin-bottom: 0cm; page-break-before: auto } P.western { font-family: "Courier 10 Pitch" } --> </style>

原理:动态遮罩(本文将使用到AactionScript2定时器),以下是其完整源码(含测试用例):

import wargrey.util.Timer;

import wargrey.util.Map;

import mx.transitions.Tween;

import mx.transitions.easing.*;

/**

* @author Yoshua

*/

class wargrey.util.SoundCaption extends MovieClip{

public static var LIB:Number=0;

public static var LOCAL:Number=1;

public static var NETWORK:Number=2;

private var masker:MovieClip;

private var maskee:MovieClip;

private var exMask:MovieClip;

private var maskeeFormator:TextFormat;

private var exMaskFormator:TextFormat;

private var song:Sound;

private var words:Array;

private var mapping:Map;

private var times:Array;

private var timer:Timer;

public function set songWord(word:String):Void{

exMask.exMaskTextFeild.text=word;

maskee.maskeeTextFeild.text=word;

exMask.exMaskTextFeild.setTextFormat(exMaskFormator);

maskee.maskeeTextFeild.setTextFormat(maskeeFormator);

}

public function SoundCaption(){

words=new Array();

mapping=new Map();

times=new Array();

timer=new Timer(5);

}

public function initialize(font:String,size:Number,bgc:Number,fgc:Number){

font=(font.length>0)?font:"Times New Roman";

size=(size>0)?size:12;

bgc=(bgc>0)?bgc:0x2CEA15;

fgc=(fgc>0)?fgc:0xff0000;

exMask=createEmptyMovieClip("exMask",5);

maskee=exMask.duplicateMovieClip("maskee",10);

masker=createEmptyMovieClip("masker",15);

maskee.createTextField("maskeeTextFeild",0,0,0,100,22);

exMask.createTextField("exMaskTextFeild",0,0,0,100,22);

maskee.maskeeTextFeild.autoSize=true;

exMask.exMaskTextFeild.autoSize=true;

maskeeFormator=new TextFormat(font,size,fgc,true);

exMaskFormator=new TextFormat(font,size,bgc,true);

masker.beginFill(0x0000ff);

masker.moveTo(0,0);

masker.lineTo(24,0);

masker.lineTo(24,22);

masker.lineTo(0,22);

masker.lineTo(0,0);

masker.endFill();

maskee.setMask(masker);

}

public function setSong(name:String,words:Array,src:Number):Void{

song=new Sound(this);

switch (src){

case SoundCaption.LOCAL:

song.loadSound(name,false);

break;

case SoundCaption.NETWORK:

song.loadSound(name,true);

break;

default:

song.attachSound(name);

}

song.onSoundComplete=this.terminate;

timer.addEventListener(Timer.TIMECYCLE,this);

setWords(words);

song.start(0,1);

timer.start();

}

private function setWords(wordArray:Array):Void{

var start:Number;

var end:Number;

for (var i:Number=0;i<wordArray.length;i++){

var wordLine:String=wordArray[i];

var lineSplit:Array=wordLine.split("]");

var len:Number=lineSplit.length;

if (len==0)break;

words.push(lineSplit[len-1]);

for (var j:Number=0;j<len-1;j++){

var timeString:String=lineSplit[j].substr(1);

var timeSplit:Array=timeString.split(":");

end=((new Number(timeSplit[0]))*60+(new Number(timeSplit[1])))*1000;

mapping.put(end,i);

}

if (i==0){

start=end;

}else{

var last:Number=end-start-1;

times.push(last);

start=end;

}

}

}

private function terminate():Void{

timer.stop();

}

private function timecycle():Void{

var pos:Number=song.position;

pos=Math.floor(pos/100)*100;

if (mapping.get(pos)==null)return;

var line:Number=Number(mapping.get(pos));

songWord=words[line];

masker._height=maskee._height;

new Tween(

masker,"_width",Regular.easeInOut,0,maskee._width,times[line]/1000,true

).start();

}


private function onLoad() : Void{

initialize("华文彩云",32);

var words : Array = new Array();

words.push("[00:01]火柴天堂 熊天平 《一个人流浪》");

words.push("[02:36][00:14]走在寒冷下雪的夜空");

words.push("[02:41][00:19]卖着火柴温饱我的梦");

words.push("[02:46][00:25]一步步冰冻一步步寂寞");

words.push("[02:51][00:29]人情寒冷冰冻我的手");

words.push("[02:59][00:39]一包火柴燃烧我的心");

words.push("[03:04][00:44]寒冷夜里挡不住前行");

words.push("[03:09][00:49]风刺我的脸雪割我的口");

words.push("[03:14][00:54]拖着脚步还能走多久");

words.push("[03:22][01:02]有谁来买我的火柴");

words.push("[03:27][01:07]有谁将一根根希望全部点燃");

words.push("[03:32][01:12]有谁来买我的孤单");

words.push("[03:37][01:18]有谁来实现我想家的呼唤");

words.push("[03:45][01:26]每次点燃火柴微微光芒");

words.push("[03:50][01:31]看到希望看到梦想");

words.push("[03:52][01:33]看见天上的妈妈说话");

words.push("[03:54][01:35]她说你要勇敢你要坚强");

words.push("[03:57][01:38]不要害怕不要慌张");

words.push("[03:59][01:40]让你从此不必再流浪");

words.push("[04:02][01:43]每次点燃火柴微微光芒");

words.push("[04:05][01:46]看到希望看到梦想");

words.push("[04:07][01:48]看见天上的妈妈说话");

words.push("[04:09][01:50]她说你要勇敢你要坚强");

words.push("[04:12][01:53]不要害怕不要慌张");

words.push("[04:14][01:55]让你从此不必再流浪");

words.push("[04:19][01:59]妈妈牵着你的手回家");

words.push("[04:24][02:02]睡在温暖花开的天堂");

words.push("[02:15](music:march heaven 《一个人流浪》)");

words.push("[02:22] ");

words.push("[04:32]天堂");

words.push("[04:38]天堂");

words.push("[04:42]......");

words.push("[04:50]");

setSong("mh.mp3",words,SoundCaption.LIB);

}

}

该字幕类SoundCaption其实就是一个空的MovieClip实例,初始化时会动态生成3个空的MovieClip,分别用于充当遮罩层masker、未唱到的歌词层maskee和已唱到歌词层exMask,三个层按顺序重叠在一起。这样一来,在歌曲开始播放时将歌词显示在歌词层,并且根据歌唱时间合理的移动遮罩层并可以实现字模的显示效果。下面是本例重要方法的说明:

initialize所做的正是上述过程的准备工作。该方法有四个参数,依次为字体(String)、字号(Number)、未唱到的歌词颜色(Number)和已唱到的歌词颜色(Number)。四个参数均可省略,默认值可以参考initialize方法的开始处。

setWords:将歌词转化成方便处理的信息。该方法有一个参数,即存放所有歌词的字符串数组,该参数下面详细介绍。这里涉及到一个集合类Map,由于ActionScript2默认的集合框架比较简陋,再加上做这个工具时比较仓促,就随便写了一个基本的Map类,代码如下:

class wargrey.util.Map {

private var keys:Array;

private var values:Array;

public function Map(){

keys=new Array();

values=new Array();

}

public function put(key:Object,value:Object):Void{

for (var i:Number=0;i<keys.length;i++){

if (keys[i]==key){

values[i]=value;

return ;

}

}

keys.push(key);

values.push(value);

return ;

}


public function get(key:Object):Object{

for (var i:Number=0;i<keys.length;i++)

if (keys[i]==key)return values[i];

return null;

}

public function del(key:Object):Object{

var result:Object=null;

for (var i:Number=0;i<length;i++){

if (keys[i]==key){

result=values[i];

keys[i]=keys[length-1];

values[i]=values[length-1];

keys.pop();

values.pop();

break;

}

}

return result;

}

public function get length():Number{

return keys.length;

}

public function toString():String{

var s:String="";

for (var i:Number=0;i<length;i++)

s=s+"\n"+keys[i]+"="+values[i];

return s;

}

}

setSong:该方法的任务比较重,一方面要找到歌曲文件,一方面还要负责歌词歌曲的处理和同步,其实都不是它自己在做。该方法有三个参数,依次为:

歌曲实例名(String):即完整合法的歌曲源名,该源可以来自库,本地文件和网络流。

歌词(Array):即包含所有歌词的字符数组,该参数将被传递给setWords方法处理。歌词的写法与lrc(歌词文件)相同,不过只包含主体部分,诸如歌手、专题等信息不在本例处理范围内。

源类型(Number):即歌曲源的类型,可选的取值有三种,分别为SoundCaption.LIB(库实例)SoundCaption.LOCAL(本地文件)SoundCaption.NETWORK(网络流),默认为SoundCaption.LIB

本例只是一个基本设计,如果可以结合一些图片类的字体便可以做出很多更有趣的字幕效果。不过本例还是有缺点的,比如并不能正真做到词曲同步,目前只能通过细化歌词来达到此目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值