import { computed , observable } from 'mobx' ; import * as _ from 'lodash' ; import editor from './editor' ; import featureFlags from './feature-flags' ; import { Note } from './note' ; import { TOTAL_SIXTEENTHS } from './constants' ; import { undoable } from './undo' ; import { player } from '../core' ; export class EditorMaskRegion { constructor ( public topLeftValue , public topLeftPosition , public bottomRightValue , public bottomRightPosition ) { } } export type IMask = number [ ] ; export type IMasks = [ IMask , IMask , IMask , IMask ] ; class Masks { @ observable private _userMasks : IMasks = this . emptyMasks ( ) ; @ computed private get _implicitMasks ( ) { return featureFlags . baseline ? this . computeMasksFromEmptyRegions ( ) : this . emptyMasks ( ) ; } @ computed get userMasks ( ) { return this . _userMasks ; } @ computed get implicitMasks ( ) { return this . _implicitMasks ; } @ computed private get userOrImplicitMasks ( ) { if ( featureFlags . baseline ) { return this . doUserMasksExist ? this . _userMasks : this . _implicitMasks ; } else { return this . _userMasks ; } } @ computed get generationMasks ( ) : IMasks { if ( featureFlags . baseline ) { return this . userOrImplicitMasks ; } else { return this . constrainToLoopBounds ( this . userOrImplicitMasks ) ; } } private constrainToLoopBounds ( masks ) { const { loopStart , loopEnd } = player ; return masks . map ( mask => { return mask . filter ( value => value >= loopStart && value < loopEnd ) ; } ) as IMasks ; } emptyMasks ( ) { return [ [ ] , [ ] , [ ] , [ ] ] ; } private getPositionsPerVoice ( notes ) { const notePositions = _ . range ( 4 ) . map ( ( ) => new Set < number > ( ) ) ; notes . forEach ( note => { const positions = _ . range ( note . start , note . end ) ; positions . forEach ( position => notePositions [ note . voice ] . add ( position ) ) ; } ) ; return notePositions ; } private computeMasksFromEmptyRegions ( ) { const notePositions = this . getPositionsPerVoice ( editor . allNotes ) ; const _masks = _ . range ( 4 ) . map ( voice => { return _ . range ( TOTAL_SIXTEENTHS ) . filter ( position => ! notePositions [ voice ] . has ( position ) ) ; } ) as IMasks ; return _masks ; } @ undoable ( 'masks.maskNotes' ) maskNotes ( notesInMask , replaceMask = true ) { const notePositions = this . getPositionsPerVoice ( notesInMask ) ; notePositions . forEach ( ( positions , voice ) => { if ( replaceMask ) { this . setMask ( voice , [ ... positions . values ( ) ] ) ; } else { this . addMask ( voice , [ ... positions . values ( ) ] ) ; } } ) ; } @ undoable ( 'masks.setMask' ) setMask ( voiceIndex , mask ) { this . _setMask ( voiceIndex , mask ) ; } _setMask ( voiceIndex , mask ) { this . _userMasks [ voiceIndex ] = [ ... mask ] ; } @ undoable ( 'masks.setMasks' ) setMasks ( masks ) { this . _userMasks = masks ; } addMask ( voiceIndex , mask ) { const maskSet = new Set < number > ( this . _userMasks [ voiceIndex ] ) ; mask . forEach ( maskIndex => maskSet . add ( maskIndex ) ) ; this . _userMasks [ voiceIndex ] = [ ... maskSet . values ( ) ] . sort ( ( a , b ) => a - b ) ; } removeMask ( voiceIndex , mask ) { const maskSet = new Set < number > ( this . _userMasks [ voiceIndex ] ) ; mask . forEach ( maskIndex => maskSet . delete ( maskIndex ) ) ; this . _userMasks [ voiceIndex ] = [ ... maskSet . values ( ) ] . sort ( ( a , b ) => a - b ) ; } clearMasks ( ) { this . _userMasks = this . emptyMasks ( ) ; } isNoteMasked ( note ) { const { voice , start , end } = note ; const mask = this . userOrImplicitMasks [ voice ] ; for ( let maskIndex of mask ) { if ( maskIndex >= start && maskIndex < end ) return true ; } return false ; } @ computed get maskedNotes ( ) { const notes = editor . allNotes ; const maskedNotes = [ ] ; for ( const note of notes ) { if ( this . isNoteMasked ( note ) ) maskedNotes . push ( note ) ; } return maskedNotes ; } @ computed get nonMaskedNotes ( ) { return editor . allNotes . filter ( note => ! this . isNoteMasked ( note ) ) ; } addImplicitMasksToUserMasks ( ) { this . _implicitMasks . forEach ( ( mask , voice ) => { this . addMask ( voice , mask ) ; } ) ; } removeImplicitMasksFromUserMasks ( ) { this . _implicitMasks . forEach ( ( mask , voice ) => { this . removeMask ( voice , mask ) ; } ) ; } @ computed get doUserMasksExist ( ) { return _ . some ( this . _userMasks , mask => mask . length > 0 ) ; } @ computed get doImplicitMasksExist ( ) { return _ . some ( this . _implicitMasks , mask => mask . length > 0 ) ; } @ computed get doAnyMasksExist ( ) { return this . doUserMasksExist || this . doImplicitMasksExist ; } } export default new Masks ( ) ;