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; } } }
private static void amplify( VsqBPList source, VsqBPList target, double amplify ) { target.clear(); int count = source.size(); int min = target.getMinimum(); int max = target.getMaximum(); for ( int i = 0; i < count; i++ ) { int clock = source.getKeyClock( i ); int value = (int)(source.getElement( i ) * amplify); if ( value < min ) { value = min; } if ( max < value ) { value = max; } target.add( clock, value ); } }
private static void amplify(VsqBPList source, VsqBPList target, double amplify) { target.clear(); int count = source.size(); int min = target.getMinimum(); int max = target.getMaximum(); for (int i = 0; i < count; i++) { int clock = source.getKeyClock(i); int value = (int)(source.getElement(i) * amplify); if (value < min) { value = min; } if (max < value) { value = max; } target.add(clock, value); } }
private static void copyCurve( VsqBPList src, VsqBPList dest, int clock_shift ){ int last_value = src.getDefault(); int count = src.size(); bool first_over_zero = true; for( int i = 0; i < count; i++ ){ int cl = src.getKeyClock( i ) - clock_shift; int value = src.getElementA( i ); if( cl < 0 ){ last_value = value; }else{ if( first_over_zero ){ first_over_zero = false; if( last_value != src.getDefault() ){ dest.add( 0, last_value ); } } dest.add( cl, value ); } } }
/// <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; } } }
/// <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; } } }