フレーズ生成版 forked from: SiON OPM Random FM Tone
ランダムFM音色生成装置
(SiONのFM音源音色エディタとしては「SiOPM音色エディタ」があります)
開発中
生成音色をランダムフレーズで鳴らしてみました
最初に、履歴バッファに自動生成結果を100個格納します(カーソル左右で聴けます)
放っておくと、音色を生成しつつコード進行を2回繰り返したのち、KEYとコード進行を変更します
ボタンはカーソルキー、キー1~9、ZXCVBNでも操作できます
/**
* Copyright cat2151 ( http://wonderfl.net/user/cat2151 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/b5LV
*/
// forked from cat2151's SiON OPM Random FM Tone
// forked from cat2151's SiON OPM Tone MML
// forked from cat2151's SiON TheABCSong2
// ランダムFM音色生成装置
// (SiONのFM音源音色エディタとしては「SiOPM音色エディタ」があります)
// fork元はDT2を使った音や過変調の音など生成される音色にバリエーションがありますが、
// こちらは聴きやすさを優先した音色に限定しています
// (1/100くらいの確率でDT2を使った音程感のない音色や過変調のノイズ系音色も生成します)
// 現状、音色については、稀に過変調に近いnoise音色が出現
// (逆に一定値未満のTLはほとんど生成されないため、ある種の音色がほとんど出現しない可能性も)、
// MUL0がありoctave低く聴こえる音色のarp,chordがbassと衝突、等の問題があります
package {
import flash.net.IDynamicPropertyOutput;
import flash.display.Stage;
import flash.display.AVM1Movie;
import flash.display.Sprite;
import org.si.sion.*; //OPM音色を鳴らすため
import org.si.sion.events.*; //演奏終了event取得のため
import flash.text.TextField; //文字列表示のため
import flash.text.TextFormat; //等幅font表示のため
import flash.events.Event; //TextFieldのScrollEvent取得のため
import flash.events.MouseEvent; //Mouse入力取得のため
import flash.events.KeyboardEvent; //キー入力取得のため
import flash.utils.Timer; //一定時間ごとのEvent取得のため
import flash.events.TimerEvent; //一定時間ごとのEvent取得のため
import flash.system.System; //MMLをclipboardに格納するため
import com.bit101.components.*; //ボタン入力とスクロールバー表示のため
public class playRandomTone extends Sprite {
public var driver:SiONDriver = new SiONDriver();
public var data:SiONData;
private var myTimer:Timer = new Timer(1, 0); //一定時間ごとに動作させるタイマに使う領域
private var tf:TextField = new TextField; //表示用領域 MML表示用
private var tfStatus:TextField = new TextField; //表示用領域 status表示用
private var tfmt:TextFormat = new TextFormat; //表示用領域のtextFormat指定に使う領域
private var vScrollBar:VScrollBar; //MML表示用領域のスクロールバー
private var toneHis:history = new history(1000); //音色履歴用領域
private var mmlCurrent:Array = new Array(6); //音色MML,演奏MML用領域
private var autoNext:Boolean = true; //演奏終了後に自動で次を生成・演奏するか
private var autoToneChange:Boolean = true; //演奏終了後の自動生成時に音色を自動更新するか
private var addLoopf:Boolean = true; //生成MMLに無限ループである$をつけるか
private var oneMeasPlay:Boolean = true; //生成MMLは1小節だけか, コード進行分(例:5小節)一度に生成するか
private var matrixBuf:Array; //フレーズ自動生成用データを格納
private const maxPart:int = 3; //パート数 Arp,Bass,Chordで3パート (領域確保用)
private const maxChannel:int = 8; //パート内最大発音数 (領域確保用)
private const maxStep:int = 16; //パート内最大step数 (領域確保用)
private var currentKey:int = 0; //KEY 0~11 0がC,11がB
private var rootIndex:int = 0; //コード進行index
private var progressArpScale:Array = [ //arp用scale定義 コード進行の種類ごと
[0,3,5,7,10], //k進行用スケール マイナーペンタ
[0,3,5,7,10],
[0,4,5,7,11], //琉球
[0,2,3,7,8], //陰旋法
];
private var progressBsScale:Array = [ //bass用scale定義 コード進行の種類ごと
[0,2,3,5,7,8,10], //k進行用スケール マイナー
[0,2,3,5,7,8,10],
[0,4,5,7,11], //琉球
[0,2,3,5,7,8,10], //陰旋法用 マイナー
];
private var progressBass:Array = [ //bass用コード進行定義 : root : bass用scaleのトニックノートからのoffset(半音単位ではない)
[0,2,3,5,6], //k進行
[0,6-7,5-7,6-7], //※progressBassの要素数は、progress~系全体の要素数とみなされるので注意。ほかもここの要素数に合わせること
[0,3-5,2-5,3-5], //琉球
[0,3-7,4-7,5-7,6-7], //陰旋法用 マイナー
];
private var progressChordRoot:Array = [ //chord用コード進行定義 : root : トニックノートを0として半音単位
[0,3,5,8,10], //k進行
[0,10,8,10],
[0,7,5,7], //琉球
[0,5,7,8,10], //陰旋法用 マイナー
];
private var progressChordTone:Array = [ //chord用コード進行定義 ランダムでocataveや倍音が変化しぶつかりを制御しきれないため、ぶつかりやすい音は外すのが無難
[ [0,3,7,10], //k進行 先頭の要素数をMML生成時の和音数としても利用しているので注意
[0,4,7,11], //※無音はnull
[0,7,10,14],
[0,4,7,11],
[0,7,10,14] ],
[ [0,3,7,10],
[0,4,7,9],
[0,4,7,9],
[0,4,7,10] ],
[ [0,4,7,11], //琉球
[0,4,7,2],
[0,4,7,9],
[0,4,7,10] ],
[ [0,3,7,10], //陰旋法用 マイナー
[0,3,7,10],
[0,3,7,10],
[0,4,7,11],
[0,4,7,10] ],
];
private var progressUnitLength:Array = [ //コード進行用 コード進行indexごとの演奏長さ(16分音符の個数)
[16,16,16,8,8], //k進行
[16,16,16,16],
[16,16,16,16],
[16,16,16,8,8],
];
private var arpScale:Array; //コード進行のcurrent用配列 progressArpScale に対応、以下同様
private var bassScale:Array; //コード進行のcurrent用配列
private var rootBassArray:Array; //コード進行のcurrent用配列
private var rootOfsArray:Array; //コード進行のcurrent用配列
private var chordArray:Array; //コード進行のcurrent用配列
private var writeLengthArray:Array; //コード進行のcurrent用配列
private var progressSeriesArr:Array; //コード進行用の数列を格納する領域
private var generatedArpScale:Array; //生成されたコード進行用配列 progressArpScale に対応、以下同様
private var generatedBassScale:Array; //生成されたコード進行用配列
private var generatedRootBassArray:Array; //生成されたコード進行用配列
private var generatedRootOfsArray:Array; //生成されたコード進行用配列
private var generatedChordArray:Array; //生成されたコード進行用配列
private var generatedWriteLengthArray:Array; //生成されたコード進行用配列
private var writeLengthArrMul:int=4; //writeLengthArrayの要素に乗算する値
private var writeLengthArrDiv:int=4; //writeLengthArrayの要素に除算する値
private var progressNumof:int = progressBass.length; //進行の種類の個数
private var progressTypeIndex:int = 0; //コード進行の種類 現在値index
private var playCount:int = 0; //複数回演奏終了後KEY変更用カウンタ
private var tempo:int = 130;
private var debugmml:String = "";
private var processChordNotenum:processChordNotenum = new processChordNotenum(); //chordのnotenum加工用
private var chordNotenumForArpArray:Array; //arp用 chordのNoteNumをchannel,measごとに格納
private var isChordSyncArp:Boolean = false; //chord音程にあわせてarpフレーズを生成するか
//関数定義
//数値を整数にし文字列3文字になるよう左space埋め(padding)
private function lpad3(inum:int) :String { return (" " + int(inum)).substr(-3,3); }
//文字列を表示する
private function dispText(text:String) :void {
tf.y = 20;
tf.text = text; //表示文字列を更新する
tf.width = 400;
tf.height = 400-20;
tf.wordWrap = true;
tf.multiline = true;
tfmt.font = "MS Gothic";
tf.setTextFormat ( tfmt );
addChild( tf );
}
//文字列を表示する ステータス表示用
private function dispTextStatus(text:String) :void {
tfStatus.y = tf.y + tf.height;
tfStatus.width = tf.width;
tfStatus.height = 50;
tfStatus.text = text; //表示文字列を更新する
tfStatus.multiline = true;
tfStatus.border = true;
addChild( tfStatus );
}
/** TextFieldに合わせてスクロールバーを設定 */
private function adjustScrollBar():void {
var visibleLines:int = tf.numLines - tf.maxScrollV + 1;
var percent:Number = visibleLines / tf.numLines;
vScrollBar.setSliderParams(1, tf.maxScrollV, tf.scrollV);
vScrollBar.setThumbPercent(percent);
vScrollBar.value = tf.scrollV;
//textfieldの横にスクロールバーを表示する
vScrollBar.x = tf.x + tf.width;
vScrollBar.y = tf.y;
vScrollBar.height = tf.height;
}
//MMLを表示する
private function dispMml(mml:String, am:String) :void {
dispText(mml);
dispTextStatus(debugmml + am);
}
//FM音色データ配列を元に、SiON用音色定義MMLを生成する
//td:FM音色データ配列
//part:音色定義番号(例:"0")
private function makeToneMml(td:Array,part:String) :String {
var mml:String = "";
mml += "#OPM@";
mml += part;
mml += " { " + td[0] + ", " + td[1] + ",\n";
for (var op:int=0;op<4;op++) {
if (0==op) {
mml += "//AR DR SR RR SL TL KS MUL DT1 DT2 AMS\n"
}
for (var counter:int=0;counter<11;counter++) {
mml = mml + lpad3(td[2 + 11*op + counter]) + ","
}
mml += "\n";
}
mml += "};\n";
return mml;
}
//ランダムに音色MMLを生成する
// Arp用音色を生成する
private function makeToneMmlArp() :String {
var td:Array = new Array(2+11*4); //ランダム音色データを格納する領域
td = createRandomFMtone(td); //ランダムにFM音色データを生成する
//エンベロープをArp用に再度ランダム生成
for (var op:int=0;op<4;op++){
td[2+11*op+ 0] = Math.random() * 5+25; //AR
td[2+11*op+ 3] = Math.random() * 15+15; //RR
}
if (Math.random() * 100 < 99){
adjustFMtoneTLbyALG_FB(td);//TLを加工する(ノイズ音色防止)
}
return makeToneMml(td,"0");
}
// Bass用音色を生成する
private function makeToneMmlBass() :String {
var td:Array = new Array(2+11*4); //ランダム音色データを格納する領域
td = createRandomFMtone(td); //ランダムにFM音色データを生成する
//エンベロープをBass用に再度ランダム生成
for (var op:int=0;op<4;op++){
td[2+11*op+ 0] = Math.random() * 8+22; //AR
td[2+11*op+ 3] = Math.random() * 7+24; //RR
}
if (Math.random() * 100 < 99){
adjustFMtoneTLbyALG_FB(td);//TLを加工する(ノイズ音色防止)
}
return makeToneMml(td,"1");
}
// Chord用音色を生成する
private function makeToneMmlChord() :String {
var td:Array = new Array(2+11*4); //ランダム音色データを格納する領域
td = createRandomFMtone(td); //ランダムにFM音色データを生成する
//エンベロープをChord用に再度ランダム生成
for (var op:int=0;op<4;op++){
if (!isModulator[td[0]][op]) { //キャリアは減衰遅く
td[2+11*op+ 1] = Math.random() * 8+4; //DR
td[2+11*op+ 2] = Math.random() * 8+4; //SR
}
}
if (Math.random() * 100 < 99){
adjustFMtoneTLbyALG_FB(td);//TLを加工する(ノイズ音色防止)
}
return makeToneMml(td,"2");
}
//フレーズ自動生成用データを初期化
private function initNoteMatrix () :void {
matrixBuf = new Array(maxPart);
for (var part:int=0;part<maxPart;part++) {
matrixBuf[part] = new Array(maxChannel);
for (var channel:int=0;channel<maxChannel;channel++) {
matrixBuf[part][channel] = new Array(maxStep);
for (var step:int=0;step<maxStep;step++) {
matrixBuf[part][channel][step] = {
note: 0 as int,
q: 8 as int
};
}
}
}
}
//あるステップのデータを別ステップへ上書きコピー
private function copyNoteMatrixStep (matrixBuf:Array,part:int,channel:int,fromStep:int,toStep:int) :void {
matrixBuf[part][channel][toStep].note =
matrixBuf[part][channel][fromStep].note;
matrixBuf[part][channel][toStep].q =
matrixBuf[part][channel][fromStep].q;
}
//scaleとnoteを元にnoteNumberを算出する
private function calcNoteNumber(scale:Array,note:int,notenumOfTonicNote:int) :int {
//noteは0,1,2...でAマイナペンタなら69,69+3,69+5...
var notenumOfs:int = 0;
var newNote:int = 0;
while (note<0){ //マイナスの場合の補正
note += scale.length;
notenumOfs -= 12;
}
notenumOfs += scale[note % scale.length];
notenumOfs += int(note / scale.length) * 12;
newNote = notenumOfTonicNote + notenumOfs;
return newNote;
}
//NoteNumberの属するoctaveを得る
private function calcOctave(noteNumber:int) :int {
return int(noteNumber / 12);
}
//oldNoteNumberのoctaveとnewNumber2のoctaveを調べ,
//octaveが相違していれば<>を得る
private function getOctMML(oldNoteNumber:int,newNoteNumber:int) :String {
var oldo:int = calcOctave(oldNoteNumber);
var newo:int = calcOctave(newNoteNumber);
var o:int = oldo;
var mml:String = "";
while (o!=newo){
if (o>newo){
mml += ">";
o--;
}else if (o<newo){
mml += "<";
o++;
}
}
return mml;
}
//NoteNumber,oldNoteNumberを元に音程MMLを得る
private function getNoteMML(noteNumber:int,oldNoteNumber:int) :String {
var mml:String = "";
var noteArray:Array = ["c","c+","d","d+","e","f","f+","g","g+","a","a+","b"];
mml += getOctMML(oldNoteNumber,noteNumber);
mml += noteArray[noteNumber % 12];
return mml;
}
//matrixを元にMMLを生成する
private function matrixToMML (matrixBuf:Array,part:int,channel:int,scale:Array,tonicNoteNum:int,defaultNoteNum:int,writeLength:int,isNoteNumMatrix:Boolean) :String {
var mml:String = "";
var nowNoteNumber:int = 0;
var oldNoteNumber:int = defaultNoteNum;
var step:int = 0; //カウンタ
var j:int = 0; //カウンタ
var sameStep:int = 0;
var bufsize:int = writeLength;
for (step=0;step<bufsize;step++) {
//note
if (isNoteNumMatrix) {
//matrixのデータをノートナンバー0~127とみなして取得する
nowNoteNumber = matrixBuf[part][channel][step].note;
//前提:scaleおよびtonicNum(keyから算出)による加工を事前に行っていること
}else{
//matrix,scale,tonicNum(keyから算出)を元にノートナンバー0~127を算出する
nowNoteNumber = calcNoteNumber(scale,matrixBuf[part][channel][step].note,tonicNoteNum);
}
//q, 休符, tie
if (-2 == matrixBuf[part][channel][step].q) {
//-2だけは休符に
mml += getOctMML(oldNoteNumber,nowNoteNumber); //休符でもoctは上下させる,でないと小節最初が休符などの場合oct整合がとれない
mml += "r";
//休符が続く個数を数える
sameStep = 1;
for (j=step+1;j<bufsize;j++){
if(matrixBuf[part][channel][j].q!=-2){
break;
}
sameStep++;
}
//音長MMLを生成
if (16==sameStep){
mml += "1";
step += sameStep-1; //loop変数制御注意
}else if(12==sameStep){
mml += "2.";
step += sameStep-1; //loop変数制御注意
}else if(8==sameStep){
mml += "2";
step += sameStep-1; //loop変数制御注意
}else if(6==sameStep){
mml += "4.";
step += sameStep-1; //loop変数制御注意
}else if(4==sameStep){
mml += "4";
step += sameStep-1; //loop変数制御注意
}else if(3==sameStep){
mml += "8.";
step += sameStep-1; //loop変数制御注意
}else if(2==sameStep){
mml += "8";
step += sameStep-1; //loop変数制御注意
}
}else{
//タイや普通のqはノートを生成
mml += getNoteMML(nowNoteNumber,oldNoteNumber);
//noteの後
if (-1 == matrixBuf[part][channel][step].q) {
//tieの場合
//同じnoteが続く個数を数える
sameStep = 1;
for (j=step+1;j<bufsize;j++){
if(matrixBuf[part][channel][j].note!=matrixBuf[part][channel][step].note){
//違うnoteならloop脱出
break;
}else if(matrixBuf[part][channel][j].q==-2){
//休符ならloop脱出
break;
}else if(matrixBuf[part][channel][j].q!=-1){
//tieでない場合
sameStep++; //個数には加えるがloop脱出(注意)
break;
}
sameStep++;
}
//音長MMLまたはtieを生成
if (16==sameStep){
mml += "1";
step += sameStep -1; //loop変数制御注意
}else if (12==sameStep){
mml += "2.";
step += sameStep -1; //loop変数制御注意
}else if (8==sameStep){
mml += "2";
step += sameStep -1; //loop変数制御注意
}else if (6==sameStep){
mml += "4.";
step += sameStep -1; //loop変数制御注意
}else if (4==sameStep){
mml += "4";
step += sameStep -1; //loop変数制御注意
}else if (3==sameStep){
mml += "8.";
step += sameStep -1; //loop変数制御注意
}else if (2==sameStep){
mml += "8";
step += sameStep -1; //loop変数制御注意
}else{
mml += "&";
}
}
}
oldNoteNumber = nowNoteNumber;
}
return mml;
}
//tonicNoteNumberを、defaultNoteNumberのoctaveにあるように調整する
// tonicNoteNumberは半音単位の数値
private function adjustTonicNoteNumber(tonicNoteNumber:int,defaultNoteNumber:int) :int {
while (tonicNoteNumber<defaultNoteNumber){ //tonicNoteNumberがdefaultを下回らないようにする
tonicNoteNumber += 12;
}
while (tonicNoteNumber>defaultNoteNumber+12){ //tonicNoteNumberがdefault+12を上回らないようにする
tonicNoteNumber -= 12;
}
return tonicNoteNumber;
}
//ランダムフレーズ生成:Arp用
// scaleを元にフレーズを1小節分生成、matrixに書き込み、MMLを出力する
private function makeRndArp(matrixBuf:Array,scale:Array,portamento:Boolean,oofs:int,writeLength:int) :String {
const part:int = 0;
const channel:int = 0;
var mml:String = ""; //出力バッファ
var bufsize:int = writeLength;
var note:int = 0; //loop内で値を維持したまま使われる
var oldnote:int = 0;
var q:int = 0; //loopごとに初期化される
var r:int = 0; //次のnoteを決める際の乱数格納
for (var step:int=0;step<bufsize;step++) {
q = 4; //初期化
if (0==step){ //note初期化
note = int(Math.random() * scale.length); //bassがtonicNoteなので、arpはtonicNote開始でなくてもいい
oldnote = note;
}else{
//noteの決定
r = Math.random() * 100;
if ( r < 90 ) { //上に向かう
note += 1 + int(Math.random() * (scale.length -1));
}else if ( r < 95 ) { //noteは前stepと同じままにし、前stepとtieで結ぶ
matrixBuf[part][channel][step-1].q = -1;
}else{ //下がる
note --;
}
//上は2octラップアラウンド、下は1octラップアラウンド
// 2oct上のtonicNoteを最高音として、それを超えた瞬間2oct下げる
if (note>scale.length * 2){ note -= scale.length*2; }
if (note<0){ note += scale.length; }
}
//q,tieの決定
if ( !portamento ) { //ポルタメントかけない場合のtie
if ( Math.random() * 100 < 10 ) {
q = -1; //tie
}
}else{ //ポルタメントかける場合のtie
if ( step>0 ){ //前のnoteからの推移が1なら前のnoteにportamentoをかける
if (Math.abs(note - oldnote) < 2){
matrixBuf[part][channel][step-1].q = -1; //tie
}
}
}
//決定したnoteを元にmatrixに書き込み
matrixBuf[part][channel][step].note = note + (oofs * scale.length);
matrixBuf[part][channel][step].q = q;
oldnote = note;
}
matrixBuf[part][channel][bufsize-1].q = 4; //最後はtieにしない
mml += "o5";
const defaultNoteNumber:int = 12*5; //直前のo5にあわせる
var tonicNoteNumber:int = currentKey;
tonicNoteNumber = adjustTonicNoteNumber(tonicNoteNumber,defaultNoteNumber);
if (portamento){
mml += "po3";
}
mml += matrixToMML(matrixBuf,part,channel,scale,tonicNoteNumber,defaultNoteNumber,bufsize,false);
return mml;
}
//ランダムにarpの音符MMLを生成する
// 上昇, ランダム フレーズ生成関数を呼び出す
private function makeOnpuMmlArpUpRnd() :String {
const part:int = 0;
var mml:String = ""; //出力バッファ
var scale:Array = arpScale; //note生成のためのスケールを指定
var portamento:Boolean = false;
var writeLength:int;
var oofs:int = 0; //octave offset
var r:int = 0; //乱数ワーク
r = Math.random() * 100;
if (r<50){ portamento = true; } //ポルタメント
r = Math.random() * 100;
if (r<10){ oofs --; } //稀にoctave下げる:ローインターバルリミットに抵触しがちなので多用できない
//設定値を元にmatrixに書き込み、matrixToMML
if (oneMeasPlay){ //1小節だけ生成
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeRndArp(matrixBuf,scale,portamento,oofs,writeLength);
}else{ //コード進行ぶん(例:5小節)生成
rootIndex = 0;
for (var i:int=0;i<rootBassArray.length;i++){
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeRndArp(matrixBuf,scale,portamento,oofs,writeLength);
mml += "\n";
progressChord();
}
}
mml += ";";
if (oneMeasPlay){ //1小節だけ生成の場合
mml += "\n";
}
return mml;
}
//arpの音符MMLを生成する
// chord音程に合わせたアルペジオを生成する
// chordNotenumForArpArrayを元に、
// 1小節分生成、matrixに書き込み、MML生成
private function makeArpFromChord(writeLength:int, chordMeas:int) :String {
const part:int = 0;
const channel:int = 0;
var mml:String = ""; //出力バッファ
var chordChannelSize:int = chordNotenumForArpArray.length; //和音数
var chordChannel:int = 0; //カウンタ
var scale:Array = arpScale; //note生成のためのスケールを指定 ダミー
var oofs:int = 0;
var isUp:Boolean = true;
//matrixBufへ、chordNotenumForArpArrayから、
// notenumモードで書き込み
// アルペジエータ(和音からアルペジオを生成)
// arp type : 2oct updown
for (var step:int=0;step<writeLength;step++){
//chordChannel, isUp決定
if (0 == step){
chordChannel = 0;
isUp = true;
} else {
if (isUp){ //up
chordChannel++;
if (chordChannel >= chordChannelSize){ //type : 2oct up
chordChannel = 0;
oofs += 12;
if (oofs>=24){
isUp = false; //downへ
}
}
} else { //down
chordChannel --;
if (chordChannel < 0){
chordChannel = chordChannelSize -1;
oofs -= 12;
} else if (chordChannel <= 1 && oofs <= 0){
isUp = true; //upへ
}
}
}
//chordChannelを元に、notenum決定
var notenum:int = chordNotenumForArpArray[chordChannel][chordMeas] + oofs;
//noteを元に、matrixBufへ書き込み
matrixBuf[part][channel][step].note = notenum;
matrixBuf[part][channel][step].q = 4;
}
mml += "o5";
const defaultNoteNumber:int = 12*5; //直前のo5にあわせる
var tonicNoteNumber:int = currentKey;
tonicNoteNumber = adjustTonicNoteNumber(tonicNoteNumber,defaultNoteNumber);
mml += matrixToMML(matrixBuf,part,channel,scale,tonicNoteNumber,defaultNoteNumber,writeLength,true);
return mml;
}
//arpの音符MMLを生成する
// アルペジオ生成関数を呼び出す
private function makeOnpuMmlArpFromChord() :String {
var mml:String = ""; //出力バッファ
var writeLength:int;
if (null == chordNotenumForArpArray){
makeOnpuMmlChord(); //配列が確保されていない場合は、初期化する
//makeOnpuMmlChordからarpを呼び出さないよう注意(無限再起呼び出しになってしまう)
}
//設定値を元にmatrixに書き込み、matrixToMML
if (oneMeasPlay){ //1小節だけ生成
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeArpFromChord(writeLength,rootIndex);
}else{ //コード進行ぶん(例:5小節)生成
rootIndex = 0;
for (var i:int=0;i<rootBassArray.length;i++){
//writeLength算出のためコード進行処理を行う
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeArpFromChord(writeLength,i);
mml += "\n";
progressChord();
}
}
mml += ";";
if (oneMeasPlay){ //1小節だけ生成の場合
mml += "\n";
}
return mml;
}
//arpの音符MMLを生成する
private function makeOnpuMmlArp() :String {
var mml:String = ""; //出力バッファ
mml += "%6@0 ";
mml += "@v0,32,64";
if (addLoopf) { mml += "$"; } //無限ループ演奏
mml += "v5l16q4";
if (isChordSyncArp){ //chord音程に合わせたアルペジオ
mml += makeOnpuMmlArpFromChord();
} else { //上昇, ランダム
mml += makeOnpuMmlArpUpRnd();
}
return mml;
}
//ランダムフレーズ生成:Bass用 matrixBufに書き込み
// 現在生成されるフレーズはrootと導音がメイン
private function makeRndBass(matrixBuf:Array,writeLength:int) :String {
const part:int = 1;
const channel:int = 0;
var mml:String = ""; //出力バッファ
var bufsize:int = writeLength;
var rep:int = Math.random() * 100; //繰り返しでフレーズを作るか
var scale:Array = bassScale; //note生成のためのスケールを指定する
var oofs:int=0, oldoofs:int=0, noteofs:int=0; //octave offset, note offset
var STf:Boolean=false, oldSTf:Boolean=false; //noteを導音にするかのフラグ
var note:int=0, q:int=0; //書き込み用
for (var step:int=0;step<bufsize;step++) {
//初期化
note = rootBassArray[rootIndex]; //root
noteofs = 0; //root
oofs = 0;
STf = false;
q = 6;
//note offset決定
if ( Math.random() * 100 < 10 ) {
//導音
if (step>1 && step<bufsize-1){ //1つめ2つめはrootにするので、それ以降で
STf = true;
noteofs --;
}
}
//octave offset決定
if ( Math.random() * 100 < 10 ) {
if (step>1){ //1つめ2つめはrootにするので、それ以降で
oofs = 1;
}
}
if ( rep > 80 ) {
//繰り返しでフレーズ生成する場合さらにoctave増やす
if ( Math.random() * 100 < 50 ) {
if (step>1){ //1つめ2つめはrootにするので、それ以降で
oofs = 1;
}
}
}
//前の音がoctave高い導音で、現在がoctave低いrootなら、現在をoctave高いrootにする
if (oldSTf==true && oldoofs==1 && STf==false && oofs==0){
oofs = 1;
if (matrixBuf[part][channel][step-1].q!=-2){
matrixBuf[part][channel][step-1].q = -1; //前の音を加工:休符でないならtieにする
}
}
//offsetを元にnote設定
note += noteofs; //root or 導音
note += oofs * scale.length; //octave
//q,休符,tie
if ( Math.random() * 100 < 10 ) {
q = 4;
}
if ( Math.random() * 100 < 10 ) {
if (step>0) { //小節最初は0にしないので、それ以外で
q = -2;
}
}
if ( Math.random() * 100 < 50 ) {
q = -1; //tie
}
//note,qをmatrixに書き込み
matrixBuf[part][channel][step].note = note;
matrixBuf[part][channel][step].q = q;
oldSTf = STf;
oldoofs = oofs;
}
if ( rep > 90 ) {
//符点八分ぶんをコピー
matrixBuf[part][channel][5].q = 6; //tieにしない
copyNoteMatrixStep(matrixBuf,part,channel, 0,6+0);
copyNoteMatrixStep(matrixBuf,part,channel, 1,6+1);
copyNoteMatrixStep(matrixBuf,part,channel, 2,6+2);
copyNoteMatrixStep(matrixBuf,part,channel, 3,6+3);
copyNoteMatrixStep(matrixBuf,part,channel, 4,6+4);
copyNoteMatrixStep(matrixBuf,part,channel, 5,6+5);
}else if ( rep > 80 ) {
//1拍目を残り3拍にもコピー
matrixBuf[part][channel][3].q = 6; //tieにしない
for (step=4;step<bufsize;step+=4) {
copyNoteMatrixStep(matrixBuf,part,channel, 0,step+0);
copyNoteMatrixStep(matrixBuf,part,channel, 1,step+1);
copyNoteMatrixStep(matrixBuf,part,channel, 2,step+2);
copyNoteMatrixStep(matrixBuf,part,channel, 3,step+3);
}
}
matrixBuf[part][channel][bufsize-1].q = 6; //最後はtieにしない(基本的に小節頭は強迫にしたい)
//matrixToMML
mml += "o3";
const defaultNoteNumber:int = 12*3; //直前のo3にあわせる
var tonicNoteNumber:int = currentKey;
tonicNoteNumber = adjustTonicNoteNumber(tonicNoteNumber,defaultNoteNumber);
mml += matrixToMML(matrixBuf,part,channel,scale,tonicNoteNumber,defaultNoteNumber,bufsize,false);
return mml;
}
//ランダムにbassの音符MMLを生成する 生成関数を呼び出す
private function makeOnpuMmlBass() :String {
const part:int = 1;
var mmlChannelInit:String = "%6@1 ";
var writeLength:int;
var mml:String = ""; //生成したフレーズを格納
if (addLoopf) { mmlChannelInit += "$"; } //無限ループ演奏
mml += mmlChannelInit;
mml += "v6l16q6";
if (oneMeasPlay){ //1小節だけ生成
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeRndBass(matrixBuf,writeLength);
}else{ //コード進行ぶん(例:5小節)生成
rootIndex = 0;
for (var i:int=0;i<rootBassArray.length;i++){
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeRndBass(matrixBuf,writeLength);
mml += "\n";
progressChord();
}
}
mml += ";";
if (oneMeasPlay){ //1小節だけ生成の場合
mml += "\n";
}
return mml;
}
//ランダムフレーズ生成:Chord用 matrixBufに書き込み MML出力
// 指定note及びrhythmを元に、1channel分、1小節分を生成
private function makeRndChord(channel:int,scale:Array,note:int,rhythmArray:Array,writeLength:int) : String {
const part:int = 2;
var mml:String = ""; //出力バッファ
var bufsize:int = writeLength;
var step:int = 0; //書き込みstepカウンタ
var isNullNote:Boolean = false;
if (-1==note){ //nullの場合
isNullNote = true;
}
if (!isNullNote){ //nullでない場合
for (step=0;step<bufsize;step++) {
//生成済みNoteNumberを直接書き込む
matrixBuf[part][channel][step].note = note;
matrixBuf[part][channel][step].q = rhythmArray[step];
}
matrixBuf[part][channel][bufsize-1].q = 6; //最後はtieにしない
}else{ //nullの場合
for (step=0;step<bufsize;step++) {
matrixBuf[part][channel][step].q = -2;//休符
}
}
mml += "o3";
const defaultNoteNumber:int = 12*3; //直前のo3にあわせる
var tonicNoteNumber:int = currentKey;
tonicNoteNumber = adjustTonicNoteNumber(tonicNoteNumber,defaultNoteNumber);
mml += matrixToMML(matrixBuf,part,channel,scale,tonicNoteNumber,defaultNoteNumber,bufsize,true);
return mml;
}
//chord用 ノートナンバーを全channel1小節ぶんだけ生成して配列chordNotenumArrの所定の位置に書き込む
// currentKey,rootIndex, rootOfsArray,chordArrayを元に生成
private function makeChordNotenumber1meas(chordNotenumArr:Array,part:int,chordSize:int,rootIndex:int,meas:int) :void {
var channel:int = 0; //カウンタ
var note:int = 0;
//和音数ぶん、配列要素に書き込み
//まだnotenumberではなくoffset
//chordArrayをもとに和音の元を生成、0~11の値に丸める
for (channel=0;channel<chordSize;channel++){
if (null==chordArray[rootIndex][channel]){ //コード進行設定から取得
//nullの場合
note = -1; //数値系変数はnullとの比較ができない(配列要素は可)ため、-1を設定する
}else{
//nullでない場合
note = chordArray[rootIndex][channel] % 12;
//前提:scaleの添字がマイナスにならないよう、chord配列の要素数=最大和音数を注意して設定済みであること
}
chordNotenumArr[channel][meas] = note;
}
//keyを元にrootNoteNumberを決める ※tonicNoteNumberではなくコードの根音のnoteNumberを決める
const defaultNoteNumber:int = 12*3; //o3にあわせる
var rootNoteNumber:int = currentKey + rootOfsArray[rootIndex]; //keyに、コードの根音を示す値(scaleのtonicからのoffset)を加算する
rootNoteNumber = adjustTonicNoteNumber(rootNoteNumber,defaultNoteNumber); //tonicNote用関数流用
//rootNoteNumberを和音の元に加算し、notenumber形式にする
for (channel=0;channel<chordSize;channel++){
if (chordNotenumArr[channel][meas]!=-1){ //nullでない場合
chordNotenumArr[channel][meas] += rootNoteNumber;
}
}
//備考:この段階ではo3c付近のコード
}
// chordArray及びコード進行用設定を元に、
// chordNotenum配列を生成する
private function makeChordNoteNum(part:int,chordSize:int) : Array {
var chordNotenumArr:Array; //出力バッファ
//出力領域確保
chordNotenumArr = new Array(chordSize);
for (var channel:int=0;channel<chordSize;channel++){
chordNotenumArr[channel] = new Array;
}
//全小節全channel分のchordNotenumを生成する
var rootIndexTaihi:int = rootIndex;
rootIndex = 0;
for (var meas:int=0;meas<rootBassArray.length;meas++){
//chord生成のため、chord構成音だけのscaleを指定
makeChordNotenumber1meas(chordNotenumArr,part,chordSize,rootIndex,meas);
progressChord(); //rootIndex変化
}
rootIndex = rootIndexTaihi;
return chordNotenumArr;
}
//指定サイズのリズムを生成 chord用
private function makeChordRhythm(bufsize:int) :Array {
var rhythmArray:Array = new Array(bufsize); //出力バッファ bufsize求めたあとで確保
//chordのrhythmを乱数で設定
if (Math.random()*100 < 50) {
for (var step:int=0;step<bufsize;step++) {
var r:int = Math.random()*100;
if (r<40){
rhythmArray[step] = -1;
}else if(r<50){
rhythmArray[step] = 6;
}else{
rhythmArray[step] = -2;
}
}
}else{ //全音符
for (step=0;step<bufsize;step++) {
rhythmArray[step] = -1;
}
}
return rhythmArray;
}
//ランダムにchordの音符MMLを生成する
private function makeOnpuMmlChord() :String {
const part:int = 2;
var mml:String = ""; //出力バッファ
var mmlChannelInit:String = "%6@2 ";
if (addLoopf) { mmlChannelInit += "$"; } //無限ループ演奏
var scale:Array; //note生成のためのスケール
var note:int = 0; //音程
var writeLength:int;
var chordNotenumArray:Array; //channel,measごとのコード構成音
var rhythmArray:Array; //コードのリズム
var chordSize:int = chordArray[0].length; //最大和音数
rhythmArray = makeChordRhythm(matrixBuf[part][0].length);
//予め全小節全channel分のchordNotenumを生成する
chordNotenumArray = makeChordNoteNum(part,chordSize);
//生成したnotenumを加工する
// o3くらいのchordをo5くらいにシフトする
processChordNotenum.shift(chordNotenumArray,2*12); //o5くらい
// コード転回処理 notenumを加工する
processChordNotenum.inversion(chordNotenumArray);
// arp用にコピーする (転回後、drop前のもの)
chordNotenumForArpArray = processChordNotenum.copy(chordNotenumArray);
// dropする
var d:int = Math.random() * 100;
if (d<20){
processChordNotenum.drop(chordNotenumArray,1); //drop2
}else if (d<40){
processChordNotenum.drop(chordNotenumArray,2); //drop3
}else if (d<60){
processChordNotenum.drop(chordNotenumArray,1); //drop2&4
processChordNotenum.drop(chordNotenumArray,3);
} //dの残りはclosed
processChordNotenum.sort(chordNotenumArray); //drop後にsortしてMMLを読みやすくする
//channelごとにMML出力 生成したnotenumを元に関数呼び出し
var notenum:int = 0;
var meas:int = 0;
for (var channel:int=0;channel<chordSize;channel++){
mml += mmlChannelInit;
mml += "v4l16";
if (oneMeasPlay){ //1小節だけ生成
//全小節全channel分のchordNotenumを元に、
//matrixを生成、MMLを出力
meas = rootIndex;
notenum = chordNotenumArray[channel][meas];
scale = chordArray[rootIndex]; //現在未使用。使う場合がきたときのため算出はしておく
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeRndChord(channel,scale,notenum,rhythmArray,writeLength);
}else{ //コード進行ぶん(例:5小節)生成
rootIndex = 0;
//1小節ごとに
for (meas=0;meas<rootBassArray.length;meas++){
//全小節全channel分のchordNotenumを元に、
//matrixを生成、MMLを出力
// writeLength生成のためコード進行処理を行う
notenum = chordNotenumArray[channel][meas];
scale = chordArray[rootIndex]; //現在未使用。使う場合がきたときのため算出はしておく
writeLength = writeLengthArray[rootIndex] * writeLengthArrMul / writeLengthArrDiv;
mml += makeRndChord(channel,scale,notenum,rhythmArray,writeLength);
progressChord();
}
}
mml += ";\n";
}
return mml;
}
//コード進行用数列を文字列化したものの末尾n文字を得る
private function getProgressStringLast(progressSeriesArr:Array, n:int) : String {
return progressSeriesArr.join("").substr(-n,n);
}
// ランダムにコード進行用数列を作成する
private function makeProgressSeries() : Array {
var progressSeriesArr:Array; //出力バッファ
var n:int = 0;
var r:int = 0; //乱数一時格納用
var n0:int = 0; //最初の1個
//出力領域確保
progressSeriesArr = new Array;
//最初の音
if ( Math.random()*100 < 70 ){
n = 6;
} else {
n = 1;
}
progressSeriesArr.push(n);
n0 = n;
//コード進行生成
while (true){
r = Math.random()*100;
switch (n){
case 1: //Iから次のコードへ進行
if (getProgressStringLast(progressSeriesArr,2)=="61" && r<50){
n = 2; //k進行
}else if (getProgressStringLast(progressSeriesArr,6)=="156341" && r<50){ //forカノン進行
r = Math.random()*100;
if ( r < 50 ){
n = 4;
} else {
n = 2;
}
}else{
r = Math.random()*100;
if ( r < 10 ){
n = 4;
} else if ( r < 50 ){
n = 5;
} else {
n = 2;
}
}
break;
case 2:
if (getProgressStringLast(progressSeriesArr,3)=="612" && r<50){
n = 4; //k進行
}else if (getProgressStringLast(progressSeriesArr,7)=="1563412" && r<50){ //forカノン進行
n = 5;
}else if(getProgressStringLast(progressSeriesArr,3)=="362" && r<50){
n = 5;
}else if(getProgressStringLast(progressSeriesArr,2)=="62"){ //forカノン進行(minor) 626にはしない
r = Math.random()*100;
if ( r < 50 ){
n = 3;
} else if ( r < 75 ){
n = 4;
} else {
n = 5;
}
}else if (getProgressStringLast(progressSeriesArr,5)=="63412" && r<50){ //forカノン進行(minor)
n = 6;
}else if(getProgressStringLast(progressSeriesArr,2)=="12"){//12からは6には行かない
r = Math.random()*100;
if ( r < 50 ){ //ツーファイブ
n = 5;
} else {
n = 4;
}
}else{
r = Math.random()*100;
if ( r < 30 ){ //ツーファイブ
n = 5;
} else if ( r < 60 ){
n = 6;
} else {
n = 4;
}
}
break;
case 3:
if (getProgressStringLast(progressSeriesArr,4)=="1563" && r<50){ //forカノン進行
n = 4;
}else if (getProgressStringLast(progressSeriesArr,2)=="63"){ //forカノン進行(minor) 636にはしない
n = 4;
}else{
r = Math.random()*100;
if ( r < 50 ){
n = 4;
} else {
n = 6;
}
}
break;
case 4:
if (getProgressStringLast(progressSeriesArr,5)=="15634" && r<50){ //forカノン進行
r = Math.random()*100;
if ( r < 30 ){
n = 1;
} else if ( r < 70 ){//45のある派生を多めに
n = 5;
} else {
n = 6;
}
}else if (getProgressStringLast(progressSeriesArr,3)=="634" && r<50){ //forカノン進行(minor)
r = Math.random()*100;
if ( r < 50 ){
n = 1;
} else {
n = 5;
}
}else{
n = 5;
}
break;
case 5:
if (getProgressStringLast(progressSeriesArr,2)=="65" && r<50){
n = 4;
}else if (getProgressStringLast(progressSeriesArr,2)=="15" && r<50){ //forカノン進行
n = 6;
}else{
r = Math.random()*100;
if ( r < 25 ){
n = 4;
} else if ( r < 50 ){
n = 1;
} else if ( r < 75 ){
n = 6;
} else {
n = 3;
}
}
break;
case 6:
if (getProgressStringLast(progressSeriesArr,3)=="156" && r<50){ //forカノン進行
n = 3;
}else{
r = Math.random()*100;
if ( r < 20 ){
n = 4;
} else if ( r < 40 ){
n = 5;
} else if ( r < 60 ){
n = 2;
} else if ( r < 80 ){
n = 3;
} else {
n = 1;
}
}
break;
}
//配列に挿入する
progressSeriesArr.push(n);
r = Math.random()*100;
if (6<=progressSeriesArr.length){ r = 0; }//長いなら早めにloop脱出
//5かつコード進行数列のサイズが2より大きいなら一定確率でloop脱出
if (5==n && progressSeriesArr.length > 2 && r<50){ break; }
//最初が6かつ、現在3かつ、コード進行数列のサイズが2より大きいなら一定確率でloop脱出
if (6==n0 && 3==n && progressSeriesArr.length > 2 && r<50){ break; }
//k進行になったらloop脱出
if (getProgressStringLast(progressSeriesArr,5)=="61245"){ break; }
}
return progressSeriesArr;
}
// コード進行用数列を元に、コード進行のcurrent用配列を生成する
// 方針:調性感の考慮は省略し、根音を元に、ダイアトニックコードのうちMaj7,min7,7を単純に当てはめる
private function makeProgressCurrentArray(progressSeriesArr:Array) : void {
var n1:int = 0, n2:int = 0; //bass用のofs, chordのroot用のofs
var oldn2:int = 0;
var n2_0:int = 0; //n2の1つ目の値を格納
generatedArpScale = [0,3,5,7,10]; //mPENTA
generatedBassScale = [0,2,3,5,7,8,10]; //マイナースケール(ただし以降のコードネームは平行調のメジャースケールに対するもの)
//progressSeriesArrを元に、各種Array生成
generatedRootBassArray = new Array;
generatedRootOfsArray = new Array;
generatedChordArray = new Array;
generatedWriteLengthArray = new Array;
for (var i:int=0;i<progressSeriesArr.length;i++){
//コード進行数列を元にn1,n2を決定しコードを挿入
switch (progressSeriesArr[i]){
case 1: //I△7
n1 = 2;
n2 = 3;
generatedChordArray.push([0,4,7,11]);
break;
case 2: //IIm7
n1 = 3;
n2 = 5;
generatedChordArray.push([0,3,7,10]);
break;
case 3: //IIIm7
n1 = 4;
n2 = 7;
generatedChordArray.push([0,3,7,10]);
break;
case 4: //IV△7
n1 = 5;
n2 = 8;
generatedChordArray.push([0,4,7,11]);
break;
case 5: //V7
n1 = 6;
n2 = 10;
generatedChordArray.push([0,4,7,10]);
break;
case 6: //VIm7
n1 = 0;
n2 = 0;
generatedChordArray.push([0,3,7,10]);
break;
}
//n1,n2を、前の音からの移動が大きくなりすぎないよう修正
if (n2 - oldn2 >= 8){ //8半音以上上に動く場合
n1 -= generatedBassScale.length;
n2 -= 12;
} else if (n2 - n2_0 >= 12 && n2 - oldn2 >= 5){ //5半音以上上に動き、かつ元のoctaveより高くなる場合
n1 -= generatedBassScale.length;
n2 -= 12;
}
generatedRootBassArray.push(n1);
generatedRootOfsArray.push(n2);
//1小節のstep数
generatedWriteLengthArray.push(16);
oldn2 = n2;
if (0==i){ n2_0 = n2; } //最初の1回のみ保存
}
//1小節のstep数を加工し、2・4・8小節等にできるだけ収める
switch (generatedWriteLengthArray.length){
case 3: for (i=1;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 5: for (i=3;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 6: for (i=2;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 7: for (i=1;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 9: for (i=7;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 10: for (i=6;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 11: for (i=5;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 12: for (i=4;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 13: for (i=3;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 14: for (i=2;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
case 15: for (i=1;i<generatedWriteLengthArray.length;i++){ generatedWriteLengthArray[i] /= 2; }; break;
}
}
//SiON用MMLを生成する
// 前提:mmlCurrentが生成済みであること
private function makeMml(mmlCurrent:Array) :String {
var mml:String = ""; //MML全体を格納する領域
mml += mmlCurrent[0]; //音色MML arp
mml += mmlCurrent[1]; //音色MML bass
mml += mmlCurrent[2]; //音色MML chord
mml += "#EFFECT1{delay300,32,1};";
mml += "#EFFECT2{autopan2.6};";
mml += "t"+tempo+";"
mml += "\n";
mml += mmlCurrent[3]; //演奏MML arp
mml += mmlCurrent[4]; //演奏MML bass
mml += mmlCurrent[5]; //演奏MML chord
return mml;
}
//SiON用MMLを元にSiONで音を鳴らす
private function playMml(mml:String) :void {
data = driver.compile(mml);
driver.play(data);
}
//ランダムにFM音色データを生成し、FM音色データ配列に出力する
private var isModulator:Array = [ //アルゴリズムごとにオペレータのどれがモジュレータかを格納
[ true, true, true, false ], //ALG0
[ true, true, true, false ], //ALG1
[ true, true, true, false ], //ALG2
[ true, true, true, false ], //ALG3
[ true, false, true, false ], //ALG3
[ true, false, false, false ], //ALG5
[ true, false, false, false ], //ALG6
[ false, false, false, false ] //ALG7
];
private function createRandomFMtone(td:Array) :Array {
var freedom:int = Math.random() * 100; //音色のフリーダム度を格納
td[0] = int(Math.random() * 8); //ALG
td[1] = int(Math.random() * 8); //FB
for (var op:int=0;op<4;op++) {
td[2+11*op+ 0] = Math.random() * 20+6; //AR
td[2+11*op+ 1] = Math.random() * 4+8; //DR
td[2+11*op+ 2] = Math.random() * 4+8; //SR
td[2+11*op+ 3] = Math.random() * 4+2; //RR
td[2+11*op+ 4] = Math.random() * 8+8; //SL
if (isModulator[td[0]][op]) {
td[2+11*op+ 5] = Math.random() * 50; //TL (Modulator)
} else {
td[2+11*op+ 5] = 0; //TL (Carrier)
}
td[2+11*op+ 6] = 0; //KS
td[2+11*op+ 7] = Math.random() * 6; //MUL
td[2+11*op+ 8] = Math.random() * 8; //DT1
td[2+11*op+ 9] = 0; //DT2
td[2+11*op+10] = 0; //AMS
if (freedom>98){ //エンベロープを早すぎず遅すぎず、などの制約を外す場合
if (Math.random()*4<1){ td[2+11*op+ 7] = Math.random() * 16; } //MUL
if (freedom>85){
td[2+11*op+ 0] = Math.random() * 32; //AR
td[2+11*op+ 1] = Math.random() * 32; //DR
td[2+11*op+ 2] = Math.random() * 32; //SR
td[2+11*op+ 3] = Math.random() * 16; //RR
td[2+11*op+ 4] = Math.random() * 16; //SL
if (Math.random()*4<1){ td[2+11*op+ 5] = Math.random() * 4; } //TL
if (Math.random()*4<1){ td[2+11*op+ 6] = Math.random() * 4; } //KS
if (Math.random()*4<1){ td[2+11*op+ 9] = Math.random() * 4; } //DT2
if (Math.random()*4<1){ td[2+11*op+10] = Math.random() * 4; } //AMS
if (freedom>95 && Math.random()*4<1){ td[2+11*op+ 5] = Math.random() * 16; } //TL (17~128は省略)
}
}
}
return td;
}
//FM音色配列のTL(TotalLevel)調整 引数を元に調整後の値を取得する
private function adjustFMtoneTL1op(td:Array, op:int, minTL:int) :void {
var _TL:int = td[2+11*op+5];
if (_TL < minTL ) { _TL += minTL; } //TLがminTL未満の場合、minTL~minTL*2の値をとるようにする
// if (_TL < minTL ) { _TL = minTL; } //TLがminTL未満の場合、minTLにする
td[2+11*op+5] = _TL;
}
//FM音色配列のTL(TotalLevel)調整 td配列の値を加工する ALGとFBを元にモジュレータTLの下限値を決める
// 過変調によるノイズや金属音の出る割合を下げる
// 制約:簡易的な調査で決めた便宜上の値であり、加工後の音色の妥当性の検証はあまりされていない
private function adjustFMtoneTLbyALG_FB(td:Array) :void {
var _op:int = 0;
var _FB:int = td[1];
switch (td[0]) {
case 0:
adjustFMtoneTL1op(td, _op++, 20+_FB);
adjustFMtoneTL1op(td, _op++, 20);
adjustFMtoneTL1op(td, _op++, 20);
break;
case 1:
adjustFMtoneTL1op(td, _op++, 10+_FB);
adjustFMtoneTL1op(td, _op++, 10);
adjustFMtoneTL1op(td, _op++, 20);
break;
case 2:
adjustFMtoneTL1op(td, _op++, 15+_FB);
adjustFMtoneTL1op(td, _op++, 20);
adjustFMtoneTL1op(td, _op++, 20);
break;
case 3:
adjustFMtoneTL1op(td, _op++, 10+_FB);
adjustFMtoneTL1op(td, _op++, 15);
adjustFMtoneTL1op(td, _op++, 15);
break;
case 4:
adjustFMtoneTL1op(td, _op++, 10+_FB);
break;
case 5:
adjustFMtoneTL1op(td, _op++, 15+_FB);
break;
case 6:
adjustFMtoneTL1op(td, _op++, 10+_FB);
break;
case 7:
adjustFMtoneTL1op(td, _op++, 0+_FB);
break;
}
}
//音色履歴から演奏、クリップボードにMML格納
private function playHistory(add:int) :void {
//MMLを履歴から取得
var mmlArray:Array = toneHis.get(add); //MML領域に、履歴から取得した値をセット
var mml:String = mmlArray[0]; //MMLを格納する領域
mmlCurrent[0] = mmlArray[1+0];
mmlCurrent[1] = mmlArray[1+1];
mmlCurrent[2] = mmlArray[1+2];
mmlCurrent[3] = mmlArray[1+3];
mmlCurrent[4] = mmlArray[1+4];
mmlCurrent[5] = mmlArray[1+5];
currentKey = mmlArray[1+6+0];
progressTypeIndex = mmlArray[1+6+1];
if (-1 == progressTypeIndex ){
progressSeriesArr = mmlArray[1+6+5];
makeProgressCurrentArray(progressSeriesArr);//progressSeriesArrを元に設定
}
setChordProgressParameter(progressTypeIndex);//progressTypeIndexを元に設定
writeLengthArrMul = mmlArray[1+6+2];
writeLengthArrDiv = mmlArray[1+6+3];
tempo = mmlArray[1+6+4];
playMml(mml); //SiON用MMLを元にSiONで音を鳴らす
System.setClipboard(mml); //clipboardにMMLを格納する
var am:String = ""; //追加表示文字列領域
am += "(履歴"+toneHis.readIndex+"番の音色データを鳴らしました)\n";
am += "(クリップボードにSiON用MMLをコピーしました)\n";
dispMml(mml,am); //MMLとstatusを表示する
autoNext=false; //演奏終了後に次の生成を行わないようにする
}
//コードを進行させる
private function progressChord() : void {
rootIndex++;
rootIndex %= rootOfsArray.length;
}
//ランダムにコード進行typeを設定する
private function setRndChordProgressParameter() :void {
if (Math.random()*100 < 70){
progressSeriesArr = makeProgressSeries();
makeProgressCurrentArray(progressSeriesArr);
progressTypeIndex = -1;
} else {
progressTypeIndex = int(Math.random()* progressNumof);
}
setChordProgressParameter( progressTypeIndex );
}
//コード進行用パラメータを、進行の種類を元に設定する
private function setChordProgressParameter(index:int) :void {
if (-1 != index){
arpScale = progressArpScale[index];
bassScale = progressBsScale[index];
rootBassArray = progressBass[index];
rootOfsArray = progressChordRoot[index];
chordArray = progressChordTone[index];
writeLengthArray = progressUnitLength[index];
} else {
if (null==generatedArpScale){
progressSeriesArr = makeProgressSeries();
makeProgressCurrentArray(progressSeriesArr);
}
arpScale = generatedArpScale;
bassScale = generatedBassScale;
rootBassArray = generatedRootBassArray;
rootOfsArray = generatedRootOfsArray;
chordArray = generatedChordArray;
writeLengthArray = generatedWriteLengthArray;
}
}
//音色生成、演奏、表示、クリップボードに格納
//clipFlag:クリップボードに格納するか
//makePart:-1なら6つとも更新、0~5なら指定した1つだけ更新
private function playAndDisp(clipFlag:Boolean,makePart:int) :void {
var mml:String; //MMLを格納する領域
var am:String = ""; //追加表示文字列領域
am += "(カーソル左右:履歴内の音色データを鳴らす)\n";
if (-1 == makePart) { //6つ全て更新
if (0<playCount/* && (0==playCount%2)*/){
progressChord(); //2回目からはroot進行
}
var loopf:Boolean=false; //規定ループに達したか
if (oneMeasPlay && rootBassArray.length*2==playCount){ loopf = true; }//コード進行が2周したら
if (!oneMeasPlay && playCount > 1){ loopf = true; }//1周
if (loopf){
//キーをランダムに変更
currentKey = int(Math.random()*12);
//コードの種類をランダムに変更
setRndChordProgressParameter();
//拍子の種類をランダムに変更
rndBeat();
playCount = 0;
//ArpTypeをランダムに変更
rndArpType();
}
playCount++;
if (autoToneChange){
//ランダムに音色MML生成
mmlCurrent[0] = makeToneMmlArp();
mmlCurrent[1] = makeToneMmlBass();
mmlCurrent[2] = makeToneMmlChord();
}
//ランダムに演奏MML生成
mmlCurrent[3] = makeOnpuMmlArp();
mmlCurrent[4] = makeOnpuMmlBass();
mmlCurrent[5] = makeOnpuMmlChord();
if (isChordSyncArp){ mmlCurrent[3] = makeOnpuMmlArp(); } //chordにあわせて再度生成する
} else { //1つだけ更新
if (!addLoopf){ play456(); }//無限loopつけないモードで音色やフレーズの変更を行った場合、一旦無限loopフレーズを生成する
//演奏終了後の「6つ全て自動更新」をやめる(消えないように)。また、コード進行つきで生成
autoNext=false; addLoopf=true; oneMeasPlay=false;
switch (makePart) {
case 0: mmlCurrent[0] = makeToneMmlArp(); am+="(Arpの音色を更新しました)"; break;
case 1: mmlCurrent[1] = makeToneMmlBass(); am+="(Bassの音色を更新しました)"; break;
case 2: mmlCurrent[2] = makeToneMmlChord(); am+="(Chordの音色を更新しました)"; break;
case 3:
if (isChordSyncArp){
var oldArpMml:String = mmlCurrent[3];
for (var i:int=0;i<100;i++){ //違うarpが生成されるまで繰り返し生成する
mmlCurrent[5] = makeOnpuMmlChord(); //arp生成にchordが必要なため
mmlCurrent[3] = makeOnpuMmlArp(); //chordにあわせてarpを再度生成する
if (oldArpMml != mmlCurrent[3]){ break; } //違うarpになったらloop脱出
}
am+="(ChordとArpのフレーズを更新しました)";
} else {
mmlCurrent[3] = makeOnpuMmlArp();
am+="(Arpのフレーズを更新しました)";
}
break;
case 4: mmlCurrent[4] = makeOnpuMmlBass(); am+="(Bassのフレーズを更新しました)"; break;
case 5: mmlCurrent[5] = makeOnpuMmlChord();
if (isChordSyncArp){
mmlCurrent[3] = makeOnpuMmlArp(); //chordにあわせて再度生成する
am+="(ChordとArpのフレーズを更新しました)";
} else {
am+="(Chordのフレーズを更新しました)";
}
break;
}
}
mml = makeMml(mmlCurrent); //SiON用MMLを生成する
var mmlArray:Array = new Array(1+6+6); //履歴用
mmlArray[0] = mml;
mmlArray[1+0] = mmlCurrent[0];
mmlArray[1+1] = mmlCurrent[1];
mmlArray[1+2] = mmlCurrent[2];
mmlArray[1+3] = mmlCurrent[3];
mmlArray[1+4] = mmlCurrent[4];
mmlArray[1+5] = mmlCurrent[5];
mmlArray[1+6+0] = currentKey;
mmlArray[1+6+1] = progressTypeIndex;
mmlArray[1+6+2] = writeLengthArrMul;
mmlArray[1+6+3] = writeLengthArrDiv;
mmlArray[1+6+4] = tempo;
mmlArray[1+6+5] = progressSeriesArr;
toneHis.add(mmlArray); //MMLを履歴に保存する
playMml(mml); //SiON用MMLを元にSiONで音を鳴らす
if (clipFlag){ System.setClipboard(mml); } //clipboardにMMLを格納する
if (clipFlag){ am += "(クリップボードにSiON用MMLをコピーしました)\n"; }
dispMml(mml,am); //MMLとstatusを表示する
}
//後述のキー4,5,6を押したのとほぼ同じ処理を行う
private function play456() :void {
autoNext=false; addLoopf=true; oneMeasPlay=false;
mmlCurrent[3] = makeOnpuMmlArp();
mmlCurrent[4] = makeOnpuMmlBass();
playAndDisp(true,5);
}
//ランダムで拍子変更 これから生成するデータに影響
private function rndBeat() :void {
if (Math.random() * 100 < 70){
beat4per4();
}else{
beat6per8();
}
}
//6/8拍子に これから生成するデータに影響
private function beat6per8() :void {
writeLengthArrMul = 3;
writeLengthArrDiv = 4;
tempo = 112;
}
//4/4拍子に これから生成するデータに影響
private function beat4per4() :void {
writeLengthArrMul = 4;
writeLengthArrDiv = 4;
tempo = 130;
}
//ランダムでArpType変更 これから生成するデータに影響
private function rndArpType() :void {
if (Math.random() * 100 < 30){
isChordSyncArp = true;
}else{
isChordSyncArp = false;
}
}
//キー入力判定
private function keyinput(keycode:int) :void {
switch (keycode) {
case 37: playHistory(-1); break; //カーソルキー左
case 39: playHistory( 1); break;
case 49: //フルキー1
case 97: //テンキー1
playAndDisp(true,0); break;
case 50:
case 98:
playAndDisp(true,1); break;
case 51:
case 99:
playAndDisp(true,2); break;
case 52:
case 100:
rndArpType(); //ボタンやキー入力で明示的にフレーズ生成を行う場合はrnd
playAndDisp(true,3); break;
case 53:
case 101:
playAndDisp(true,4); break;
case 54:
case 102:
playAndDisp(true,5); break;
case 55:
case 103:
currentKey+=11;//キー変更
currentKey%=12;
play456();
dispTextStatus("(KEYを変更しました)");
break;
case 56:
case 104:
currentKey++;//キー変更
currentKey%=12;
play456();
dispTextStatus("(KEYを変更しました)");
break;
case 57:
case 105:
//音色は維持したまま自動生成・演奏するモードにし、生成する
autoToneChange = false;
autoNext=true; addLoopf=false; oneMeasPlay=false;
playAndDisp(false,-1);
dispTextStatus("(音色を維持したまま自動演奏します)");
break;
case 90: //Z
beat6per8(); //1小節の長さを3/4に変更
play456();
dispTextStatus("(6/8拍子にしました)");
break;
case 88: //X
beat4per4(); //1小節の長さを4/4に変更
play456();
dispTextStatus("(4/4拍子にしました)");
break;
case 67: //C
//コード進行タイプを変更
progressTypeIndex = (progressTypeIndex + progressNumof -1)% progressNumof;
setChordProgressParameter( progressTypeIndex );
play456();
dispTextStatus("(コード進行を変更しました)");
break;
case 86: //V
//コード進行タイプを変更
progressTypeIndex = (progressTypeIndex + 1)% progressNumof;
setChordProgressParameter( progressTypeIndex );
play456();
dispTextStatus("(コード進行を変更しました)");
break;
case 66: //B
//arp生成タイプを変更
if (isChordSyncArp){
isChordSyncArp = false;
} else {
isChordSyncArp = true;
}
playAndDisp(true,5);
playAndDisp(true,3);
break;
case 78: //N
//ランダムなコード進行
progressSeriesArr = makeProgressSeries();
makeProgressCurrentArray(progressSeriesArr);
progressTypeIndex = -1;
setChordProgressParameter( progressTypeIndex );
play456();
dispTextStatus("(コード進行をランダムに生成しました)");
break;
}
}
//ボタン押下時に呼び出される
private function _buttonL(e :MouseEvent) :void { keyinput(37); }
private function _buttonR(e :MouseEvent) :void { keyinput(39); }
private function _button1(e :MouseEvent) :void { keyinput(48+1); }
private function _button2(e :MouseEvent) :void { keyinput(48+2); }
private function _button3(e :MouseEvent) :void { keyinput(48+3); }
private function _button4(e :MouseEvent) :void { keyinput(48+4); }
private function _button5(e :MouseEvent) :void { keyinput(48+5); }
private function _button6(e :MouseEvent) :void { keyinput(48+6); }
private function _button7(e :MouseEvent) :void { keyinput(48+7); }
private function _button8(e :MouseEvent) :void { keyinput(48+8); }
private function _buttonStepM(e :MouseEvent) :void { keyinput(90); }
private function _buttonStepP(e :MouseEvent) :void { keyinput(88); }
private function _buttonProgressM(e :MouseEvent) :void { keyinput(67); }
private function _buttonProgressP(e :MouseEvent) :void { keyinput(86); }
private function _buttonAuto(e :MouseEvent) :void { keyinput(48+9); }
private function _buttonArpType(e :MouseEvent) :void { keyinput(66); }
private function _buttonRndProgress(e :MouseEvent) :void { keyinput(78); }
//コンストラクタ(初期設定)
function playRandomTone() {
//ボタンの見た目を設定する ボタン登録より前に行う必要がある
Style.embedFonts = false;
Style.fontName = "MS Gothic";
Style.fontSize = 12;
Style.BACKGROUND = 0x010801; //背景(ボタン輪郭に使用)
Style.BUTTON_FACE = 0xE0D0D0; //ボタン背景
Style.LABEL_TEXT = 0x010101; //ボタン文字
//ボタンを登録する
var _btn1:PushButton = new PushButton(this, 0, 0, "ToneA", _button1 );
var _btn2:PushButton = new PushButton(this, 50, 0, "ToneB", _button2 );
var _btn3:PushButton = new PushButton(this, 100, 0, "ToneC", _button3 );
var _btn4:PushButton = new PushButton(this, 150, 0, "PhraseA", _button4 );
var _btn5:PushButton = new PushButton(this, 200, 0, "PhraseB", _button5 );
var _btn6:PushButton = new PushButton(this, 250, 0, "PhraseC", _button6 );
var _btn7:PushButton = new PushButton(this, 300, 0, "Key -", _button7 );
var _btn8:PushButton = new PushButton(this, 350, 0, "Key +", _button8 );
var _btnStepM:PushButton = new PushButton(this, 410, 20, "6/8", _buttonStepM );
var _btnStepP:PushButton = new PushButton(this, 410, 40, "4/4", _buttonStepP );
var _btnProgressM:PushButton = new PushButton(this, 410, 60, "Chord-", _buttonProgressM );
var _btnProgressP:PushButton = new PushButton(this, 410, 80, "Chord+", _buttonProgressP );
var _btnArpType:PushButton = new PushButton(this, 410, 100, "ArpType", _buttonArpType );
var _btnRndProgress:PushButton = new PushButton(this, 410, 120, "RndProgress", _buttonRndProgress );
var _btnL:PushButton = new PushButton(this, 410, 300, "Hist-", _buttonL );
var _btnR:PushButton = new PushButton(this, 410, 320, "Hist+", _buttonR );
var _btnAuto:PushButton = new PushButton(this, 410, 380, "Auto", _buttonAuto );
_btn1.width = _btn2.width = _btn3.width = 50;
_btn4.width = _btn5.width = _btn6.width = 50;
_btn7.width = _btn8.width = 50;
_btnAuto.width = _btnL.width = _btnR.width =
_btnStepM.width = _btnStepP.width =
_btnProgressM.width = _btnProgressP.width = _btnArpType.width = _btnRndProgress.width = 45;
//MML表示領域にスクロールバーをつける
vScrollBar = new VScrollBar(this, 400, 0, function(e:Event):void {
// スクロールバーのスクロール時にTextFieldの位置を合わせる
tf.scrollV = vScrollBar.value;
});
//イベントハンドラを登録する
// MOUSE_DOWN(マウス左ボタンプッシュ)を登録
stage.addEventListener( MouseEvent.MOUSE_DOWN, function(e :MouseEvent) :void {
//クリックした場合
// playAndDisp(true,-1); //生成と演奏と表示とクリップボード格納
});
// キーボードのキー押下を登録
stage.addEventListener( KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent) :void {
//キーボードのキーが押された場合
keyinput(e.keyCode);
});
// SiON演奏終了を登録
driver.addEventListener( SiONEvent.FINISH_SEQUENCE, function(e :SiONEvent) :void {
//演奏が終了した場合
myTimer.start(); //タイマをスタートさせる
});
// 一定時間ごとに発生するタイマイベントを登録
myTimer.addEventListener(TimerEvent.TIMER, function(e :TimerEvent) :void {
//タイマのスタートから一定時間が経過した場合
myTimer.stop(); //タイマを停止させる
if (autoNext) {
playAndDisp(false,-1); //生成と演奏と表示
}});
// MML表示領域のスクロールイベントを登録
tf.addEventListener(Event.SCROLL, function(e:Event):void {
//MML表示領域がスクロールした場合にスクロールバーの位置を合わせる
adjustScrollBar();
});
//フレーズ自動生成用バッファを初期化
initNoteMatrix();
//予めMMLを生成して履歴に格納しておく
addLoopf = true; oneMeasPlay = false; //履歴にコード進行版を格納するため
setChordProgressParameter(0); //コード進行用パラメタ初期設定
for (var i:int=0;i<100;i++){
var mml:String = ""; //生成したMML格納のためのテンポラリ領域
var mmlArray:Array = new Array(1+6+6); //履歴格納領域を確保
//2回目以降のパラメタ変更
if (i>0){
//コード進行用パラメタ設定
setRndChordProgressParameter();
//MML生成用 拍子パラメータ設定
rndBeat();
//キーをランダムに変更
currentKey = int(Math.random()*12);
//ArpTypeをランダムに変更
rndArpType();
}
//パラメタを元に生成
mmlCurrent[0] = makeToneMmlArp(); //音色MML生成
mmlCurrent[1] = makeToneMmlBass();
mmlCurrent[2] = makeToneMmlChord();
mmlCurrent[3] = makeOnpuMmlArp(); //演奏MML生成
mmlCurrent[4] = makeOnpuMmlBass();
mmlCurrent[5] = makeOnpuMmlChord();
if (isChordSyncArp){ mmlCurrent[3] = makeOnpuMmlArp(); } //chordにあわせて再度生成する
mml = makeMml(mmlCurrent); //SiON演奏用MMLを生成する
mmlArray[0] = mml; //履歴保存のためarrayに格納
mmlArray[1+0] = mmlCurrent[0];
mmlArray[1+1] = mmlCurrent[1];
mmlArray[1+2] = mmlCurrent[2];
mmlArray[1+3] = mmlCurrent[3];
mmlArray[1+4] = mmlCurrent[4];
mmlArray[1+5] = mmlCurrent[5];
mmlArray[1+6+0] = currentKey;
mmlArray[1+6+1] = progressTypeIndex;
mmlArray[1+6+2] = writeLengthArrMul;
mmlArray[1+6+3] = writeLengthArrDiv;
mmlArray[1+6+4] = tempo;
mmlArray[1+6+5] = progressSeriesArr;
toneHis.add(mmlArray); //音色データを履歴に保存する
}
setChordProgressParameter(0); beat4per4(); isChordSyncArp=false; //初回演奏用
addLoopf = false; oneMeasPlay = true; //初回演奏後自動演奏するため
//初回演奏
playAndDisp(false,-1); //生成と演奏と表示
}
}
}
//履歴バッファ
//FIFO(lengthはコンストラクタで指定)に対して読み書き
//制約:履歴バッファにaddする値の型はArray限定
class history {
//公開プロパティ
public var readIndex:int; //履歴index(読み込み用)
//非公開プロパティ
private var writeIndex:int; //履歴index(書き込み用)
private var writeIndexMax:int; //何段目まで履歴が入っているか
private var fifoBuf:Array; //履歴を格納
private var bufLength:int; //履歴段数を格納
//メソッド
//コンストラクタ
public function history(length:int) {
fifoBuf = new Array(length);
bufLength = length;
}
//値を履歴に書き込み、readIndexも同期させる
public function add(data:Array) :void {
//履歴にデータ書き込み
fifoBuf[writeIndex] = data;
//readIndexを同期させる
readIndex = writeIndex;
//max更新
if (writeIndexMax < bufLength - 1){ writeIndexMax = writeIndex; }
//writeIndexをラップアラウンド
writeIndex = (writeIndex + 1) % bufLength;
}
//指定された分、履歴を遡り、値を取得する(readIndexを変更し、それを元に取得)
public function get(add:int) :Array {
//備考:ArrayではなくObjectと記述した場合、呼び出し側記述時にコンパイルエラー
//readIndex更新
readIndex += add;
//readIndexを前後ともラップアラウンド
if (readIndex < 0){ readIndex = writeIndexMax; }
else if (readIndex>writeIndexMax){ readIndex = 0; }
//履歴からデータ取得
return fifoBuf[readIndex];
}
}
//chordNotenum領域(channelごと小節ごとの二次元配列にnoteNumberが格納されているもの)に対する加工を行う
// 音程シフト、コード転回、など
class processChordNotenum {
//メソッド
//コンストラクタ
public function processChordNotenum(){
}
//数値を整数にし文字列3文字になるよう左space埋め(padding)
private function lpad3(inum:int) :String { return (" " + int(inum)).substr(-3,3); }
//chordNoteNumのnotenumberを指定したshiftValue分だけシフトする
public function shift(chordNotenumArray:Array,shiftValue:int) :void {
var chordSize:int = chordNotenumArray.length; //和音数 入力配列のlengthを元に算出する
var numofMeas:int = chordNotenumArray[0].length; //小節数 入力配列の先頭を元に算出する
var channel:int = 0; //カウンタ
var meas:int = 0; //小節番号
for (channel=0;channel<chordSize;channel++){
for (meas=0;meas<numofMeas;meas++){
if (chordNotenumArray[channel][meas]!=-1){ //無音でない場合
chordNotenumArray[channel][meas] += shiftValue;
}
}
}
}
//コード転回
public function inversion(chordNotenumArray:Array) :void {
var chordSize:int = chordNotenumArray.length; //和音数 入力配列のlengthを元に算出する
var numofMeas:int = chordNotenumArray[0].length; //小節数 入力配列の先頭を元に算出する
var channel:int = 0; //カウンタ
var meas:int = 0; //小節番号
//転回
var chordToneArr:Array = new Array(chordSize); //1小節ごとのsort用 1小節全channel分 chordSize算出後に確保すること
var inversionArr:Array = new Array(chordSize); //1小節ごとの転回用ワーク chordSize算出後に確保すること
var inversionCount:int = 0; //転回処理用カウンタ
var selectedInversion:int = 0; //どの転回を選んだか
var oldTopNoteNum:int = 0;
var inversionSize:int = 0;
//転回用ワーク領域確保
// 基本形、第一転回形、...、ごと :和音数ぶん(基本形+)転回形がある
for (inversionCount=0;inversionCount<chordSize;inversionCount++){
//[転回形の数][和音数]
inversionArr[inversionCount] = new Array(chordSize);
}
//1小節ごと、全channelをセットにして処理
for (meas=0;meas<numofMeas;meas++){
inversionSize = chordSize; //転回の種類の数 使わない転回がある場合減少する
//転回の前処理としてソートする
// コード構成音配列に入れる
for (channel=0;channel<chordSize;channel++){
chordToneArr[channel] = chordNotenumArray[channel][meas];
}
// コード構成音配列をnotenumでソート 数値昇順ソート
chordToneArr.sort(Array.NUMERIC);
//備考:以降の転回後にもソートを行ない、
//配列の末尾がtopnoteであることを前提とした処理を行う
//転回:ソート済み配列を元に、それを転回したものを生成する
for (inversionCount=0;inversionCount<chordSize;inversionCount++){
//コピー
for (channel=0;channel<chordSize;channel++){
inversionArr[inversionCount][channel] = chordToneArr[channel];
}
if (inversionCount!=0){
//転回の回数ぶん繰り返し
for (var k:int=0;k<inversionCount;k++){
var n:int = inversionArr[inversionCount].pop(); //末尾を抜き出しnにセット
if (n>12){ n -= 12; }//nをoctave下げる
inversionArr[inversionCount].unshift(n); //配列先頭にnを挿入
inversionArr[inversionCount].sort(Array.NUMERIC); //-1が配列先頭にくるよう数値昇順ソート
}
}
}
// topNoteと2ndNoteが半音でぶつかる転回を探し、配列から削除する(lengthが変化する)
for (inversionCount=0;inversionCount<inversionArr.length;inversionCount++){
if (inversionArr[inversionCount].length>1){
//2和音以上の場合
if (1 == inversionArr[inversionCount][chordSize-1] - inversionArr[inversionCount][chordSize-2]){
//半音でぶつかる場合
for (channel=0;channel<chordSize;channel++){
inversionArr[inversionCount][channel] = -1;
inversionSize--;
} //-1で埋め、優先度検索で一番下にくるようにする
//備考: inversionArr.splice(inversionCount,1); はフリーズ。ただし1次元配列のときはspliceも成功した
// sliceで1つ目と2つ目を抜き取り、1つ目に2つ目をconcatして、それをinversionArrに上書きしてもフリーズ
break;
}
}
}
//選択:どの転回がよいかの優先度を算出する
if (0==meas){ // 1小節目の場合、ランダムでoldTopNoteNumを設定
oldTopNoteNum = 12*5 +6 + (int(Math.random() * 12)); //o5あたり, 少し高めもあり
}
// 前の小節からのtopNoteの移動の少ないものを選択
var topNoteDiff1:int = 0; //topNoteの移動の値、調査直後
var topNoteDiffAbs:int = 0; //絶対値
var topNoteDiffAbsArray:Array = new Array(inversionArr.length);
for (inversionCount=0;inversionCount<inversionArr.length;inversionCount++){
if (-1==inversionArr[inversionCount][chordSize-1]){
topNoteDiff1 = 999; //topnoteが-1の場合
}else{
topNoteDiff1 = inversionArr[inversionCount][chordSize-1] - oldTopNoteNum;
//前のtopnoteにあわせて転回を加工する
if (topNoteDiff1>6){
//高すぎるならoctave落とす
for (channel=0;channel<chordSize;channel++){
inversionArr[inversionCount][channel] -= 12;
topNoteDiff1 -= 12;
}
}else if (topNoteDiff1<-6){
//低すぎるならoctave上げる
for (channel=0;channel<chordSize;channel++){
inversionArr[inversionCount][channel] += 12;
topNoteDiff1 += 12;
}
}
}
topNoteDiffAbs = Math.abs(topNoteDiff1);
var rndStr:String = lpad3(int(Math.random() * 1000)); //移動で-1と+1がある場合乱数で決める
topNoteDiffAbsArray[inversionCount] = lpad3(topNoteDiffAbs) + rndStr + lpad3(inversionCount);
//"005137002"のような文字列を生成 topNoteとの差の絶対値が5,乱数137,添字2の転回
}
// ソート
topNoteDiffAbsArray.sort();
// ソート結果を設定 例:"001137002"から"002"を取得して設定
selectedInversion = topNoteDiffAbsArray[0].substr(6,3);
//書き戻す :1小節分全channel
for (channel=0;channel<chordSize;channel++){
chordNotenumArray[channel][meas] = inversionArr[selectedInversion][channel];
}
oldTopNoteNum = chordNotenumArray[chordSize-1][meas];
}
}
//chordNoteNumの、topNoteから指定した分のnotenumberを、drop(octave下げ)する
// 制約:ローインターバルリミットの考慮(音色,arp,bassとの兼ね合い, octave up,omit, etc.)がない
public function drop(chordNotenumArray:Array,ofsFromTopNote:int) :void {
var chordSize:int = chordNotenumArray.length; //和音数 入力配列のlengthを元に算出する
var numofMeas:int = chordNotenumArray[0].length; //小節数 入力配列の先頭を元に算出する
var meas:int = 0; //小節番号カウンタ
if (ofsFromTopNote>=chordSize){ //和音数が少ない場合はdropしない
return;
}
for (meas=0;meas<numofMeas;meas++){
if (chordNotenumArray[ofsFromTopNote][meas]!=-1){ //無音でない場合
chordNotenumArray[ofsFromTopNote][meas] -= 12; //octave下げる
}
}
}
//chordNoteNumの全channelのnoteを、measごとに、notenumでソートする
public function sort(chordNotenumArray:Array) :void {
var chordSize:int = chordNotenumArray.length; //和音数 入力配列のlengthを元に算出する
var numofMeas:int = chordNotenumArray[0].length; //小節数 入力配列の先頭を元に算出する
var chordToneArr:Array = new Array(chordSize); //1小節ごとのsort用 1小節全channel分 chordSize算出後に確保すること
var meas:int = 0; //小節番号カウンタ
//小節ごと
for (meas=0;meas<numofMeas;meas++){
var channel:int = 0; //カウンタ
// コード構成音配列に入れる
for (channel=0;channel<chordSize;channel++){
chordToneArr[channel] = chordNotenumArray[channel][meas];
}
// コード構成音配列をnotenumでソート 数値昇順ソート
chordToneArr.sort(Array.NUMERIC);
//書き戻す :1小節分全channel
for (channel=0;channel<chordSize;channel++){
chordNotenumArray[channel][meas] = chordToneArr[channel];
}
}
}
//コピー
public function copy(fromChordNotenumArray:Array) :Array {
var chordSize:int = fromChordNotenumArray.length; //和音数 入力配列のlengthを元に算出する
var numofMeas:int = fromChordNotenumArray[0].length; //小節数 入力配列の先頭を元に算出する
var chordNotenumArr:Array = new Array(chordSize); //出力バッファ
for (var channel:int=0;channel<chordSize;channel++){
chordNotenumArr[channel] = new Array(numofMeas);
for (var meas:int=0;meas<numofMeas;meas++){
chordNotenumArr[channel][meas] = fromChordNotenumArray[channel][meas];
}
}
return chordNotenumArr;
}
}