@@ -95,6 +95,7 @@ class PlayThroughContext {
9595export class MidiFileGenerator {
9696 private static readonly _defaultDurationDead : number = 30 ;
9797 private static readonly _defaultDurationPalmMute : number = 80 ;
98+ private static readonly _fullExpression : number = 127 ;
9899
99100 private readonly _score : Score ;
100101 private _settings : Settings ;
@@ -103,6 +104,7 @@ export class MidiFileGenerator {
103104
104105 private _currentTime : number = 0 ;
105106 private _calculatedBeatTimers : Set < number > = new Set < number > ( ) ;
107+ private _fadedVoices : Set < string > = new Set < string > ( ) ;
106108
107109 /**
108110 * Gets a lookup object which can be used to quickly find beats and bars
@@ -143,6 +145,7 @@ export class MidiFileGenerator {
143145 public generate ( ) : void {
144146 this . transpositionPitches . clear ( ) ;
145147 this . _calculatedBeatTimers . clear ( ) ;
148+ this . _fadedVoices . clear ( ) ;
146149 this . _currentTime = 0 ;
147150
148151 // initialize tracks
@@ -757,6 +760,14 @@ export class MidiFileGenerator {
757760 for ( const automation of beat . automations ) {
758761 this . _generateNonTempoAutomation ( beat , automation , barStartTick ) ;
759762 }
763+ const voiceKey = MidiFileGenerator . _voiceKey ( beat ) ;
764+ if ( ! beat . isRest && this . _fadedVoices . has ( voiceKey ) ) {
765+ this . _fadedVoices . delete ( voiceKey ) ;
766+ // a fading beat sets the expression with its own ramp
767+ if ( beat . fade === FadeType . None ) {
768+ this . _addExpression ( track , barStartTick + beatStart , MidiFileGenerator . _fullExpression ) ;
769+ }
770+ }
760771 if ( beat . isRest ) {
761772 this . _handler . addRest ( track . index , barStartTick + beatStart , track . playbackInfo . primaryChannel ) ;
762773 } else if ( beat . deadSlapped ) {
@@ -1314,56 +1325,39 @@ export class MidiFileGenerator {
13141325 const track : Track = beat . voice . bar . staff . track ;
13151326 switch ( beat . fade ) {
13161327 case FadeType . FadeIn :
1317- this . _generateFadeSteps (
1318- track ,
1319- beatStart ,
1320- beatDuration ,
1321- 0 ,
1322- MidiFileGenerator . _toChannelShort ( track . playbackInfo . volume )
1323- ) ;
1324- break ;
1328+ this . _generateFadeSteps ( track , beatStart , beatDuration , 0 , MidiFileGenerator . _fullExpression ) ;
1329+ return ;
13251330 case FadeType . FadeOut :
1326- this . _generateFadeSteps (
1327- track ,
1328- beatStart ,
1329- beatDuration ,
1330- MidiFileGenerator . _toChannelShort ( track . playbackInfo . volume ) ,
1331- 0
1332- ) ;
1331+ this . _generateFadeSteps ( track , beatStart , beatDuration , MidiFileGenerator . _fullExpression , 0 ) ;
13331332 break ;
13341333 case FadeType . VolumeSwell :
13351334 const half = ( beatDuration / 2 ) | 0 ;
1336- this . _generateFadeSteps (
1337- track ,
1338- beatStart ,
1339- half ,
1340- 0 ,
1341- MidiFileGenerator . _toChannelShort ( track . playbackInfo . volume )
1342- ) ;
1343- this . _generateFadeSteps (
1344- track ,
1345- beatStart + half ,
1346- half ,
1347- MidiFileGenerator . _toChannelShort ( track . playbackInfo . volume ) ,
1348- 0
1349- ) ;
1350-
1335+ this . _generateFadeSteps ( track , beatStart , half , 0 , MidiFileGenerator . _fullExpression ) ;
1336+ this . _generateFadeSteps ( track , beatStart + half , half , MidiFileGenerator . _fullExpression , 0 ) ;
13511337 break ;
13521338 }
1339+
1340+ // the expression is restored with the voice's next non-rest beat, so the release of the faded notes stays silent
1341+ this . _fadedVoices . add ( MidiFileGenerator . _voiceKey ( beat ) ) ;
1342+ }
1343+
1344+ private static _voiceKey ( beat : Beat ) : string {
1345+ const staff = beat . voice . bar . staff ;
1346+ return `${ staff . track . index } :${ staff . index } :${ beat . voice . index } ` ;
13531347 }
13541348
13551349 private _generateFadeSteps (
13561350 track : Track ,
13571351 start : number ,
13581352 duration : number ,
1359- startVolume : number ,
1360- endVolume : number
1353+ startExpression : number ,
1354+ endExpression : number
13611355 ) : void {
13621356 const tickStep : number = 120 ;
13631357 // we want to reach the target volume a bit earlier than the end of the note
13641358 duration = ( duration * 0.8 ) | 0 ;
13651359
1366- const volumeFactor : number = ( endVolume - startVolume ) / duration ;
1360+ const expressionFactor : number = ( endExpression - startExpression ) / duration ;
13671361
13681362 const steps : number = ( duration / tickStep + 1 ) | 0 ;
13691363 const endTick : number = start + duration ;
@@ -1372,25 +1366,30 @@ export class MidiFileGenerator {
13721366 // ensure final value at end depending on rounding we might not reach it exactly
13731367 const isLast = i === steps - 1 ;
13741368 const tick : number = isLast ? endTick : start + i * tickStep ;
1375- const volume : number = isLast ? endVolume : Math . round ( startVolume + ( tick - start ) * volumeFactor ) ;
1376-
1377- this . _handler . addControlChange (
1378- track . index ,
1379- tick ,
1380- track . playbackInfo . primaryChannel ,
1381- ControllerType . VolumeCoarse ,
1382- volume
1383- ) ;
1384- this . _handler . addControlChange (
1385- track . index ,
1386- tick ,
1387- track . playbackInfo . secondaryChannel ,
1388- ControllerType . VolumeCoarse ,
1389- volume
1390- ) ;
1369+ const expression : number = isLast
1370+ ? endExpression
1371+ : Math . round ( startExpression + ( tick - start ) * expressionFactor ) ;
1372+ this . _addExpression ( track , tick , expression ) ;
13911373 }
13921374 }
13931375
1376+ private _addExpression ( track : Track , tick : number , expression : number ) : void {
1377+ this . _handler . addControlChange (
1378+ track . index ,
1379+ tick ,
1380+ track . playbackInfo . primaryChannel ,
1381+ ControllerType . ExpressionControllerCoarse ,
1382+ expression
1383+ ) ;
1384+ this . _handler . addControlChange (
1385+ track . index ,
1386+ tick ,
1387+ track . playbackInfo . secondaryChannel ,
1388+ ControllerType . ExpressionControllerCoarse ,
1389+ expression
1390+ ) ;
1391+ }
1392+
13941393 private _generateVibrato (
13951394 note : Note ,
13961395 noteStart : number ,
@@ -2446,7 +2445,12 @@ export class MidiFileGenerator {
24462445
24472446 public generateSingleBeat ( beat : Beat ) {
24482447 const tempo = this . prepareSingleBeat ( beat ) ;
2448+ this . _fadedVoices . clear ( ) ;
24492449 this . _generateBeat ( beat , - beat . playbackStart /* to bring it to 0*/ , beat . voice . bar , tempo ) ;
2450+ if ( this . _fadedVoices . size > 0 ) {
2451+ this . _fadedVoices . clear ( ) ;
2452+ this . _addExpression ( beat . voice . bar . staff . track , beat . playbackDuration , MidiFileGenerator . _fullExpression ) ;
2453+ }
24502454 }
24512455
24522456 public generateSingleNote ( note : Note ) {
0 commit comments