/// <summary> /// /// </summary> /// <param name="vsq"></param> /// <param name="track"></param> /// <param name="clock_start"></param> /// <param name="clock_end"></param> /// <returns></returns> protected override EventQueueSequence generateMidiEvent(VsqFileEx vsq, int track, int clock_start, int clock_end) { EventQueueSequence list = new EventQueueSequence(); VsqTrack t = vsq.Track.get(track); addSingerEvents(list, t, clock_start, clock_end); // ノートon, off Vector <Point> pit_send = new Vector <Point>(); // PITが追加されたゲートタイム。音符先頭の分を重複して送信するのを回避するために必要。 VsqBPList pit = t.getCurve("pit"); VsqBPList pbs = t.getCurve("pbs"); VsqBPList dyn = t.getCurve("dyn"); VsqBPList bre = t.getCurve("bre"); VsqBPList cle = t.getCurve("cle"); VsqBPList por = t.getCurve("por"); for (Iterator <VsqEvent> itr = t.getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); int endclock = item.Clock + item.ID.getLength(); boolean contains_start = clock_start <= item.Clock && item.Clock <= clock_end; boolean contains_end = clock_start <= endclock && endclock <= clock_end; if (contains_start || contains_end) { if (contains_start) { #region contains_start // noteonのゲートタイムが,範囲に入っている // noteon MIDIイベントを作成 MidiEvent[] noteOnEvents = mDriver.createNoteOnEvent(item.ID.Note, item.ID.Dynamics, item.ID.LyricHandle.L0.Phrase); if (noteOnEvents.Length > 0) { MidiEventQueue queue = list.get(item.Clock); Vector <MidiEvent> add = Arrays.asList(noteOnEvents); queue.noteon.addAll(add); pit_send.add(new Point(item.Clock, item.Clock)); } /* 音符頭で設定するパラメータ */ // Release MidiEventQueue q = list.get(item.Clock); String strRelease = VsqFileEx.getEventTag(item, VsqFileEx.TAG_VSQEVENT_AQUESTONE_RELEASE); int release = 64; try { release = int.Parse(strRelease); } catch (Exception ex) { Logger.write(typeof(AquesToneWaveGenerator) + ".generateMidiEvent; ex=" + ex + "\n"); release = 64; } ParameterEvent pe = new ParameterEvent(); pe.index = mDriver.releaseParameterIndex; pe.value = release / 127.0f; q.param.add(pe); // dyn int dynAtStart = dyn.getValue(item.Clock); ParameterEvent peDyn = new ParameterEvent(); peDyn.index = mDriver.volumeParameterIndex; peDyn.value = (float)(dynAtStart - dyn.getMinimum()) / (float)(dyn.getMaximum() - dyn.getMinimum()); q.param.add(peDyn); // bre int breAtStart = bre.getValue(item.Clock); ParameterEvent peBre = new ParameterEvent(); peBre.index = mDriver.haskyParameterIndex; peBre.value = (float)(breAtStart - bre.getMinimum()) / (float)(bre.getMaximum() - bre.getMinimum()); q.param.add(peBre); // cle int cleAtStart = cle.getValue(item.Clock); ParameterEvent peCle = new ParameterEvent(); peCle.index = mDriver.resonancParameterIndex; peCle.value = (float)(cleAtStart - cle.getMinimum()) / (float)(cle.getMaximum() - cle.getMinimum()); q.param.add(peCle); // por int porAtStart = por.getValue(item.Clock); ParameterEvent pePor = new ParameterEvent(); pePor.index = mDriver.portaTimeParameterIndex; pePor.value = (float)(porAtStart - por.getMinimum()) / (float)(por.getMaximum() - por.getMinimum()); q.param.add(pePor); #endregion } // ビブラート // ビブラートが存在する場合、PBSは勝手に変更する。 if (item.ID.VibratoHandle == null) { if (contains_start) { // 音符頭のPIT, PBSを強制的に指定 int notehead_pit = pit.getValue(item.Clock); MidiEvent pit0 = getPitMidiEvent(notehead_pit); MidiEventQueue queue = list.get(item.Clock); queue.pit.clear(); queue.pit.add(pit0); int notehead_pbs = pbs.getValue(item.Clock); ParameterEvent pe = new ParameterEvent(); pe.index = mDriver.bendLblParameterIndex; pe.value = notehead_pbs / 13.0f; queue.param.add(pe); } } else { int delta_clock = 5; //ピッチを取得するクロック間隔 int tempo = 120; double sec_start_act = vsq.getSecFromClock(item.Clock); double sec_end_act = vsq.getSecFromClock(item.Clock + item.ID.getLength()); double delta_sec = delta_clock / (8.0 * tempo); //ピッチを取得する時間間隔 float pitmax = 0.0f; int st = item.Clock; if (st < clock_start) { st = clock_start; } int end = item.Clock + item.ID.getLength(); if (clock_end < end) { end = clock_end; } pit_send.add(new Point(st, end)); // ビブラートが始まるまでのピッチを取得 double sec_vibstart = vsq.getSecFromClock(item.Clock + item.ID.VibratoDelay); int pit_count = (int)((sec_vibstart - sec_start_act) / delta_sec); TreeMap <Integer, Float> pit_change = new TreeMap <Integer, Float>(); for (int i = 0; i < pit_count; i++) { double gtime = sec_start_act + delta_sec * i; int clock = (int)vsq.getClockFromSec(gtime); float pvalue = (float)t.getPitchAt(clock); pitmax = Math.Max(pitmax, Math.Abs(pvalue)); pit_change.put(clock, pvalue); } // ビブラート部分のピッチを取得 Vector <PointD> ret = new Vector <PointD>(); Iterator <PointD> itr2 = new VibratoPointIteratorBySec( vsq, item.ID.VibratoHandle.getRateBP(), item.ID.VibratoHandle.getStartRate(), item.ID.VibratoHandle.getDepthBP(), item.ID.VibratoHandle.getStartDepth(), item.Clock + item.ID.VibratoDelay, item.ID.getLength() - item.ID.VibratoDelay, (float)delta_sec); for ( ; itr2.hasNext();) { PointD p = itr2.next(); float gtime = (float)p.getX(); int clock = (int)vsq.getClockFromSec(gtime); float pvalue = (float)(t.getPitchAt(clock) + p.getY() * 100.0); pitmax = Math.Max(pitmax, Math.Abs(pvalue)); pit_change.put(clock, pvalue); } // ピッチベンドの最大値を実現するのに必要なPBS int required_pbs = (int)Math.Ceiling(pitmax / 100.0); #if DEBUG sout.println("AquesToneRenderingRunner#generateMidiEvent; required_pbs=" + required_pbs); #endif if (required_pbs > 13) { required_pbs = 13; } MidiEventQueue queue = list.get(item.Clock); ParameterEvent pe = new ParameterEvent(); pe.index = mDriver.bendLblParameterIndex; pe.value = required_pbs / 13.0f; queue.param.add(pe); // PITを順次追加 for (Iterator <Integer> itr3 = pit_change.keySet().iterator(); itr3.hasNext();) { Integer clock = itr3.next(); if (clock_start <= clock && clock <= clock_end) { float pvalue = pit_change.get(clock); int pit_value = (int)(8192.0 / (double)required_pbs * pvalue / 100.0); MidiEventQueue q = list.get(clock); MidiEvent me = getPitMidiEvent(pit_value); q.pit.clear(); q.pit.add(me); } else if (clock_end < clock) { break; } } } //pit_send.add( pit_send_p ); // noteoff MIDIイベントを作成 if (contains_end) { MidiEvent noteoff = new MidiEvent(); noteoff.firstByte = 0x80; noteoff.data = new int[] { item.ID.Note, 0x40 }; // ここのvel Vector <MidiEvent> a_noteoff = Arrays.asList(new MidiEvent[] { noteoff }); MidiEventQueue q = list.get(endclock); q.noteoff.addAll(a_noteoff); pit_send.add(new Point(endclock, endclock)); // PITの送信を抑制するために必要 } } if (clock_end < item.Clock) { break; } } // pitch bend sensitivity // RPNで送信するのが上手くいかないので、parameterを直接いぢる if (pbs != null) { int keycount = pbs.size(); for (int i = 0; i < keycount; i++) { int clock = pbs.getKeyClock(i); if (clock_start <= clock && clock <= clock_end) { int value = pbs.getElementA(i); ParameterEvent pbse = new ParameterEvent(); pbse.index = mDriver.bendLblParameterIndex; pbse.value = value / 13.0f; MidiEventQueue queue = list.get(clock); queue.param.add(pbse); } else if (clock_end < clock) { break; } } } // pitch bend if (pit != null) { int keycount = pit.size(); for (int i = 0; i < keycount; i++) { int clock = pit.getKeyClock(i); if (clock_start <= clock && clock <= clock_end) { boolean contains = false; for (Iterator <Point> itr = pit_send.iterator(); itr.hasNext();) { Point p = itr.next(); if (p.x <= clock && clock <= p.y) { contains = true; break; } } if (contains) { continue; } int value = pit.getElementA(i); MidiEvent pbs0 = getPitMidiEvent(value); MidiEventQueue queue = list.get(clock); queue.pit.clear(); queue.pit.add(pbs0); } else if (clock_end < clock) { break; } } } appendParameterEvents(list, dyn, mDriver.volumeParameterIndex, clock_start, clock_end); appendParameterEvents(list, bre, mDriver.haskyParameterIndex, clock_start, clock_end); appendParameterEvents(list, cle, mDriver.resonancParameterIndex, clock_start, clock_end); appendParameterEvents(list, por, mDriver.portaTimeParameterIndex, clock_start, clock_end); return(list); }