/// <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; } } }
/// <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; } } }