音操作、スロー再生とか
http://hakuhin.jp/as3/sound.html
を参考に、音の扱いについて学びました。
各スライダで速度を調整できます。
またFileRefarenceで手持ちの音声に差し替えらるようにしました。
録音した音声をボイスチェンジャー風に加工したりするアプリにすると楽しそう。
でも、音程を操るにはさらに、難しいみたい。すごく重くてアプリかは無理っぽい
参考リンク
・http://marubayashi.net/diary/2010/03/cat-voice-keyboard.html
・http://iq12.com/old_blog/2009/08/25/real- time-pitch-shifting/
/**
* Copyright otherone ( http://wonderfl.net/user/otherone )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5a8N
*/
package
{
import com.bit101.components.PushButton;
import com.bit101.components.Slider;
import com.bit101.components.Text;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.media.SoundLoaderContext;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.utils.ByteArray;
import flash.net.FileReference;
import flash.display.Loader;
/**
* ...
* @author nekome
*/
public class Main extends Sprite
{
//*******************************************************
private var traceTxt:TextField;
private var src_obj:Sound;
private var sound_loader_context:SoundLoaderContext;
private var url:URLRequest;
private var _fr:FileReference;
private var _loader:Loader;
// パラメータ
private var sampling:uint = 2048; // 1 度に転送するサンプリング数
private var sound_speed:Number = 1.0; // 再生速度
private var loop:Boolean = true; // ループあり
private var loop_begin:Number = 0; // ループ開始地点
private var sound_speed_amp:Number = 0.0; // 再生速度の振幅
private var sound_speed_phase_speed:Number = 0.0; // 再生速度の振幅の位相変化量(0 ~ 360)
//private var src_obj:Sound; // = new MySound(); // ソース用サウンドオブジェクト
private var out_obj:Sound = null; // 出力用サウンドオブジェクト
private var channel:Object = null;
private var phase:Number = 0; // 再生速度振幅の位相を変化
// 波形用ワーク
private var wave:Object = { pos: 0 };
private var sli_speed:Slider,sli_seek:Slider,sli_amp:Slider,sli_phase:Slider;
private var tf_speed:Text,tf_phase:Text,tf_amp:Text;
public function Main():void
{ if (stage)
init();
else
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
traceTxt = new TextField();
addChild(traceTxt);
traceTxt.x = 10, traceTxt.y = 10, traceTxt.width = stage.stageWidth - 20, traceTxt.height = stage.stageHeight - 20;
url = new URLRequest("http://www.voice-pro.jp/announce2/sample/business/sibuya-bz01.mp3");
src_obj = new Sound(url,new SoundLoaderContext(1000,true));
src_obj.addEventListener(IOErrorEvent.IO_ERROR, SoundIOErrorFunc), src_obj.addEventListener(Event.COMPLETE, SoundCompleteFunc);
addEventListener(Event.ENTER_FRAME, update);
_fr= new FileReference();
_fr.addEventListener(Event.SELECT, onSelect);
}
private function SoundIOErrorFunc(event:IOErrorEvent):void{traceTxt.text=("ファイル入出力のエラー");}
private function SoundCompleteFunc(e:Event):void
{
var play_b:PushButton = new PushButton(stage,100,300,"Play",fnPlay);// 再生ボタン
var stop_b:PushButton = new PushButton(stage, 200, 300, "Stop", fnStop);// 停止ボタン
var file_b:PushButton = new PushButton(stage, 300, 300, "File", fnFileSelect);// 停止ボタン
sli_speed = new Slider("horizontal", stage, 50, 150, SliderSpeedSetFunc);//スピード
sli_amp = new Slider("horizontal", stage, 50, 200, SliderAmpSetFunc);
sli_phase=new Slider("horizontal",stage,50,250,SliderPhaseSetFunc);
sli_speed.width = sli_amp.width = sli_phase.width = 300;
tf_speed = new Text(stage, 350, 150,"speed : "+sound_speed);
tf_amp = new Text(stage, 350, 200, "phase : " + sound_speed_phase_speed);
tf_phase = new Text(stage, 350, 250,"amp : "+sound_speed_amp);
sli_speed.maximum = 100,sli_amp.maximum = 0.5,sli_phase.maximum = 90;
sli_speed.value = 50;
sli_speed.height = sli_amp.height = sli_phase.height = tf_speed.height = tf_phase.height = tf_amp.height = 20;
tf_speed.width = tf_phase.width = tf_amp.width = 100;
}
private function fnFileSelect(e:MouseEvent):void
{
_fr.browse();
}
private function onSelect(e:Event):void
{
_fr.addEventListener(Event.COMPLETE, compHandler);
_fr.load();
}
private function compHandler(e:Event):void
{
_loader = new Loader();
src_obj=new Sound();
soundNull();
src_obj.loadCompressedDataFromByteArray(e.target.data, e.target.data.length);
}
private function SliderSpeedSetFunc(e:Event):void
{
var d:Number = sli_speed.value
if (d >= 50) sound_speed = 1.0 + ((d - 50) / 50) * 2.0;
else sound_speed = 1.0 / (1.0 + (1.0 - (d / 50)) * 2.0);
tf_speed.text = String("speed : "+Math.floor(sound_speed * 100) / 100);
}
private function SliderPhaseSetFunc(e:Event):void
{
sound_speed_phase_speed = sli_phase.value;
tf_phase.text = String("phase : "+Math.floor(sound_speed_phase_speed * 100) / 100);
}
private function SliderAmpSetFunc(e:Event):void
{
sound_speed_amp = sli_amp.value;
tf_amp.text = String("amp : "+Math.floor(sound_speed_amp * 100) / 100);
}
private function fnPlay(e:MouseEvent):void
{
if (out_obj) return;
out_obj = new Sound();
// サウンドデータ要求時に呼び出されるイベント
out_obj.addEventListener(SampleDataEvent.SAMPLE_DATA, SampleDataFunc);
channel = out_obj.play();
}
private function fnStop(e:MouseEvent):void { soundNull(); }
private function soundNull():void
{
if (channel)channel.stop(), out_obj = null, channel = null;
}
// サウンドデータ要求
private function SampleDataFunc(event:SampleDataEvent):void
{
var i:uint;
// 再生速度
var speed:Number = sound_speed;
// 再生速度振幅分を加算
speed += Math.sin(phase * Math.PI / 180) * sound_speed_amp;
// 速度制限
if (speed < 0.1)speed = 0.1;
if (speed > 10)speed = 10;
// 表示用
//traceTxt.text = String(Math.floor(speed * 100) / 100);
// サウンドバッファの開始位置
var pos:Number = wave.pos - Math.floor(wave.pos);
// ソース用と出力用のバッファ
var src_buffer:ByteArray = new ByteArray();
var out_buffer:ByteArray = event.data;
// 開始直後のノイズ対策
if (event.position == 0)
{
for (i = 0; i < 4096; i++)
{
// 左チャンネル
out_buffer.writeFloat(0.0);
// 右チャンネル
out_buffer.writeFloat(0.0);
}
return;
}
// ソースから取得するサンプリング数
var copy_sampling:uint = Math.ceil(sampling * speed);
if (!copy_sampling)copy_sampling = 1;
// サウンドデータを取得
var get_sampling:uint = src_obj.extract(src_buffer, copy_sampling, wave.pos);
// サウンドバッファが埋まるまでループを繰り返す
while (true)
{
wave.pos += get_sampling;
copy_sampling -= get_sampling;
if (copy_sampling <= 0)break;
if (!loop)
{
// ループしないなら空白で埋める
for (i = 0; i < copy_sampling; i++)
{
src_buffer.writeFloat(0);
src_buffer.writeFloat(0);
}
break;
}
// ループ開始位置をセット
wave.pos = loop_begin;
// サウンドデータを再取得
get_sampling = src_obj.extract(src_buffer, copy_sampling, wave.pos);
// エラー
if (!get_sampling)return;
}
// 再生速度に合わせてサウンドデータをコピー
var data:Number;
for (i = 0; i < sampling; i++)
{
src_buffer.position = Math.floor(pos) * 4 * 2;
pos += speed;
// 左チャンネル
data = src_buffer.readFloat();
out_buffer.writeFloat(data);
// 右チャンネル
data = src_buffer.readFloat();
out_buffer.writeFloat(data);
}
}
private function GetDigitalTime(time:Number):String
{
var mil:* = Math.floor(time);
var sec:* = Math.floor(mil / 1000);
var min:* = Math.floor(sec / 60);
if (min > 99) min = 99;
sec %= 60;
mil %= 1000;
if (min < 10) min = "0" + min;
if (sec < 10) sec = "0" + sec;
var z:* = "";
if (mil < 100) z += "0";
if (mil < 10) z += "0";
mil = z + mil;
return min + ":" + sec + ":" + mil;
}
private function update(e:Event):void
{
if (channel != null)
{
//channel.position%src_obj.length;
//traceTxt.text = GetDigitalTime(channel.position%src_obj.length)+" / "+GetDigitalTime(src_obj.length);
}
phase += sound_speed_phase_speed;
}
}
}