public void testReflectVibratoPitch() { var tempoTable = new TempoVector(); var pitchBend = new VsqBPList( CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum() ); var pitchBendSensitivity = new VsqBPList( CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum() ); pitchBend.add( 0, 8191 ); pitchBendSensitivity.add( 0, 2 ); var generator = new AquesTone2WaveGeneratorStub(); var item = new VsqEvent( 0, new VsqID() ); item.ID.type = VsqIDType.Anote; item.ID.VibratoHandle = new VibratoHandle(); item.ID.VibratoHandle.StartRate = 0x40; item.ID.VibratoHandle.RateBP.clear(); item.ID.VibratoHandle.StartDepth = 0x40; item.ID.VibratoHandle.DepthBP.clear(); item.ID.setLength( 480 ); item.ID.VibratoDelay = 430; item.ID.VibratoHandle.setLength( 50 ); generator.reflectVibratoPitch( item, pitchBend, pitchBendSensitivity, tempoTable ); { var expectedPit = new Dictionary<int, int> { { 0, 8191 }, { 430, 5461 }, { 431, 5467 }, { 432, 5484 }, { 433, 5514 }, { 434, 5555 }, { 435, 5608 }, { 436, 5672 }, { 437, 5748 }, { 438, 5835 }, { 439, 5932 }, { 440, 6041 }, { 441, 6096 }, { 442, 6151 }, { 443, 6205 }, { 444, 6257 }, { 445, 6309 }, { 446, 6360 }, { 447, 6410 }, { 448, 6459 }, { 449, 6507 }, { 450, 6553 }, { 451, 6598 }, { 452, 6642 }, { 453, 6684 }, { 454, 6725 }, { 455, 6764 }, { 456, 6802 }, { 457, 6838 }, { 458, 6873 }, { 459, 6906 }, { 460, 6937 }, { 461, 6967 }, { 462, 6994 }, { 463, 7020 }, { 464, 7044 }, { 465, 7066 }, { 466, 7087 }, { 467, 7105 }, { 468, 7121 }, { 469, 7136 }, { 470, 7148 }, { 471, 6989 }, { 472, 6826 }, { 473, 6660 }, { 474, 6491 }, { 475, 6320 }, { 476, 6149 }, { 477, 5976 }, { 478, 5804 }, { 479, 5632 }, { 480, 8191 } }; Assert.AreEqual( expectedPit.Count, pitchBend.size() ); int i = 0; foreach ( var pitInfo in expectedPit ) { Assert.AreEqual( pitInfo.Key, pitchBend.getKeyClock( i ) ); Assert.AreEqual( pitInfo.Value, pitchBend.getElementA( i ) ); ++i; } } { var expectedPbs = new Dictionary<int, int> { { 0, 2 }, { 430, 3 }, { 480, 2 } }; Assert.AreEqual( expectedPbs.Count, pitchBendSensitivity.size() ); int i = 0; foreach ( var pbsInfo in expectedPbs ) { Assert.AreEqual( pbsInfo.Key, pitchBendSensitivity.getKeyClock( i ) ); Assert.AreEqual( pbsInfo.Value, pitchBendSensitivity.getElementA( i ) ); ++i; } } }
/// <summary> /// コンストラクタ。trackはcloneされないが、tempoはcloneされる。 /// </summary> /// <param name="track"></param> /// <param name="tempo"></param> public RenderedStatus(VsqTrack track, TempoVector tempo, SequenceConfig config) { this.track = track; this.tempo = new TempoVector(); foreach (var entry in tempo) { this.tempo.Add((TempoTableEntry)entry.clone()); } this.config = config; }
/// <summary> /// Initialize by VsqEvent object. /// </summary> /// <param name="item"></param> /// <param name="timesig_table"></param> public TiedEvent(VsqEvent item, TimesigVector timesig_table, TempoVector tempo_table) { Init(timesig_table, tempo_table, item.Clock, item.ID.getLength(), item.ID.Note, false); if (notes_.Count > 0 && item.ID.LyricHandle != null && item.ID.LyricHandle.getCount() > 0) { var lyric = new lyric(); lyric.text.Add(new textelementdata()); lyric.text.First.Value = item.ID.LyricHandle.getLyricAt(0).Phrase; notes_[0].lyric = new lyric[] { lyric }; } }
/// <summary> /// このWAVE描画コンテキストが保持しているWAVEデータを、ゲートタイム基準でグラフィクスに描画します。 /// 縦軸は最大振幅がちょうど描画範囲に収まるよう調節されます。 /// </summary> /// <param name="g">描画に使用するグラフィクスオブジェクト</param> /// <param name="pen">描画に使用するペン</param> /// <param name="rect">描画範囲</param> /// <param name="clock_start">描画開始位置のゲートタイム</param> /// <param name="clock_end">描画終了位置のゲートタイム</param> /// <param name="tempo_table">ゲートタイムから秒数を調べる際使用するテンポ・テーブル</param> /// <param name="pixel_per_clock">ゲートタイムあたりの秒数</param> public void draw( Graphics2D g, Color pen, Rectangle rect, int clock_start, int clock_end, TempoVector tempo_table, float pixel_per_clock) { drawCore(g, pen, rect, clock_start, clock_end, tempo_table, pixel_per_clock, 1.0f, true); }
/// <summary> /// このWAVE描画コンテキストが保持しているWAVEデータを、ゲートタイム基準でグラフィクスに描画します。 /// </summary> /// <param name="g">描画に使用するグラフィクスオブジェクト</param> /// <param name="pen">描画に使用するペン</param> /// <param name="rect">描画範囲</param> /// <param name="clock_start">描画開始位置のゲートタイム</param> /// <param name="clock_end">描画終了位置のゲートタイム</param> /// <param name="tempo_table">ゲートタイムから秒数を調べる際使用するテンポ・テーブル</param> /// <param name="pixel_per_clock">ゲートタイムあたりの秒数</param> /// <param name="scale_y">Y軸方向の描画スケール。デフォルトは1.0</param> /// <param name="auto_maximize">自動で最大化するかどうか</param> private void drawCore( Graphics2D g, Color pen, Rectangle rect, int clock_start, int clock_end, TempoVector tempo_table, float pixel_per_clock, float scale_y, bool auto_maximize) { }
public VibratoPointIteratorByClock(TempoVector tempo_table, VibratoBPList rate, int start_rate, VibratoBPList depth, int start_depth, int clock_start, int clock_width) { this.mTempoTable = tempo_table; this.mRate = rate; this.mStartRate = start_rate; this.mDepth = depth; this.mStartDepth = start_depth; this.mClockStart = clock_start; this.mClockWidth = clock_width; rewind(); }
private TempoVector quantizeTempoTable(TempoVector tempo_table) { var copy = new TempoVector(); copy.AddRange( tempo_table .Select((tempo) => { int clock = quantize(tempo.Clock); return(new TempoTableEntry(clock, tempo.Tempo, 0.0)); })); for (int i = copy.Count - 1; i > 0; i--) { if (copy[i].Clock == copy[i - 1].Clock) { copy.RemoveAt(i); } } copy.updateTempoInfo(); return(copy); }
/// <summary> /// Create MusicXML.scorepartwisePart object by VsqTrack instance. /// </summary> /// <param name="track"></param> /// <param name="timesig_table"></param> /// <returns></returns> private scorepartwisePart createScorePart(VsqTrack track, TimesigVector timesig_table, TempoVector tempo_table) { var part = new scorepartwisePart(); var note_list = quantizeTrack(track, timesig_table, tempo_table); var measures = new List <scorepartwisePartMeasure>(); int measure = 0; Timesig timesig = new Timesig(0, 0); while (0 < note_list.Count) { int measure_start_clock = timesig_table.getClockFromBarCount(measure); int measure_end_clock = timesig_table.getClockFromBarCount(measure + 1); var tempo_change_in_measure = new TempoVector(); tempo_change_in_measure.AddRange( tempo_table .Where((tempo) => measure_start_clock <= tempo.Clock && tempo.Clock < measure_end_clock) .Select((tempo) => (TempoTableEntry)tempo.Clone())); // get the list of TiedEvent, contained in target measure. var in_measure_tied_note_list = note_list .Where((tied_event) => { int tied_event_start = tied_event.Clock; int tied_event_end = tied_event.Clock + tied_event.Length; return ((measure_start_clock <= tied_event_start && tied_event_start < measure_end_clock) || (measure_start_clock <= tied_event_end && tied_event_end < measure_end_clock) || (tied_event_start <= measure_start_clock && measure_end_clock <= tied_event_end)); }); // get the list of MusicXML.note. var in_measure_note_list = in_measure_tied_note_list .SelectMany((tied_event) => { var result = new List <object>(); int clock = tied_event.Clock; foreach (var note in tied_event) { int length = (int)note.duration.First; if (measure_start_clock <= clock && clock + length <= measure_end_clock) { var tempo_change = tempo_change_in_measure .FirstOrDefault((tempo) => tempo.Clock == clock); if (tempo_change != null) { var direction = new direction(); direction.placement = abovebelow.above; direction.placementSpecified = true; direction.directiontype = new directiontype[] { new directiontype() }; direction.directiontype[0].metronome.Add(new metronome()); var perminute = new perminute(); perminute.Value = getTempo(tempo_change).ToString(); direction.directiontype[0].metronome[0].Items = new object[] { notetypevalue.quarter, perminute }; direction.sound = new sound(); direction.sound.tempo = getTempo(tempo_change); direction.sound.tempoSpecified = true; result.Add(direction); } result.Add(note); } clock += length; } return(result); }); var partwise_measure = new scorepartwisePartMeasure(); partwise_measure.number = (measure + 1).ToString(); var items = new List <object>(); var measure_timesig = timesig_table.getTimesigAt(measure_start_clock); if (!measure_timesig.Equals(timesig)) { var attributes = new MusicXML.attributes(); attributes.divisions = 480; attributes.divisionsSpecified = true; attributes.time = new time[] { new time() }; attributes.time[0].beats.Add(measure_timesig.numerator.ToString()); attributes.time[0].beattype.Add(measure_timesig.denominator.ToString()); attributes.time[0].symbol = timesymbol.common; attributes.time[0].symbolSpecified = true; items.Add(attributes); } timesig = measure_timesig; items.AddRange(in_measure_note_list); partwise_measure.Items = items.ToArray(); measures.Add(partwise_measure); note_list.RemoveAll((tied_event) => tied_event.Clock + tied_event.Length <= measure_end_clock); measure++; } part.measure = measures.ToArray(); return(part); }
private void Init(TimesigVector timesig_table, TempoVector tempo_table, int clock_start, int length, int note_number, bool create_rest) { clock_ = clock_start; length_ = length; // 着目している範囲内のテンポのみを取り出す var small_tempo_table = new TempoVector(); small_tempo_table.AddRange( tempo_table .Where((tempo) => clock_start <= tempo.Clock && tempo.Clock < clock_start + length) .Select((tempo) => (TempoTableEntry)tempo.Clone())); // <pitch> エレメントのテンプレートを作成 MusicXML.pitch pitch = null; if (!create_rest) { pitch = new pitch(); int octave = VsqNote.getNoteOctave(note_number) + 1; step step; if (Enum.TryParse <step>(VsqNote.getNoteStringBase(note_number), out step)) { pitch.step = step; } pitch.octave = octave.ToString(); int alter = VsqNote.getNoteAlter(note_number); if (alter != 0) { pitch.alter = alter; pitch.alterSpecified = true; } } int clock_end = clock_start + length; int current_clock = clock_start; while (current_clock < clock_start + length) { int next_bar_clock = timesig_table.getClockFromBarCount(timesig_table.getBarCountFromClock(current_clock) + 1); int next_tempo_change = clock_end; if (small_tempo_table.Count > 0) { try { next_tempo_change = small_tempo_table .First((tempo) => { return(current_clock < tempo.Clock && tempo.Clock < clock_end); }) .Clock; } catch (InvalidOperationException) { } } int next_separation_point = Math.Min(Math.Min(clock_end, next_bar_clock), next_tempo_change); int remain = next_separation_point - current_clock; var template = templates_ .OrderByDescending((note) => note.duration.First) .FirstOrDefault((note) => note.duration.First <= remain); if (template == null) { break; } else { var note = template.Clone(); if (create_rest) { note.rest.Add(new rest()); } else { note.pitch.Add(pitch.Clone()); note.stem = new stem(); note.stem.Value = stemvalue.up; } notes_.Add(note); current_clock += (int)note.duration.First; } } // connect note with tie if (!create_rest && notes_.Count >= 2) { for (int i = 0; i < notes_.Count; ++i) { var note = notes_[i]; var tied_list = new List <tied>(); if (i < notes_.Count - 1) { var start_tie = new tie(); start_tie.type = startstop.start; note.tie.Add(start_tie); var tied = new tied(); tied.type = startstopcontinue.start; tied_list.Add(tied); } if (0 < i) { var stop_tie = new tie(); stop_tie.type = startstop.stop; note.tie.Add(stop_tie); var tied = new tied(); tied.type = startstopcontinue.stop; tied_list.Add(tied); } var notations = new notations(); notations.Items = tied_list.ToArray(); note.notations = new MusicXML.notations[] { notations }; } } }
/// <summary> /// Initialize by start clock and length. This constructor creates a 'rest'. /// </summary> /// <param name="clock_start"></param> /// <param name="length"></param> /// <param name="timesig_table"></param> public TiedEvent(int clock_start, int length, TimesigVector timesig_table, TempoVector tempo_table) { Init(timesig_table, tempo_table, clock_start, length, 0, true); }
public void reflectVibratoPitch( VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable ) { base.reflectNoteEventPitch( item, pitchBend, pitchBendSensitivity, tempoTable ); }
/// <summary> /// 音符に付随するピッチベンドの情報を、PIT・PBS カーブに反映する /// </summary> /// <param name="item">音符</param> /// <param name="pitchBend">PIT カーブ</param> /// <param name="pitchBendSensitivity">PBS カーブ</param> /// <param name="tempoTable">テンポ情報</param> protected void reflectNoteEventPitch( VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable ) { if ( item.ID.type != VsqIDType.Anote ) return; // AquesTone2 では、note on と同 clock にピッチベンドイベントを送らないと音程が反映されないので、必ずピッチイベントが送られるようにする pitchBend.add( item.Clock, pitchBend.getValue( item.Clock ) ); if ( item.ID.VibratoHandle == null ) { return; } int startClock = item.Clock + item.ID.VibratoDelay; int vibratoLength = item.ID.Length - item.ID.VibratoDelay; var iterator = new VibratoPointIteratorByClock( tempoTable, item.ID.VibratoHandle.RateBP, item.ID.VibratoHandle.StartRate, item.ID.VibratoHandle.DepthBP, item.ID.VibratoHandle.StartDepth, startClock, vibratoLength ); var pitContext = new ByRef<int>( 0 ); var pbsContext = new ByRef<int>( 0 ); int pitAtEnd = pitchBend.getValue( startClock + vibratoLength ); int pbsAtEnd = pitchBendSensitivity.getValue( startClock + vibratoLength ); var pitBackup = (VsqBPList)pitchBend.Clone(); var pbsBackup = (VsqBPList)pitchBendSensitivity.Clone(); bool resetPBS = false; double maxNetPitchBendInCent = 0.0; for ( int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock ) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitchBend.getValue( clock, pitContext ); int pbs = pitchBendSensitivity.getValue( clock, pbsContext ); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max( maxNetPitchBendInCent, Math.Abs( netPitchBendInCent ) ); int draftPitchBend = (int)Math.Round( (netPitchBendInCent / 100.0) * pow2_13 / pbs ); if ( draftPitchBend < pitchBend.Minimum || pitchBend.Maximum < draftPitchBend ) { // pbs を変更せずにビブラートによるピッチベンドを反映しようとすると、 // pit が範囲を超えてしまう。 resetPBS = true; } else { if ( draftPitchBend != pit ) { pitchBend.add( clock, draftPitchBend ); } } } if ( !resetPBS ) { return; } pitchBend.Data = pitBackup.Data; // ピッチベンドの最大値を実現するのに必要なPBS int requiredPitchbendSensitivity = (int)Math.Ceiling( maxNetPitchBendInCent / 100.0 ); int pseudoMaxPitchbendSensitivity = 12; // AquesTone2 は最大 12 半音までベンドできる。 if ( requiredPitchbendSensitivity < pitchBendSensitivity.Minimum ) requiredPitchbendSensitivity = pitchBendSensitivity.Minimum; if ( pseudoMaxPitchbendSensitivity < requiredPitchbendSensitivity ) requiredPitchbendSensitivity = pseudoMaxPitchbendSensitivity; { int i = 0; while ( i < pitchBend.size() ) { var clock = pitchBend.getKeyClock( i ); if ( startClock <= clock && clock < startClock + vibratoLength ) { pitchBend.removeElementAt( i ); } else { ++i; } } } { int i = 0; while ( i < pitchBendSensitivity.size() ) { var clock = pitchBendSensitivity.getKeyClock( i ); if ( startClock <= clock && clock < startClock + vibratoLength ) { pitchBendSensitivity.removeElementAt( i ); } else { ++i; } } } if ( pitchBendSensitivity.getValue( startClock ) != requiredPitchbendSensitivity ) { pitchBendSensitivity.add( startClock, requiredPitchbendSensitivity ); } pitchBend.add( startClock + vibratoLength, pitAtEnd ); pitchBendSensitivity.add( startClock + vibratoLength, pbsAtEnd ); iterator.rewind(); pitContext.value = 0; pbsContext.value = 0; int lastPitchBend = pitchBend.getValue( startClock ); for ( int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock ) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitBackup.getValue( clock, pitContext ); int pbs = pbsBackup.getValue( clock, pbsContext ); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max( maxNetPitchBendInCent, Math.Abs( netPitchBendInCent ) ); int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / requiredPitchbendSensitivity); if ( draftPitchBend < pitchBend.Minimum ) draftPitchBend = pitchBend.Minimum; if ( pitchBend.Maximum < draftPitchBend ) draftPitchBend = pitchBend.Maximum; if ( draftPitchBend != lastPitchBend ) { pitchBend.add( clock, draftPitchBend ); lastPitchBend = draftPitchBend; } } }
public void testReflectVibratoPitch() { var tempoTable = new TempoVector(); var pitchBend = new VsqBPList(CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum()); var pitchBendSensitivity = new VsqBPList(CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum()); pitchBend.add(0, 8191); pitchBendSensitivity.add(0, 2); var generator = new AquesTone2WaveGeneratorStub(); var item = new VsqEvent(0, new VsqID()); item.ID.type = VsqIDType.Anote; item.ID.VibratoHandle = new VibratoHandle(); item.ID.VibratoHandle.StartRate = 0x40; item.ID.VibratoHandle.RateBP.clear(); item.ID.VibratoHandle.StartDepth = 0x40; item.ID.VibratoHandle.DepthBP.clear(); item.ID.setLength(480); item.ID.VibratoDelay = 430; item.ID.VibratoHandle.setLength(50); generator.reflectVibratoPitch(item, pitchBend, pitchBendSensitivity, tempoTable); { var expectedPit = new Dictionary <int, int> { { 0, 8191 }, { 430, 5461 }, { 431, 5467 }, { 432, 5484 }, { 433, 5514 }, { 434, 5555 }, { 435, 5608 }, { 436, 5672 }, { 437, 5748 }, { 438, 5835 }, { 439, 5932 }, { 440, 6041 }, { 441, 6096 }, { 442, 6151 }, { 443, 6205 }, { 444, 6257 }, { 445, 6309 }, { 446, 6360 }, { 447, 6410 }, { 448, 6459 }, { 449, 6507 }, { 450, 6553 }, { 451, 6598 }, { 452, 6642 }, { 453, 6684 }, { 454, 6725 }, { 455, 6764 }, { 456, 6802 }, { 457, 6838 }, { 458, 6873 }, { 459, 6906 }, { 460, 6937 }, { 461, 6967 }, { 462, 6994 }, { 463, 7020 }, { 464, 7044 }, { 465, 7066 }, { 466, 7087 }, { 467, 7105 }, { 468, 7121 }, { 469, 7136 }, { 470, 7148 }, { 471, 6989 }, { 472, 6826 }, { 473, 6660 }, { 474, 6491 }, { 475, 6320 }, { 476, 6149 }, { 477, 5976 }, { 478, 5804 }, { 479, 5632 }, { 480, 8191 } }; Assert.AreEqual(expectedPit.Count, pitchBend.size()); int i = 0; foreach (var pitInfo in expectedPit) { Assert.AreEqual(pitInfo.Key, pitchBend.getKeyClock(i)); Assert.AreEqual(pitInfo.Value, pitchBend.getElementA(i)); ++i; } } { var expectedPbs = new Dictionary <int, int> { { 0, 2 }, { 430, 3 }, { 480, 2 } }; Assert.AreEqual(expectedPbs.Count, pitchBendSensitivity.size()); int i = 0; foreach (var pbsInfo in expectedPbs) { Assert.AreEqual(pbsInfo.Key, pitchBendSensitivity.getKeyClock(i)); Assert.AreEqual(pbsInfo.Value, pitchBendSensitivity.getElementA(i)); ++i; } } }
public void reflectVibratoPitch(VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable) { base.reflectNoteEventPitch(item, pitchBend, pitchBendSensitivity, tempoTable); }
/// <summary> /// 音符に付随するピッチベンドの情報を、PIT・PBS カーブに反映する /// </summary> /// <param name="item">音符</param> /// <param name="pitchBend">PIT カーブ</param> /// <param name="pitchBendSensitivity">PBS カーブ</param> /// <param name="tempoTable">テンポ情報</param> protected void reflectNoteEventPitch(VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable) { if (item.ID.type != VsqIDType.Anote) { return; } // AquesTone2 では、note on と同 clock にピッチベンドイベントを送らないと音程が反映されないので、必ずピッチイベントが送られるようにする pitchBend.add(item.Clock, pitchBend.getValue(item.Clock)); if (item.ID.VibratoHandle == null) { return; } int startClock = item.Clock + item.ID.VibratoDelay; int vibratoLength = item.ID.Length - item.ID.VibratoDelay; var iterator = new VibratoPointIteratorByClock(tempoTable, item.ID.VibratoHandle.RateBP, item.ID.VibratoHandle.StartRate, item.ID.VibratoHandle.DepthBP, item.ID.VibratoHandle.StartDepth, startClock, vibratoLength); var pitContext = new ByRef <int>(0); var pbsContext = new ByRef <int>(0); int pitAtEnd = pitchBend.getValue(startClock + vibratoLength); int pbsAtEnd = pitchBendSensitivity.getValue(startClock + vibratoLength); var pitBackup = (VsqBPList)pitchBend.Clone(); var pbsBackup = (VsqBPList)pitchBendSensitivity.Clone(); bool resetPBS = false; double maxNetPitchBendInCent = 0.0; for (int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitchBend.getValue(clock, pitContext); int pbs = pitchBendSensitivity.getValue(clock, pbsContext); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max(maxNetPitchBendInCent, Math.Abs(netPitchBendInCent)); int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / pbs); if (draftPitchBend < pitchBend.Minimum || pitchBend.Maximum < draftPitchBend) { // pbs を変更せずにビブラートによるピッチベンドを反映しようとすると、 // pit が範囲を超えてしまう。 resetPBS = true; } else { if (draftPitchBend != pit) { pitchBend.add(clock, draftPitchBend); } } } if (!resetPBS) { return; } pitchBend.Data = pitBackup.Data; // ピッチベンドの最大値を実現するのに必要なPBS int requiredPitchbendSensitivity = (int)Math.Ceiling(maxNetPitchBendInCent / 100.0); int pseudoMaxPitchbendSensitivity = 12; // AquesTone2 は最大 12 半音までベンドできる。 if (requiredPitchbendSensitivity < pitchBendSensitivity.Minimum) { requiredPitchbendSensitivity = pitchBendSensitivity.Minimum; } if (pseudoMaxPitchbendSensitivity < requiredPitchbendSensitivity) { requiredPitchbendSensitivity = pseudoMaxPitchbendSensitivity; } { int i = 0; while (i < pitchBend.size()) { var clock = pitchBend.getKeyClock(i); if (startClock <= clock && clock < startClock + vibratoLength) { pitchBend.removeElementAt(i); } else { ++i; } } } { int i = 0; while (i < pitchBendSensitivity.size()) { var clock = pitchBendSensitivity.getKeyClock(i); if (startClock <= clock && clock < startClock + vibratoLength) { pitchBendSensitivity.removeElementAt(i); } else { ++i; } } } if (pitchBendSensitivity.getValue(startClock) != requiredPitchbendSensitivity) { pitchBendSensitivity.add(startClock, requiredPitchbendSensitivity); } pitchBend.add(startClock + vibratoLength, pitAtEnd); pitchBendSensitivity.add(startClock + vibratoLength, pbsAtEnd); iterator.rewind(); pitContext.value = 0; pbsContext.value = 0; int lastPitchBend = pitchBend.getValue(startClock); for (int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitBackup.getValue(clock, pitContext); int pbs = pbsBackup.getValue(clock, pbsContext); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max(maxNetPitchBendInCent, Math.Abs(netPitchBendInCent)); int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / requiredPitchbendSensitivity); if (draftPitchBend < pitchBend.Minimum) { draftPitchBend = pitchBend.Minimum; } if (pitchBend.Maximum < draftPitchBend) { draftPitchBend = pitchBend.Maximum; } if (draftPitchBend != lastPitchBend) { pitchBend.add(clock, draftPitchBend); lastPitchBend = draftPitchBend; } } }
public RenderedStatus() { track = new VsqTrack(0, 0, 0); tempo = new TempoVector(); config = new SequenceConfig(); }
private List <TiedEvent> quantizeTrack(VsqTrack track, TimesigVector timesig_table, TempoVector tempo_table) { var result = new List <TiedEvent>(); if (track.MetaText == null) { return(result); } track.MetaText.Events.Events .AsParallel() .ForAll((item) => { if (item.ID != null) { var start_clock = quantize(item.Clock); var end_clock = quantize(item.Clock + item.ID.getLength()); item.Clock = start_clock; item.ID.setLength(end_clock - start_clock); } }); track.MetaText.Events.Events .RemoveAll((item) => { if (item.ID == null) { return(false); } return(item.ID.getLength() <= 0); }); int count = track.MetaText.Events.Events.Count; int clock = 0; for (int i = 0; i < count; ++i) { var item = track.MetaText.Events.Events[i]; if (item.ID.type == VsqIDType.Anote) { int rest_length = item.Clock - clock; if (rest_length > 0) { result.Add(new TiedEvent(clock, rest_length, timesig_table, tempo_table)); } result.Add(new TiedEvent(item, timesig_table, tempo_table)); clock = item.Clock + item.ID.getLength(); } } return(result); }
/// <summary> /// このWAVE描画コンテキストが保持しているWAVEデータを、ゲートタイム基準でグラフィクスに描画します。 /// </summary> /// <param name="g">描画に使用するグラフィクスオブジェクト</param> /// <param name="pen">描画に使用するペン</param> /// <param name="rect">描画範囲</param> /// <param name="clock_start">描画開始位置のゲートタイム</param> /// <param name="clock_end">描画終了位置のゲートタイム</param> /// <param name="tempo_table">ゲートタイムから秒数を調べる際使用するテンポ・テーブル</param> /// <param name="pixel_per_clock">ゲートタイムあたりの秒数</param> /// <param name="scale_y">Y軸方向の描画スケール。デフォルトは1.0</param> /// <param name="auto_maximize">自動で最大化するかどうか</param> private void drawCore( Graphics2D g, Color pen, Rectangle rect, int clock_start, int clock_end, TempoVector tempo_table, float pixel_per_clock, float scale_y, bool auto_maximize) { if (mWave.Length == 0) { return; } #if DEBUG double startedTime = PortUtil.getCurrentTime(); #endif mDrawer.setGraphics(g); mDrawer.clear(); double secStart = tempo_table.getSecFromClock(clock_start); double secEnd = tempo_table.getSecFromClock(clock_end); int sStart0 = (int)(secStart * mSampleRate) - 1; int sEnd0 = (int)(secEnd * mSampleRate) + 1; int count = tempo_table.Count; int sStart = 0; double cStart = 0.0; float order_y = 1.0f; if (auto_maximize) { order_y = rect.height / 2.0f / 127.0f * mMaxAmplitude / mActualMaxAmplitude; } else { order_y = rect.height / 127.0f * scale_y * mMaxAmplitude; } int ox = rect.x; int oy = rect.height / 2; int last = mWave[0]; int lastx = ox; int lastYMax = oy - (int)(last * order_y); int lastYMin = lastYMax; int lasty = lastYMin; int lasty2 = lastYMin; bool skipped = false; mDrawer.append(ox, lasty); int xmax = rect.x + rect.width; int lastTempo = 500000; for (int i = 0; i <= count; i++) { double time = 0.0; int tempo = 500000; int cEnd = 0; if (i < count) { TempoTableEntry entry = tempo_table[i]; time = entry.Time; tempo = entry.Tempo; cEnd = entry.Clock; } else { time = tempo_table.getSecFromClock(clock_end); tempo = tempo_table[i - 1].Tempo; cEnd = clock_end; } int sEnd = (int)(time * mSampleRate); // sStartサンプルからsThisEndサンプルまでを描画する(必要なら!) if (sEnd < sStart0) { sStart = sEnd; cStart = cEnd; lastTempo = tempo; continue; } if (sEnd0 < sStart) { break; } // int xoffset = (int)(cStart * pixel_per_clock) - AppManager.mMainWindowController.getStartToDrawX() + AppManager.keyOffset; double sec_per_clock = lastTempo * 1e-6 / 480.0; lastTempo = tempo; double pixel_per_sample = 1.0 / mSampleRate / sec_per_clock * pixel_per_clock; int j0 = sStart; if (j0 < 0) { j0 = 0; } int j1 = sEnd; if (mWave.Length < j1) { j1 = mWave.Length; } // 第j0サンプルのデータを画面に描画したときのx座標がいくらになるか? int draftStartX = xoffset + (int)((j0 - sStart) * pixel_per_sample); if (draftStartX < rect.x) { j0 = (int)((rect.x - xoffset) / pixel_per_sample) + sStart; } // 第j1サンプルのデータを画面に描画した時のx座標がいくらになるか? int draftEndX = xoffset + (int)((j1 - sStart) * pixel_per_sample); if (rect.x + rect.width < draftEndX) { j1 = (int)((rect.x + rect.width - xoffset) / pixel_per_sample) + sStart; } bool breakRequired = false; for (int j = j0; j < j1; j++) { int v = mWave[j]; if (v == last) { skipped = true; continue; } int x = xoffset + (int)((j - sStart) * pixel_per_sample); if (xmax < x) { breakRequired = true; break; } if (x < rect.x) { continue; } int y = oy - (int)(v * order_y); if (lastx == x) { lastYMax = Math.Max(lastYMax, y); lastYMin = Math.Min(lastYMin, y); continue; } if (skipped) { mDrawer.append(x - 1, lasty); lastx = x - 1; } if (lastYMax == lastYMin) { mDrawer.append(x, y); } else { if (lasty2 != lastYMin) { mDrawer.append(lastx, lastYMin); } mDrawer.append(lastx, lastYMax); if (lastYMax != lasty) { mDrawer.append(lastx, lasty); } mDrawer.append(x, y); } lasty2 = lasty; lastx = x; lastYMin = y; lastYMax = y; lasty = y; last = v; skipped = false; } sStart = sEnd; cStart = cEnd; if (breakRequired) { break; } } mDrawer.append(rect.x + rect.width, lasty); mDrawer.flush(); }