ActionScript日記 - サウンドのリアルタイム生成(2)
第1回|第2回|第3回|第4回|第5回
完全にプログラマ向けのエントリです。
また物凄く間が空いてしまいましたが、前回の続き。
まだまだ使い物になるレベルじゃないんですが、一応音楽らしきものが鳴らせるようになったので公開してみたいと思います。5%ルールってとこですか。
トルコ行進曲(2小節)
一応プログラム中でメロディを記述することは出来るんですが、このままじゃアレなんで、次はMMLに対応してみたいと思います。
プチノイズも何とかしたいし、対応してない機能もどうにかしたいですねぇ。
ソースは続きをどうぞ。
完全にプログラマ向けのエントリです。
また物凄く間が空いてしまいましたが、前回の続き。
まだまだ使い物になるレベルじゃないんですが、一応音楽らしきものが鳴らせるようになったので公開してみたいと思います。5%ルールってとこですか。
トルコ行進曲(2小節)
一応プログラム中でメロディを記述することは出来るんですが、このままじゃアレなんで、次はMMLに対応してみたいと思います。
プチノイズも何とかしたいし、対応してない機能もどうにかしたいですねぇ。
ソースは続きをどうぞ。
ソースの貼り付けにはas2htmlを利用させていただきました。
PlayMML.as
MMixer.as
MTrack.as
MChannel.as
MEvent.as
MStatus.as
PlayMML.as
package {
import flash.display.Sprite;
import nu.mine.flashnet.sound.core.*;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class PlayMML extends Sprite {
private var engine:PlaybackEngine;
private var tracks:Array;
public function PlayMML() {
var playbackBufferSize:int = 2048;
var dataProvider:MMixer = new MMixer(); //create a data source
tracks = new Array();
tracks[0] = new MTrack(dataProvider.getFmt(), playbackBufferSize);
tracks[0].addNote(74, 48);
tracks[0].addNote(76, 48);
tracks[0].addNote(78, 96);
tracks[0].addNote(74, 48);
tracks[0].addNote(76, 48);
tracks[0].addNote(78, 48);
tracks[0].addNote(76, 48);
tracks[0].addNote(74, 48);
tracks[0].addNote(73, 48);
tracks[0].addNote(71, 48);
tracks[0].addNote(73, 48);
tracks[0].addNote(74, 48);
tracks[0].addNote(76, 48);
tracks[0].addNote(73, 48);
tracks[0].addNote(69, 48);
dataProvider.connect(tracks[0]);
tracks[1] = new MTrack(dataProvider.getFmt(), playbackBufferSize);
tracks[1].addRest(96);
tracks[1].addNote(62, 192);
tracks[1].addNote(62, 192);
tracks[1].addNote(55, 192);
tracks[1].addNote(61, 192);
dataProvider.connect(tracks[1]);
engine = new PlaybackEngine(dataProvider, playbackBufferSize); //hand the data source to the playback engine, and the buffersize for the engine
engine.addEventListener(PlaybackEngine.READY, engineReady, false, 0, true); //listen for when the engine is ready
}
private function engineReady(event:Event):void {
engine.start(); //commence playback
}
}
}
MMixer.as
package {
import nu.mine.flashnet.sound.core.*;
public class MMixer extends AudioDataProvider {
private var phase:Number;
private var phaseFactor:Number;
private var trackArr:Array;
private var fmt:PCMSoundFormat;
public function MMixer() {
fmt = new PCMSoundFormat(44100, 16, 1);
super(fmt);
phase = 0.0;
phaseFactor = 1000;
trackArr = new Array();
}
public function getFmt():PCMSoundFormat {
return fmt;
}
public function connect(track:MTrack):void {
trackArr.push(track);
}
protected override function fillBuffer(numSamples:int):void {
for(var i:int = 0;i < numSamples; ++i) {
var s:Number = 0;
for each (var track:MTrack in trackArr) {
s += track.getNextSample();
}
buffer.writeMonoSample(s);
}
}
}
}
MTrack.as
package {
import nu.mine.flashnet.sound.core.*;
public class MTrack {
private var m_ch:MChannel; // channel (instrument)
private var m_fmt:PCMSoundFormat; //
private var m_playbackBufferSize:int; //
private var m_needle:int; // global time
private var m_bpm:Number; // beat per minute
private var m_vol:int; // default volume (max:127)
private var m_vel:int; // default velocity (max:127)
private var m_len:int; // default length
private var m_gate:Number; // default gate time (max:1.0)
private var m_events:Array; //
private var m_spt:Number; // samples per tick
private var m_pointer:int; // current event no.
private var m_delta:int;
public function MTrack(fmt:PCMSoundFormat, playbackBufferSize:int) {
m_ch = new MChannel(fmt);
m_fmt = fmt;
m_playbackBufferSize = playbackBufferSize;
m_needle = -4096; // initialize time
m_bpm = 120;
m_vol = 100;
m_vel = 100;
m_len = 96; // quater note
m_gate = 0.8;
m_events = new Array();
var tps:Number = m_bpm * 96 / 60; // ticks per second (quater note = 96ticks)
m_spt = 44100 / tps; // samples per tick
m_pointer = 0;
m_delta = 0;
}
public function getNextSample():Number {
if (m_pointer < m_events.length) {
var e:MEvent = m_events[m_pointer];
var delta:Number = m_events[m_pointer].getDelta() * m_spt;
if (m_needle >= delta) {
switch(e.getStatus()) {
case MStatus.NOTE_ON:
m_ch.noteOn(MEvent.getFrequency(e.getNoteNo()));
break;
case MStatus.NOTE_OFF:
m_ch.noteOff();
break;
case MStatus.EOT:
m_ch.noteOff();
break;
default:
break;
}
trace(m_pointer+": "+e.getStatus()+": "+e.getNoteNo()+": "+m_needle+", "+delta);
m_needle = 0;
m_pointer++;
}
}
m_needle += 1.0;
return m_ch.getNextSample();
}
public function seek(delta:int):void {
m_delta = delta;
}
public function addNote(noteNo:int, len:int = -1, vel:int = -1):void {
if (len < 0) len = m_len;
if (vel < 0) vel = m_vel;
var e0:MEvent = new MEvent();
e0.setNoteOn(noteNo, vel);
e0.setDelta(m_delta);
m_events.push(e0);
var e1:MEvent = new MEvent();
var gate:int = len * m_gate;
seek(gate);
e1.setNoteOff(noteNo, vel);
e1.setDelta(m_delta);
m_events.push(e1);
seek(len - gate);
}
public function addRest(len:int = -1):void {
if (len < 0) len = m_len;
seek(len);
}
public function setVolume(vol:int):void {
var e:MEvent = new MEvent();
e.setVolume(vol);
m_events.push(e);
}
public function setTempo(tempo:Number):void {
var e:MEvent = new MEvent();
e.setTempo(tempo);
m_events.push(e);
}
}
}
MChannel.as
package {
import nu.mine.flashnet.sound.core.*;
import nu.mine.flashnet.sound.synthesis.*;
import nu.mine.flashnet.sound.synthesis.tones.*;
public class MChannel {
private var envelope:ADSREnvelope;
private var tone:ToneGenerator;
private var vel:Number;
public function MChannel(soundFormat:PCMSoundFormat, type:String = "sine") {
envelope = new ADSREnvelope(0, 0.4, 0.2, 0, soundFormat);
tone = new SineToneGenerator(soundFormat, 440.0);
vel = 0.5;
}
public function setVel(v:Number):void {
vel = v;
}
public function noteOn(frequency:Number):void {
tone.setPitch(frequency);
envelope.triggerEnvelope();
}
public function noteOff():void {
envelope.releaseEnvelope();
}
public function getNextSample():Number {
return tone.getNextSample() * envelope.getNextAmplitude() * vel;
}
}
}
MEvent.as
package {
public class MEvent {
private var m_delta:int;
private var m_status:int;
private var m_data0:int;
private var m_data1:int;
public function MEvent() {
set(MStatus.NOP, 0, 0);
}
public function set(status:int, data0:int, data1:int):void {
m_status = status;
m_data0 = data0;
m_data1 = data1;
}
public function setEOT():void { set(MStatus.EOT, 0, 0); }
public function setNoteOn(noteNo:int, vel:int):void { set(MStatus.NOTE_ON, noteNo, vel); }
public function setNoteOff(noteNo:int, vel:int):void { set(MStatus.NOTE_OFF, noteNo, vel); }
public function setTempo(tempo:int):void { set(MStatus.TEMPO, tempo, 0); }
public function setVolume(vol:int):void { set(MStatus.VOLUME, vol, 0); }
public function setDelta(delta:int):void { m_delta = delta; }
public function getStatus():int { return m_status; }
public function getDelta():int { return m_delta; }
public function getNoteNo():int { return m_data0; }
public function getVelocity():int { return m_data1; }
public function getTempo():int { return m_data0; }
public function getVolume():int { return m_data0; }
public static function getFrequency(noteNo:int):Number {
return frequencyMap[noteNo];
}
private static const frequencyMap:Array = new Array(
// octave -2
8.18, // C 0
8.66, // C# 1
9.18, // D 2
9.73, // D# 3
10.30, // E 4
10.92, // F 5
11.56, // F# 6
12.25, // G 7
12.98, // G# 8
13.75, // A 9
14.57, // A# 10
15.44, // B 11
// octave -1
16.35, // C 12
17.32, // C# 13
18.35, // D 14
19.45, // D# 15
20.60, // E 16
21.83, // F 17
23.12, // F# 18
24.50, // G 19
25.96, // G# 20
27.50, // A 21
29.14, // A# 22
30.87, // B 23
// octave 0
32.70, // C 24
34.65, // C# 25
36.71, // D 26
38.89, // D# 27
41.20, // E 28
43.65, // F 29
46.25, // F# 30
49.00, // G 31
51.91, // G# 32
55.00, // A 33
58.27, // A# 34
61.74, // B 35
// octave 1
65.41, // C 36
69.30, // C# 37
73.42, // D 38
77.78, // D# 39
82.41, // E 40
87.41, // F 41
92.50, // F# 42
98.00, // G 43
103.83, // G# 44
110.00, // A 45
116.54, // A# 46
123.47, // B 47
// octave 2
130.81, // C 48
138.59, // C# 49
146.83, // D 50
155.56, // D# 51
164.81, // E 52
174.61, // F 53
185.00, // F# 54
196.00, // G 55
207.65, // G# 56
220.00, // A 57
233.08, // A# 58
246.94, // B 59
// octave 3
261.63, // C 60
277.18, // C# 61
293.66, // D 62
311.13, // D# 63
329.63, // E 64
349.23, // F 65
369.99, // F# 66
392.00, // G 67
415.30, // G# 68
440.00, // A 69
466.16, // A# 70
493.88, // B 71
// octave 4
523.25, // C 72
554.37, // C# 73
587.33, // D 74
622.25, // D# 75
659.26, // E 76
698.46, // F 77
739.99, // F# 78
783.99, // G 79
830.61, // G# 80
880.00, // A 81
932.33, // A# 82
987.77, // B 83
// octave 5
1046.50, // C 84
1108.73, // C# 85
1174.66, // D 86
1244.51, // D# 87
1318.51, // E 88
1396.91, // F 89
1479.98, // F# 90
1567.98, // G 91
1661.22, // G# 92
1760.00, // A 93
1864.66, // A# 94
1975.53, // B 95
// octave 6
2093.00, // C 96
2217.46, // C#
2349.32, // D
2489.02, // D#
2637.02, // E
2793.83, // F
2959.96, // F#
3135.96, // G
3322.44, // G#
3520.00, // A
3729.31, // A#
3951.07, // B
// octave 7
4186.01, // C 108
4434.92, // C# 109
4698.64, // D 110
4678.03, // D# 111
5274.04, // E 112
5587.65, // F 113
5919.91, // F# 114
6271.93, // G 115
6644.88, // G# 116
7040.00, // A 117
7458.62, // A# 118
7902.13, // B 119
// octave 8
8372.02, // C 120
8869.84, // C# 121
9397.27, // D 122
9956.06, // D# 123
10548.08, // E 124
11175.30, // F 125
11839.82, // F# 126
12543.85, // G 127
13289.75, // G# 128
14080.00, // A 129
14917.24, // A# 130
15804.27 // B 131
);
}
}
MStatus.as
package {
public final class MStatus {
public static const EOT:int = 0;
public static const NOP:int = 1;
public static const NOTE_ON:int = 2;
public static const NOTE_OFF:int = 3;
public static const TEMPO:int = 4;
public static const VOLUME:int = 5;
}
}
« 『ぼんくら』(宮部みゆき) | トップページ | 『トランスフォーマー』 »
「ActionScript 3.0」カテゴリの記事
- FlMML - リングモジュレーターとSync(2009.07.26)
- FlMML - ファミコンDPCM(2009.05.16)
- キー入力のレベル、トリガ、リピートを取得(2009.05.06)
- FlMML - エクスプレッション(2009.05.03)
- FlMML - 引数つきマクロ(2009.04.12)
「FlMML」カテゴリの記事
- FlMMLリポジトリの引越し(2011.02.05)
- FlMML - リングモジュレーターとSync(2009.07.26)
- MML対応Twitterクライアント(2009.07.24)
- FlMML - DPCM変換ツール(2009.05.17)
- FlMML - ファミコンDPCM(2009.05.16)
この記事へのコメントは終了しました。
コメント