OSDN Git Service

4adcb6fa2cb257f83fc7d16a23a8b2abda908ee7
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / music / MelodyTrackSpec.java
1 package camidion.chordhelper.music;
2
3 /**
4  * メロディトラック仕様
5  */
6 public class MelodyTrackSpec extends AbstractNoteTrackSpec {
7         /**
8          * 音域
9          */
10         public Range range;
11         /**
12          * 音を出すかどうかを表すビット列
13          */
14         public int beatPattern = 0xFFFF;
15         /**
16          * あとで音を出し続けるかどうかを表すビット列
17          */
18         public int continuousBeatPattern = 0xEEEE;
19         /**
20          * ベース音を使う場合 true、それ以外のコード構成音を使う場合 false
21          */
22         public boolean isBass = false;
23         /**
24          * 乱数メロディを作るかどうか
25          */
26         public boolean randomMelody = false;
27         /**
28          * 乱数歌詞を作るかどうか
29          */
30         public boolean randomLyric = false;
31         /**
32          * 乱数歌詞をNSX-39(ポケット・ミク)対応の
33          * システムエクスクルーシブを使って出力するかどうか
34          */
35         public boolean nsx39 = false;
36         /**
37          * メロディトラック仕様を構築
38          * @param ch MIDIチャンネル
39          * @param name トラック名
40          */
41         public MelodyTrackSpec(int ch, String name) {
42                 super(ch,name);
43                 range = new Range(Note.SEMITONES_PER_OCTAVE * 5, Note.SEMITONES_PER_OCTAVE * 6 );
44         }
45         /**
46          * 音域を指定してメロディトラック仕様を構築
47          * @param ch MIDIチャンネル
48          * @param name トラック名
49          * @param range 音域
50          */
51         public MelodyTrackSpec(int ch, String name, Range range) {
52                 super(ch,name);
53                 this.range = range;
54         }
55         /**
56          * コードの追加
57          * @param cp コード進行
58          */
59         public void addChords( ChordProgression cp ) {
60                 int mask;
61                 long tick;
62                 long startTickPos;
63
64                 // 音階ごとの生起確率を決める重みリスト(random_melody の場合)
65                 int i, noteNumber, prevNoteNumber = 1;
66                 int noteWeights[] = new int[range.maxNote - range.minNote];
67                 //
68                 Key key = cp.key;
69                 if( key == null ) key = new Key("C");
70
71                 for( ChordProgression.Line line : cp.lines ) { // 行単位の処理
72                         for( ChordProgression.Measure measure : line ) { // 小節単位の処理
73                                 if( measure.ticks_per_beat == null )
74                                         continue;
75                                 ChordProgression.TickRange tickRange = measure.getRange();
76                                 boolean isNoteOn = false;
77                                 //
78                                 // 各ビートごとに繰り返し
79                                 for(
80                                         tick = startTickPos = tickRange.startTickPos, mask = 0x8000;
81                                         tick < tickRange.end_tick_pos;
82                                         tick += minNoteTicks, mask >>>= 1
83                                 ) {
84                                         // そのtick地点のコードを調べる
85                                         Chord chord = measure.chordStrokeAt(tick).chord;
86                                         int notes[] = chord.toNoteArray(range, null);
87                                         //
88                                         // 各音階ごとに繰り返し
89                                         if( Math.random() < 0.9 ) {
90                                                 if( (beatPattern & mask) == 0 ) {
91                                                         // 音を出さない
92                                                         continue;
93                                                 }
94                                         }
95                                         else {
96                                                 // ランダムに逆パターン
97                                                 if( (beatPattern & mask) != 0 ) {
98                                                         continue;
99                                                 }
100                                         }
101                                         if( ! isNoteOn ) {
102                                                 // 前回のビートで継続していなかったので、
103                                                 // この地点で音を出し始めることにする。
104                                                 startTickPos = tick;
105                                                 isNoteOn = true;
106                                         }
107                                         if( Math.random() < 0.9 ) {
108                                                 if( (continuousBeatPattern & mask) != 0 ) {
109                                                         // 音を継続
110                                                         continue;
111                                                 }
112                                         }
113                                         else {
114                                                 // ランダムに逆パターン
115                                                 if( (continuousBeatPattern & mask) == 0 ) {
116                                                         continue;
117                                                 }
118                                         }
119                                         // このビートの終了tickで音を出し終わることにする。
120                                         if( randomMelody ) {
121                                                 // 音階ごとに出現確率を決める
122                                                 int totalWeight = 0;
123                                                 for( i=0; i<noteWeights.length; i++ ) {
124                                                         noteNumber = range.minNote + i;
125                                                         int m12 = Note.mod12(noteNumber - chord.rootNoteSymbol().toNoteNumber());
126                                                         int w;
127                                                         if( chord.indexOf(noteNumber) >= 0 ) {
128                                                                 // コード構成音は確率を上げる
129                                                                 w = 255;
130                                                         }
131                                                         else {
132                                                                 switch( m12 ) {
133                                                                 case 2: // 長2度
134                                                                 case 9: // 長6度
135                                                                         w = 63; break;
136                                                                 case 5: // 完全4度
137                                                                 case 11: // 長7度
138                                                                         w = 47; break;
139                                                                 default:
140                                                                         w = 0; break;
141                                                                 }
142                                                                 if( ! key.isOnScale( noteNumber ) ) {
143                                                                         // スケールを外れている音は採用しない
144                                                                         w = 0;
145                                                                 }
146                                                         }
147                                                         // 乱高下軽減のため、前回との差によって確率を調整する
148                                                         int diff = noteNumber - prevNoteNumber;
149                                                         if( diff < 0 ) diff = -diff;
150                                                         if( diff == 0 ) w /= 8;
151                                                         else if( diff > 7 ) w = 0;
152                                                         else if( diff > 4 ) w /= 8;
153                                                         totalWeight += (noteWeights[i] = w);
154                                                 }
155                                                 // さいころを振って音階を決定
156                                                 noteNumber = range.invertedNoteOf(key.rootNoteNumber());
157                                                 double r = Math.random();
158                                                 totalWeight *= r;
159                                                 for( i=0; i<noteWeights.length; i++ ) {
160                                                         if( (totalWeight -= noteWeights[i]) < 0 ) {
161                                                                 noteNumber = range.minNote + i;
162                                                                 break;
163                                                         }
164                                                 }
165                                                 if( randomLyric ) {
166                                                         // ランダムな歌詞を作成
167                                                         int index = (int)(Math.random() * MIDISpec.nsx39LyricElements.length);
168                                                         if( nsx39 ) {
169                                                                 // ポケット・ミク用システムエクスクルーシブ
170                                                                 byte nsx39SysEx[] = {
171                                                                         0x43, 0x79, 0x09, 0x11, 0x0A, 0x00, (byte)(index & 0x7F), (byte) 0xF7
172                                                                 };
173                                                                 addSysEx(nsx39SysEx, startTickPos);
174                                                         }
175                                                         // 決定された音符を追加
176                                                         addNote(startTickPos, tick + minNoteTicks, noteNumber, velocity);
177                                                         // 歌詞をテキストとして追加
178                                                         addStringTo(0x05, MIDISpec.nsx39LyricElements[index], startTickPos);
179                                                 }
180                                                 else {
181                                                         // 決定された音符を追加
182                                                         addNote(startTickPos, tick + minNoteTicks, noteNumber, velocity);
183                                                 }
184                                                 prevNoteNumber = noteNumber;
185                                         }
186                                         else if( isBass ) {
187                                                 // ベース音を追加
188                                                 int note = range.invertedNoteOf(chord.bassNoteSymbol().toNoteNumber());
189                                                 addNote(startTickPos, tick + minNoteTicks, note, velocity);
190                                         }
191                                         else {
192                                                 // コード本体の音を追加
193                                                 for( int note : notes ) {
194                                                         addNote(startTickPos, tick + minNoteTicks, note, velocity);
195                                                 }
196                                         }
197                                         isNoteOn = false;
198                                 }
199                         }
200                 }
201
202         }
203
204 }