※2008.12.20追記
→このとき書いたものから修正した、のこぎり波とかのソースです。
こんにちは、ソフトバンクだというのに、この間までVodafone携帯を使っていました。いいチャンスだと思い、思い切ってiPhoneに機種変更をしました。ところが!きちんと注意書きを読まなかったため最も楽しみにしていたAppができません。
というのも、わたくしはクレジットカードを持っていないため、iPhoneのアクティベーションができないんです。アクティベーションができないと無料のソフトもダウンロードできなくて、がっくしです。
なので、クレジットカードを申し込んだんですが、フリーという不安定な立場で審査が下りるか夜も眠れない日々です。(ウソ)
さてAstroです。音の生成って楽しいですね。
今回は、のこぎり波、矩形波などを生成してみました。式は例によって適当なんで、疑ってきいてみてください。
●サイン波、矩形波、のこぎり波、三角波、(ホワイト?)ノイズ。の5種類の波形が生成可能。
●キーボードに応じて、周波数をきりかえて音を出す。
●音量を調整可能。
●波形をSoundMixer.computeSpectrumからでなく、生成する音のデータから作成。(例の本のmiurrorさんのページを参考に)
この波形表示はサンプルバッファごとに描画しているので、すぐ流れていったりしちゃいますね。これを同期をとって静止画のように止められればよいのですが、、。これってオシロスコープの役割と同じなのかな?
以下ソースです。鍵盤とかslideとかのコンポーネントとかは、別途swcとしてflaから書き出したものを使用しています。
三角波がたまにノイズがプチプチなることがあります。どなたか改善方法を教えてくださるとうれしいのですが、、。
package { import fl.controls.ComboBox; import fl.controls.Slider; import fl.events.SliderEvent; import flash.display.Graphics; import flash.display.MovieClip; import flash.display.Sprite; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.events.SampleDataEvent; import flash.media.Sound; import flash.media.SoundChannel; [SWF(width="400", height="300", frameRate="30", backgroundColor="#5696A0")] public class AstroWaveTypeTest extends Sprite { public static const SINE:String = "sine"; public static const SAW:String = "saw"; public static const TRIANGLE:String = "triangle"; public static const PULSE:String = "pulse"; public static const NOISE:String = "noise"; private const WAVE_TYPE_CANVAS_BASE_WIDTH:uint = 150; private const WAVE_TYPE_CANVAS_BASE_HEIGHT:uint = 80; private var synth1:Sound; private var ch1:SoundChannel; private var frequency:Number; private var keyboardTable:Array; private var keyboardAsset:KeyboardAsset; private var waveType:String; private var waveTypeCombo:ComboBox; private var amplifier:Slider; private var volume:Number; private var waveTypeCanvas:Sprite; private var waveTypeCanvasBase:Sprite; public function AstroWaveTypeTest() { super(); stage.scaleMode = StageScaleMode.NO_SCALE; initialize(); } private function initialize():void { initializeKeyboardTable(); waveType = SINE; volume = 0.5; keyboardAsset = new KeyboardAsset(); addChild(keyboardAsset); addKeyboradListener(); initializeCombobox(); initializeAmplifier(); initializeWaveTypeCanvas(); synth1 = new Sound(); synth1.addEventListener("sampleData",waveGenerator); } private function addKeyboradListener():void { var key:MovieClip; var i:uint; for(i = 1; i <= 24; i++) { key = keyboardAsset["key" + i]; key.addEventListener(MouseEvent.MOUSE_DOWN, keyDownHandler); key.useHandCursor = key.buttonMode = true; } } private function initializeCombobox():void { waveTypeCombo = keyboardAsset.waveTypeCombo; waveTypeCombo.addItem( { label: "サイン波", data:SINE } ); waveTypeCombo.addItem( { label: "矩形波", data:PULSE } ); waveTypeCombo.addItem( { label: "のこぎり波", data:SAW } ); waveTypeCombo.addItem( { label: "三角波", data:TRIANGLE } ); waveTypeCombo.addItem( { label: "ノイズ", data:NOISE } ); waveTypeCombo.addEventListener(Event.CHANGE, waveTypeComboChangeHandler); } private function waveTypeComboChangeHandler(e:Event):void { waveType = waveTypeCombo.value; } private function initializeAmplifier():void { amplifier = keyboardAsset.amplifier; amplifier.value = volume * 100; amplifier.width = 100; amplifier.tickInterval = 5; amplifier.addEventListener(SliderEvent.THUMB_DRAG, amplifierDrag); } private function amplifierDrag(e:SliderEvent):void { volume = amplifier.value / 100; } private function keyDownHandler(e:MouseEvent):void { stage.addEventListener(MouseEvent.MOUSE_UP, stageUpHandler); setFreqency(e.currentTarget as MovieClip); ch1 = synth1.play(); } private function stageUpHandler(e:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP, stageUpHandler); ch1.stop(); } private function initializeKeyboardTable():void { keyboardTable = [ [0, "F"], [0, "F#"], [0, "G"], [0, "G#"], [1, "A"], [1, "A#"], [1, "B"], [1, "C"], [1, "C#"], [1, "D"], [1, "D#"], [1, "E"], [1, "F"], [1, "F#"], [1, "G"], [1, "G#"], [2, "A"], [2, "A#"], [2, "B"], [2, "C"], [2, "C#"], [2, "D"], [2, "D#"], [2, "E"] ] } private function setFreqency(key:MovieClip):void { var keyNum:uint = uint(key.name.substr("key".length)); var keyStep:uint = keyNum % 12; keyStep = keyStep < 5 ? keyStep + 7 : keyStep - 5; var octave:Number = 110.5* Math.pow(2, keyboardTable[keyNum - 1][0]); frequency = octave * Math.pow(2, keyStep / 12); } private function waveGenerator(e:SampleDataEvent):void { var sample:Number; var f0:Number = frequency / 44100; var f1:Number = 44100 / frequency; var PAI2:Number = 2 * Math.PI; var waveArray:Array = new Array(); for ( var c:int=0; c < 2048; c++ ) { switch(waveType) { //サイン波 case SINE: sample = Math.sin(PAI2 * f0 * Number(c + e.position)); break; //矩形波 case PULSE: sample = (Math.sin(PAI2 * f0 * Number(c + e.position)) < 0) ? 1 : -1; break; //のこぎり波 case SAW: sample = 2 * f0 * ((c + e.position) % f1) - 1; break; //三角波 case TRIANGLE: sample = (Math.sin(PAI2 * f0 * Number(c + e.position)) < 0) ? (4 * f0 * ((c + e.position) % (f1 / 2)) - 1) : (-4 * f0 * ((c + e.position) % (f1 / 2)) + 1); break; //ホワイトノイズ case NOISE: sample = Math.random() * 2 - 1; break; default: break; } //音量調節 sample *= volume; //範囲外チェック sample = (sample < -1) ? -1 : sample; sample = (sample > 1) ? 1 : sample; e.data.writeFloat(sample); e.data.writeFloat(sample); //波形描画用に保存 if(waveArray.length < 512) { waveArray.push(sample); } } //波形描画 drawWaveType(waveArray); } private function initializeWaveTypeCanvas():void { waveTypeCanvasBase = new Sprite(); addChild(waveTypeCanvasBase); waveTypeCanvasBase.graphics.beginFill(0x333333, 1); waveTypeCanvasBase.graphics.drawRect(0,0,WAVE_TYPE_CANVAS_BASE_WIDTH,WAVE_TYPE_CANVAS_BASE_HEIGHT); waveTypeCanvasBase.graphics.endFill(); waveTypeCanvasBase.x = 230; waveTypeCanvasBase.y = 20; waveTypeCanvas = new Sprite(); addChild(waveTypeCanvas); } private function drawWaveType(waveArray:Array):void { var g:Graphics = waveTypeCanvas.graphics; var step:uint = waveArray.length / WAVE_TYPE_CANVAS_BASE_WIDTH; var i:uint; var ptx:uint = waveTypeCanvasBase.x; var pty:uint = waveTypeCanvasBase.y + WAVE_TYPE_CANVAS_BASE_HEIGHT / 2; g.clear(); g.lineStyle(1, 0xFF231E, 1); g.moveTo(ptx, pty); for(i = 0; i < WAVE_TYPE_CANVAS_BASE_WIDTH; i = i + step) { g.lineTo(i + ptx, waveArray[i] * WAVE_TYPE_CANVAS_BASE_HEIGHT / 2 + pty); } } } }
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ