musicxml-io 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/accessors/index.d.mts +280 -2
- package/dist/accessors/index.d.ts +280 -2
- package/dist/accessors/index.js +1299 -1
- package/dist/accessors/index.mjs +1251 -1
- package/dist/index-DCty0U8p.d.ts +377 -0
- package/dist/index-nCaclfqu.d.mts +377 -0
- package/dist/index.d.mts +6 -184
- package/dist/index.d.ts +6 -184
- package/dist/index.js +2087 -309
- package/dist/index.mjs +2039 -308
- package/dist/operations/index.d.mts +2 -92
- package/dist/operations/index.d.ts +2 -92
- package/dist/operations/index.js +1580 -150
- package/dist/operations/index.mjs +1562 -148
- package/dist/query/index.d.mts +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +0 -1
- package/dist/query/index.mjs +0 -1
- package/dist/{types-DC_TnJu_.d.ts → types-Dp9zcgIg.d.mts} +298 -3
- package/dist/{types-DC_TnJu_.d.mts → types-Dp9zcgIg.d.ts} +298 -3
- package/package.json +6 -2
- package/dist/accessors/index.js.map +0 -1
- package/dist/accessors/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/operations/index.js.map +0 -1
- package/dist/operations/index.mjs.map +0 -1
- package/dist/query/index.js.map +0 -1
- package/dist/query/index.mjs.map +0 -1
|
@@ -215,6 +215,7 @@ interface MeasureAttributes {
|
|
|
215
215
|
keys?: KeySignature[];
|
|
216
216
|
clef?: Clef[];
|
|
217
217
|
staves?: number;
|
|
218
|
+
instruments?: number;
|
|
218
219
|
transpose?: Transpose;
|
|
219
220
|
transposes?: Transpose[];
|
|
220
221
|
staffDetails?: StaffDetails[];
|
|
@@ -265,7 +266,7 @@ interface TimeSignature {
|
|
|
265
266
|
}
|
|
266
267
|
interface KeySignature {
|
|
267
268
|
fifths: number;
|
|
268
|
-
mode?: 'major' | 'minor' | 'dorian' | 'phrygian' | 'lydian' | 'mixolydian' | 'aeolian' | 'ionian' | 'locrian';
|
|
269
|
+
mode?: 'major' | 'minor' | 'dorian' | 'phrygian' | 'lydian' | 'mixolydian' | 'aeolian' | 'ionian' | 'locrian' | 'none';
|
|
269
270
|
cancel?: number;
|
|
270
271
|
cancelLocation?: 'left' | 'right' | 'before-barline';
|
|
271
272
|
number?: number;
|
|
@@ -284,6 +285,8 @@ interface Clef {
|
|
|
284
285
|
line: number;
|
|
285
286
|
staff?: number;
|
|
286
287
|
clefOctaveChange?: number;
|
|
288
|
+
printObject?: boolean;
|
|
289
|
+
afterBarline?: boolean;
|
|
287
290
|
}
|
|
288
291
|
interface Transpose {
|
|
289
292
|
diatonic: number;
|
|
@@ -292,7 +295,7 @@ interface Transpose {
|
|
|
292
295
|
}
|
|
293
296
|
interface Barline {
|
|
294
297
|
location: 'left' | 'right' | 'middle';
|
|
295
|
-
barStyle?: 'regular' | 'dotted' | 'dashed' | 'heavy' | 'light-light' | 'light-heavy' | 'heavy-light' | 'heavy-heavy' | 'none';
|
|
298
|
+
barStyle?: 'regular' | 'dotted' | 'dashed' | 'heavy' | 'light-light' | 'light-heavy' | 'heavy-light' | 'heavy-heavy' | 'tick' | 'short' | 'none';
|
|
296
299
|
repeat?: {
|
|
297
300
|
direction: 'forward' | 'backward';
|
|
298
301
|
times?: number;
|
|
@@ -372,6 +375,9 @@ interface DirectionSound {
|
|
|
372
375
|
volume?: number;
|
|
373
376
|
pan?: number;
|
|
374
377
|
};
|
|
378
|
+
damperPedal?: 'yes' | 'no';
|
|
379
|
+
softPedal?: 'yes' | 'no';
|
|
380
|
+
sostenutoPedal?: 'yes' | 'no';
|
|
375
381
|
}
|
|
376
382
|
interface DirectionEntry {
|
|
377
383
|
type: 'direction';
|
|
@@ -403,6 +409,9 @@ interface SoundEntry {
|
|
|
403
409
|
fine?: boolean;
|
|
404
410
|
forwardRepeat?: boolean;
|
|
405
411
|
swing?: Swing;
|
|
412
|
+
damperPedal?: boolean | 'yes' | 'no';
|
|
413
|
+
softPedal?: boolean | 'yes' | 'no';
|
|
414
|
+
sostenutoPedal?: boolean | 'yes' | 'no';
|
|
406
415
|
}
|
|
407
416
|
interface HarmonyEntry {
|
|
408
417
|
type: 'harmony';
|
|
@@ -561,6 +570,7 @@ interface SlurNotation extends BaseNotation {
|
|
|
561
570
|
slurType: 'start' | 'stop' | 'continue';
|
|
562
571
|
number?: number;
|
|
563
572
|
lineType?: 'solid' | 'dashed' | 'dotted' | 'wavy';
|
|
573
|
+
orientation?: 'over' | 'under';
|
|
564
574
|
defaultX?: number;
|
|
565
575
|
defaultY?: number;
|
|
566
576
|
bezierX?: number;
|
|
@@ -696,6 +706,8 @@ type DirectionType = {
|
|
|
696
706
|
number?: number;
|
|
697
707
|
lineEnd?: 'up' | 'down' | 'both' | 'arrow' | 'none';
|
|
698
708
|
lineType?: 'solid' | 'dashed' | 'dotted' | 'wavy';
|
|
709
|
+
defaultY?: number;
|
|
710
|
+
relativeX?: number;
|
|
699
711
|
} | {
|
|
700
712
|
kind: 'dashes';
|
|
701
713
|
type: 'start' | 'stop' | 'continue';
|
|
@@ -793,5 +805,288 @@ interface NoteIteratorItem {
|
|
|
793
805
|
note: NoteEntry;
|
|
794
806
|
position: number;
|
|
795
807
|
}
|
|
808
|
+
/**
|
|
809
|
+
* Voice to Staff mapping for inferring staff when not explicitly specified
|
|
810
|
+
*/
|
|
811
|
+
interface VoiceToStaffMap {
|
|
812
|
+
get(voice: number): number | undefined;
|
|
813
|
+
has(voice: number): boolean;
|
|
814
|
+
entries(): IterableIterator<[number, number]>;
|
|
815
|
+
size: number;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Note with full context information
|
|
819
|
+
*/
|
|
820
|
+
interface NoteWithContext {
|
|
821
|
+
note: NoteEntry;
|
|
822
|
+
part: Part;
|
|
823
|
+
partIndex: number;
|
|
824
|
+
measure: Measure;
|
|
825
|
+
measureIndex: number;
|
|
826
|
+
position: number;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Entry with context (for iterateEntries)
|
|
830
|
+
*/
|
|
831
|
+
interface EntryWithContext {
|
|
832
|
+
entry: MeasureEntry;
|
|
833
|
+
part: Part;
|
|
834
|
+
partIndex: number;
|
|
835
|
+
measure: Measure;
|
|
836
|
+
measureIndex: number;
|
|
837
|
+
position: number;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Direction with context information
|
|
841
|
+
*/
|
|
842
|
+
interface DirectionWithContext {
|
|
843
|
+
direction: DirectionEntry;
|
|
844
|
+
part: Part;
|
|
845
|
+
partIndex: number;
|
|
846
|
+
measure: Measure;
|
|
847
|
+
measureIndex: number;
|
|
848
|
+
position: number;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Staff range (min and max staff numbers)
|
|
852
|
+
*/
|
|
853
|
+
interface StaffRange {
|
|
854
|
+
min: number;
|
|
855
|
+
max: number;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Options for position-based queries
|
|
859
|
+
*/
|
|
860
|
+
interface PositionQueryOptions {
|
|
861
|
+
staff?: number;
|
|
862
|
+
voice?: number;
|
|
863
|
+
includeChordNotes?: boolean;
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Vertical slice - all notes at a specific position across all parts
|
|
867
|
+
*/
|
|
868
|
+
interface VerticalSlice {
|
|
869
|
+
measureIndex: number;
|
|
870
|
+
position: number;
|
|
871
|
+
parts: Map<number, NoteEntry[]>;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Voice line - continuous melodic line across measures
|
|
875
|
+
*/
|
|
876
|
+
interface VoiceLine {
|
|
877
|
+
partIndex: number;
|
|
878
|
+
voice: number;
|
|
879
|
+
staff?: number;
|
|
880
|
+
notes: NoteWithContext[];
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Adjacent notes (previous and next)
|
|
884
|
+
*/
|
|
885
|
+
interface AdjacentNotes {
|
|
886
|
+
prev: NoteWithContext | null;
|
|
887
|
+
next: NoteWithContext | null;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Direction kind type for filtering
|
|
891
|
+
*/
|
|
892
|
+
type DirectionKind = DirectionType['kind'];
|
|
893
|
+
/**
|
|
894
|
+
* Dynamic marking with context
|
|
895
|
+
*/
|
|
896
|
+
interface DynamicWithContext {
|
|
897
|
+
dynamic: DynamicsValue;
|
|
898
|
+
direction: DirectionEntry;
|
|
899
|
+
part: Part;
|
|
900
|
+
partIndex: number;
|
|
901
|
+
measure: Measure;
|
|
902
|
+
measureIndex: number;
|
|
903
|
+
position: number;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Tempo marking with context
|
|
907
|
+
*/
|
|
908
|
+
interface TempoWithContext {
|
|
909
|
+
beatUnit: NoteType;
|
|
910
|
+
perMinute?: number | string;
|
|
911
|
+
beatUnitDot?: boolean;
|
|
912
|
+
direction: DirectionEntry;
|
|
913
|
+
partIndex: number;
|
|
914
|
+
measureIndex: number;
|
|
915
|
+
position: number;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Pedal marking with context
|
|
919
|
+
*/
|
|
920
|
+
interface PedalWithContext {
|
|
921
|
+
pedalType: 'start' | 'stop' | 'change' | 'continue';
|
|
922
|
+
direction: DirectionEntry;
|
|
923
|
+
partIndex: number;
|
|
924
|
+
measureIndex: number;
|
|
925
|
+
position: number;
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Wedge (crescendo/diminuendo) with context
|
|
929
|
+
*/
|
|
930
|
+
interface WedgeWithContext {
|
|
931
|
+
wedgeType: 'crescendo' | 'diminuendo' | 'stop';
|
|
932
|
+
direction: DirectionEntry;
|
|
933
|
+
partIndex: number;
|
|
934
|
+
measureIndex: number;
|
|
935
|
+
position: number;
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Octave shift with context
|
|
939
|
+
*/
|
|
940
|
+
interface OctaveShiftWithContext {
|
|
941
|
+
shiftType: 'up' | 'down' | 'stop';
|
|
942
|
+
size?: number;
|
|
943
|
+
direction: DirectionEntry;
|
|
944
|
+
partIndex: number;
|
|
945
|
+
measureIndex: number;
|
|
946
|
+
position: number;
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* A group of tied notes (notes connected by ties)
|
|
950
|
+
*/
|
|
951
|
+
interface TiedNoteGroup {
|
|
952
|
+
notes: NoteWithContext[];
|
|
953
|
+
/** Total duration of all tied notes */
|
|
954
|
+
totalDuration: number;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* A slur span from start to stop
|
|
958
|
+
*/
|
|
959
|
+
interface SlurSpan {
|
|
960
|
+
number: number;
|
|
961
|
+
startNote: NoteWithContext;
|
|
962
|
+
endNote: NoteWithContext;
|
|
963
|
+
/** Notes covered by the slur (including start and end) */
|
|
964
|
+
notes: NoteWithContext[];
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* A tuplet group
|
|
968
|
+
*/
|
|
969
|
+
interface TupletGroup {
|
|
970
|
+
number: number;
|
|
971
|
+
notes: NoteWithContext[];
|
|
972
|
+
/** Actual notes (numerator of time modification) */
|
|
973
|
+
actualNotes: number;
|
|
974
|
+
/** Normal notes (denominator of time modification) */
|
|
975
|
+
normalNotes: number;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* A beam group (notes connected by beams)
|
|
979
|
+
*/
|
|
980
|
+
interface BeamGroup {
|
|
981
|
+
notes: NoteWithContext[];
|
|
982
|
+
/** Beam level (1 for eighth notes, 2 for sixteenth, etc.) */
|
|
983
|
+
beamLevel: number;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Filter for finding notes with specific notations
|
|
987
|
+
*/
|
|
988
|
+
type NotationType = Notation['type'];
|
|
989
|
+
/**
|
|
990
|
+
* Harmony entry with context
|
|
991
|
+
*/
|
|
992
|
+
interface HarmonyWithContext {
|
|
993
|
+
harmony: HarmonyEntry;
|
|
994
|
+
part: Part;
|
|
995
|
+
partIndex: number;
|
|
996
|
+
measure: Measure;
|
|
997
|
+
measureIndex: number;
|
|
998
|
+
position: number;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Lyric entry with context
|
|
1002
|
+
*/
|
|
1003
|
+
interface LyricWithContext {
|
|
1004
|
+
lyric: Lyric;
|
|
1005
|
+
note: NoteEntry;
|
|
1006
|
+
part: Part;
|
|
1007
|
+
partIndex: number;
|
|
1008
|
+
measure: Measure;
|
|
1009
|
+
measureIndex: number;
|
|
1010
|
+
position: number;
|
|
1011
|
+
verse: number;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Assembled lyrics for a verse
|
|
1015
|
+
*/
|
|
1016
|
+
interface AssembledLyrics {
|
|
1017
|
+
verse: number;
|
|
1018
|
+
text: string;
|
|
1019
|
+
syllables: {
|
|
1020
|
+
text: string;
|
|
1021
|
+
position: number;
|
|
1022
|
+
measureIndex: number;
|
|
1023
|
+
}[];
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Barline information with context
|
|
1027
|
+
*/
|
|
1028
|
+
interface BarlineWithContext {
|
|
1029
|
+
barline: Barline;
|
|
1030
|
+
partIndex: number;
|
|
1031
|
+
measureIndex: number;
|
|
1032
|
+
measureNumber: string;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Repeat structure information
|
|
1036
|
+
*/
|
|
1037
|
+
interface RepeatInfo {
|
|
1038
|
+
type: 'forward' | 'backward';
|
|
1039
|
+
times?: number;
|
|
1040
|
+
measureIndex: number;
|
|
1041
|
+
measureNumber: string;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Ending (volta bracket) information
|
|
1045
|
+
*/
|
|
1046
|
+
interface EndingInfo {
|
|
1047
|
+
number: string;
|
|
1048
|
+
type: 'start' | 'stop' | 'discontinue';
|
|
1049
|
+
partIndex: number;
|
|
1050
|
+
measureIndex: number;
|
|
1051
|
+
measureNumber: string;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Key change information
|
|
1055
|
+
*/
|
|
1056
|
+
interface KeyChangeInfo {
|
|
1057
|
+
key: KeySignature;
|
|
1058
|
+
partIndex: number;
|
|
1059
|
+
measureIndex: number;
|
|
1060
|
+
measureNumber: string;
|
|
1061
|
+
position: number;
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Time signature change information
|
|
1065
|
+
*/
|
|
1066
|
+
interface TimeChangeInfo {
|
|
1067
|
+
time: TimeSignature;
|
|
1068
|
+
partIndex: number;
|
|
1069
|
+
measureIndex: number;
|
|
1070
|
+
measureNumber: string;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Clef change information
|
|
1074
|
+
*/
|
|
1075
|
+
interface ClefChangeInfo {
|
|
1076
|
+
clef: Clef;
|
|
1077
|
+
staff: number;
|
|
1078
|
+
partIndex: number;
|
|
1079
|
+
measureIndex: number;
|
|
1080
|
+
measureNumber: string;
|
|
1081
|
+
position: number;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* All structural changes combined
|
|
1085
|
+
*/
|
|
1086
|
+
interface StructuralChanges {
|
|
1087
|
+
keyChanges: KeyChangeInfo[];
|
|
1088
|
+
timeChanges: TimeChangeInfo[];
|
|
1089
|
+
clefChanges: ClefChangeInfo[];
|
|
1090
|
+
}
|
|
796
1091
|
|
|
797
|
-
export type { Accidental as A, BackupEntry as B, Clef as C, DirectionEntry as D, ForwardEntry as F, KeySignature as K, Lyric as L, Measure as M, NoteEntry as N,
|
|
1092
|
+
export type { TupletGroup as $, Accidental as A, BackupEntry as B, Clef as C, DirectionEntry as D, EntryWithContext as E, ForwardEntry as F, StaffRange as G, PositionQueryOptions as H, VerticalSlice as I, VoiceLine as J, KeySignature as K, Lyric as L, Measure as M, NoteEntry as N, AdjacentNotes as O, Pitch as P, DirectionKind as Q, DynamicWithContext as R, Score as S, TieInfo as T, TempoWithContext as U, VoiceGroup as V, PedalWithContext as W, WedgeWithContext as X, OctaveShiftWithContext as Y, TiedNoteGroup as Z, SlurSpan as _, ScoreMetadata as a, BeamGroup as a0, NotationType as a1, HarmonyWithContext as a2, LyricWithContext as a3, AssembledLyrics as a4, BarlineWithContext as a5, RepeatInfo as a6, EndingInfo as a7, KeyChangeInfo as a8, TimeChangeInfo as a9, ClefChangeInfo as aa, StructuralChanges as ab, HarmonyEntry as ac, PartInfo as b, PartGroup as c, PartListEntry as d, Part as e, MeasureAttributes as f, MeasureEntry as g, NoteType as h, AccidentalInfo as i, BeamInfo as j, Notation as k, DirectionType as l, DynamicsValue as m, TimeSignature as n, Transpose as o, Barline as p, StaffGroup as q, NoteWithPosition as r, Chord as s, NoteIteratorItem as t, Print as u, Defaults as v, Credit as w, VoiceToStaffMap as x, NoteWithContext as y, DirectionWithContext as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musicxml-io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Parse and serialize MusicXML (.xml/.mxl) with high round-trip fidelity",
|
|
5
5
|
"author": "tan-z-tan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,7 +48,11 @@
|
|
|
48
48
|
"typecheck": "tsc --noEmit",
|
|
49
49
|
"prepublishOnly": "npm run build && npm run test",
|
|
50
50
|
"coverage:roundtrip": "tsx scripts/measure-coverage.ts",
|
|
51
|
-
"diff:roundtrip": "tsx scripts/diff-roundtrip.ts"
|
|
51
|
+
"diff:roundtrip": "tsx scripts/diff-roundtrip.ts",
|
|
52
|
+
"release": "bash scripts/release.sh",
|
|
53
|
+
"release:patch": "bash scripts/release.sh patch",
|
|
54
|
+
"release:minor": "bash scripts/release.sh minor",
|
|
55
|
+
"release:major": "bash scripts/release.sh major"
|
|
52
56
|
},
|
|
53
57
|
"keywords": [
|
|
54
58
|
"musicxml",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/accessors/index.ts","../../src/utils/index.ts"],"sourcesContent":["import type {\n Score,\n Measure,\n NoteEntry,\n VoiceGroup,\n StaffGroup,\n NoteWithPosition,\n Chord,\n NoteIteratorItem,\n} from '../types';\nimport { getAbsolutePositionForNote, createPositionState, updatePositionForEntry } from '../utils';\n\n/**\n * Filter options for voice/staff selection\n */\nexport interface VoiceFilter {\n voice?: number;\n staff?: number;\n}\n\n/**\n * Get all notes for a specific voice (and optionally staff)\n */\nexport function getNotesForVoice(measure: Measure, filter: VoiceFilter): NoteEntry[] {\n return measure.entries.filter((entry): entry is NoteEntry => {\n if (entry.type !== 'note') return false;\n if (filter.voice !== undefined && entry.voice !== filter.voice) return false;\n if (filter.staff !== undefined && (entry.staff ?? 1) !== filter.staff) return false;\n return true;\n });\n}\n\n/**\n * Get all notes for a specific staff (regardless of voice)\n */\nexport function getNotesForStaff(measure: Measure, filter: { staff: number }): NoteEntry[] {\n return measure.entries.filter((entry): entry is NoteEntry => {\n if (entry.type !== 'note') return false;\n return (entry.staff ?? 1) === filter.staff;\n });\n}\n\n/**\n * Group notes by voice (and staff)\n */\nexport function groupByVoice(measure: Measure): VoiceGroup[] {\n const groups = new Map<string, VoiceGroup>();\n\n for (const entry of measure.entries) {\n if (entry.type !== 'note') continue;\n\n const staff = entry.staff ?? 1;\n const voice = entry.voice;\n const key = `${staff}-${voice}`;\n\n if (!groups.has(key)) {\n groups.set(key, { staff, voice, notes: [] });\n }\n\n groups.get(key)!.notes.push(entry);\n }\n\n // Sort by staff, then by voice\n return Array.from(groups.values()).sort((a, b) => {\n if (a.staff !== b.staff) return a.staff - b.staff;\n return a.voice - b.voice;\n });\n}\n\n/**\n * Group notes by staff\n */\nexport function groupByStaff(measure: Measure): StaffGroup[] {\n const groups = new Map<number, StaffGroup>();\n\n for (const entry of measure.entries) {\n if (entry.type !== 'note') continue;\n\n const staff = entry.staff ?? 1;\n\n if (!groups.has(staff)) {\n groups.set(staff, { staff, notes: [] });\n }\n\n groups.get(staff)!.notes.push(entry);\n }\n\n return Array.from(groups.values()).sort((a, b) => a.staff - b.staff);\n}\n\n/**\n * Calculate absolute position of a note within a measure\n * Position is in divisions from the start of the measure\n */\nexport function getAbsolutePosition(note: NoteEntry, measure: Measure): number {\n return getAbsolutePositionForNote(note, measure);\n}\n\n/**\n * Add absolute position to all notes in a measure\n */\nexport function withAbsolutePositions(measure: Measure): NoteWithPosition[] {\n const result: NoteWithPosition[] = [];\n const state = createPositionState();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n const notePosition = entry.chord ? state.lastNonChordPosition : state.position;\n result.push({\n ...entry,\n absolutePosition: notePosition,\n });\n }\n updatePositionForEntry(state, entry);\n }\n\n return result;\n}\n\n/**\n * Get chords (groups of simultaneously sounding notes)\n */\nexport function getChords(measure: Measure, filter?: VoiceFilter): Chord[] {\n const notesWithPos = withAbsolutePositions(measure);\n\n // Filter by voice/staff if specified\n const filteredNotes = filter\n ? notesWithPos.filter((note) => {\n if (filter.voice !== undefined && note.voice !== filter.voice) return false;\n if (filter.staff !== undefined && (note.staff ?? 1) !== filter.staff) return false;\n return true;\n })\n : notesWithPos;\n\n // Group by position\n const chordMap = new Map<number, NoteWithPosition[]>();\n\n for (const note of filteredNotes) {\n const pos = note.absolutePosition;\n if (!chordMap.has(pos)) {\n chordMap.set(pos, []);\n }\n chordMap.get(pos)!.push(note);\n }\n\n // Convert to Chord array\n const chords: Chord[] = [];\n\n for (const [position, notes] of chordMap.entries()) {\n // All notes in a chord should have the same duration (using first note's duration)\n const duration = notes[0].duration;\n\n chords.push({\n position,\n duration,\n notes: notes.map(({ absolutePosition, ...note }) => note),\n });\n }\n\n // Sort by position\n return chords.sort((a, b) => a.position - b.position);\n}\n\n/**\n * Iterate over all notes in a score\n */\nexport function* iterateNotes(score: Score): Generator<NoteIteratorItem> {\n for (const part of score.parts) {\n for (const measure of part.measures) {\n const state = createPositionState();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n const notePosition = entry.chord ? state.lastNonChordPosition : state.position;\n yield {\n part,\n measure,\n note: entry,\n position: notePosition,\n };\n }\n updatePositionForEntry(state, entry);\n }\n }\n }\n}\n\n/**\n * Get all notes from a score as an array\n */\nexport function getAllNotes(score: Score): NoteIteratorItem[] {\n return Array.from(iterateNotes(score));\n}\n\n/**\n * Get unique voices used in a measure\n */\nexport function getVoices(measure: Measure): number[] {\n const voices = new Set<number>();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n voices.add(entry.voice);\n }\n }\n\n return Array.from(voices).sort((a, b) => a - b);\n}\n\n/**\n * Get unique staves used in a measure\n */\nexport function getStaves(measure: Measure): number[] {\n const staves = new Set<number>();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n staves.add(entry.staff ?? 1);\n }\n }\n\n return Array.from(staves).sort((a, b) => a - b);\n}\n\n/**\n * Check if a measure contains any notes\n */\nexport function hasNotes(measure: Measure): boolean {\n return measure.entries.some((entry) => entry.type === 'note');\n}\n\n/**\n * Check if a measure is a rest (no pitched notes)\n */\nexport function isRestMeasure(measure: Measure): boolean {\n const notes = measure.entries.filter((entry): entry is NoteEntry => entry.type === 'note');\n return notes.length === 0 || notes.every((note) => !note.pitch);\n}\n\n/**\n * Options for normalized position calculation\n */\nexport interface NormalizedPositionOptions {\n baseDivisions: number;\n currentDivisions?: number;\n}\n\n/**\n * Get a normalized position of a note using a common base divisions\n * This is useful when comparing positions across measures with different divisions\n */\nexport function getNormalizedPosition(\n note: NoteEntry,\n measure: Measure,\n options: NormalizedPositionOptions\n): number {\n const absolutePosition = getAbsolutePosition(note, measure);\n const currentDivisions = options.currentDivisions ?? measure.attributes?.divisions ?? 1;\n\n // Convert from current divisions to base divisions\n return (absolutePosition * options.baseDivisions) / currentDivisions;\n}\n\n/**\n * Get normalized duration of a note using a common base divisions\n */\nexport function getNormalizedDuration(\n note: NoteEntry,\n options: NormalizedPositionOptions\n): number {\n const currentDivisions = options.currentDivisions ?? 1;\n return (note.duration * options.baseDivisions) / currentDivisions;\n}\n","import type { Pitch, Measure, MeasureEntry, NoteEntry } from '../types';\n\n// Pitch constants\nexport const STEPS: Pitch['step'][] = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];\nexport const STEP_SEMITONES: Record<Pitch['step'], number> = {\n 'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11,\n};\n\n/** Convert pitch to semitone value (MIDI-like) */\nexport function pitchToSemitone(pitch: Pitch): number {\n return pitch.octave * 12 + STEP_SEMITONES[pitch.step] + (pitch.alter ?? 0);\n}\n\n// Position tracking for measure iteration\nexport interface PositionState {\n position: number;\n lastNonChordPosition: number;\n}\n\nexport function createPositionState(): PositionState {\n return { position: 0, lastNonChordPosition: 0 };\n}\n\n/** Update position state for entry, returns position before update */\nexport function updatePositionForEntry(state: PositionState, entry: MeasureEntry): number {\n const pos = state.position;\n switch (entry.type) {\n case 'note': {\n const note = entry as NoteEntry;\n if (!note.chord) {\n state.lastNonChordPosition = state.position;\n state.position += note.duration;\n }\n return note.chord ? state.lastNonChordPosition : pos;\n }\n case 'backup':\n state.position -= entry.duration;\n state.lastNonChordPosition = state.position;\n return pos;\n case 'forward':\n state.position += entry.duration;\n state.lastNonChordPosition = state.position;\n return pos;\n default:\n return pos;\n }\n}\n\n/** Get absolute position of a note within a measure */\nexport function getAbsolutePositionForNote(note: NoteEntry, measure: Measure): number {\n const state = createPositionState();\n for (const entry of measure.entries) {\n if (entry === note) return entry.chord ? state.lastNonChordPosition : state.position;\n updatePositionForEntry(state, entry);\n }\n return state.position;\n}\n\n/** Get position at end of measure */\nexport function getMeasureEndPosition(measure: Measure): number {\n const state = createPositionState();\n for (const entry of measure.entries) updatePositionForEntry(state, entry);\n return state.position;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/index.ts","../../src/accessors/index.ts"],"sourcesContent":["import type { Pitch, Measure, MeasureEntry, NoteEntry } from '../types';\n\n// Pitch constants\nexport const STEPS: Pitch['step'][] = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];\nexport const STEP_SEMITONES: Record<Pitch['step'], number> = {\n 'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11,\n};\n\n/** Convert pitch to semitone value (MIDI-like) */\nexport function pitchToSemitone(pitch: Pitch): number {\n return pitch.octave * 12 + STEP_SEMITONES[pitch.step] + (pitch.alter ?? 0);\n}\n\n// Position tracking for measure iteration\nexport interface PositionState {\n position: number;\n lastNonChordPosition: number;\n}\n\nexport function createPositionState(): PositionState {\n return { position: 0, lastNonChordPosition: 0 };\n}\n\n/** Update position state for entry, returns position before update */\nexport function updatePositionForEntry(state: PositionState, entry: MeasureEntry): number {\n const pos = state.position;\n switch (entry.type) {\n case 'note': {\n const note = entry as NoteEntry;\n if (!note.chord) {\n state.lastNonChordPosition = state.position;\n state.position += note.duration;\n }\n return note.chord ? state.lastNonChordPosition : pos;\n }\n case 'backup':\n state.position -= entry.duration;\n state.lastNonChordPosition = state.position;\n return pos;\n case 'forward':\n state.position += entry.duration;\n state.lastNonChordPosition = state.position;\n return pos;\n default:\n return pos;\n }\n}\n\n/** Get absolute position of a note within a measure */\nexport function getAbsolutePositionForNote(note: NoteEntry, measure: Measure): number {\n const state = createPositionState();\n for (const entry of measure.entries) {\n if (entry === note) return entry.chord ? state.lastNonChordPosition : state.position;\n updatePositionForEntry(state, entry);\n }\n return state.position;\n}\n\n/** Get position at end of measure */\nexport function getMeasureEndPosition(measure: Measure): number {\n const state = createPositionState();\n for (const entry of measure.entries) updatePositionForEntry(state, entry);\n return state.position;\n}\n","import type {\n Score,\n Measure,\n NoteEntry,\n VoiceGroup,\n StaffGroup,\n NoteWithPosition,\n Chord,\n NoteIteratorItem,\n} from '../types';\nimport { getAbsolutePositionForNote, createPositionState, updatePositionForEntry } from '../utils';\n\n/**\n * Filter options for voice/staff selection\n */\nexport interface VoiceFilter {\n voice?: number;\n staff?: number;\n}\n\n/**\n * Get all notes for a specific voice (and optionally staff)\n */\nexport function getNotesForVoice(measure: Measure, filter: VoiceFilter): NoteEntry[] {\n return measure.entries.filter((entry): entry is NoteEntry => {\n if (entry.type !== 'note') return false;\n if (filter.voice !== undefined && entry.voice !== filter.voice) return false;\n if (filter.staff !== undefined && (entry.staff ?? 1) !== filter.staff) return false;\n return true;\n });\n}\n\n/**\n * Get all notes for a specific staff (regardless of voice)\n */\nexport function getNotesForStaff(measure: Measure, filter: { staff: number }): NoteEntry[] {\n return measure.entries.filter((entry): entry is NoteEntry => {\n if (entry.type !== 'note') return false;\n return (entry.staff ?? 1) === filter.staff;\n });\n}\n\n/**\n * Group notes by voice (and staff)\n */\nexport function groupByVoice(measure: Measure): VoiceGroup[] {\n const groups = new Map<string, VoiceGroup>();\n\n for (const entry of measure.entries) {\n if (entry.type !== 'note') continue;\n\n const staff = entry.staff ?? 1;\n const voice = entry.voice;\n const key = `${staff}-${voice}`;\n\n if (!groups.has(key)) {\n groups.set(key, { staff, voice, notes: [] });\n }\n\n groups.get(key)!.notes.push(entry);\n }\n\n // Sort by staff, then by voice\n return Array.from(groups.values()).sort((a, b) => {\n if (a.staff !== b.staff) return a.staff - b.staff;\n return a.voice - b.voice;\n });\n}\n\n/**\n * Group notes by staff\n */\nexport function groupByStaff(measure: Measure): StaffGroup[] {\n const groups = new Map<number, StaffGroup>();\n\n for (const entry of measure.entries) {\n if (entry.type !== 'note') continue;\n\n const staff = entry.staff ?? 1;\n\n if (!groups.has(staff)) {\n groups.set(staff, { staff, notes: [] });\n }\n\n groups.get(staff)!.notes.push(entry);\n }\n\n return Array.from(groups.values()).sort((a, b) => a.staff - b.staff);\n}\n\n/**\n * Calculate absolute position of a note within a measure\n * Position is in divisions from the start of the measure\n */\nexport function getAbsolutePosition(note: NoteEntry, measure: Measure): number {\n return getAbsolutePositionForNote(note, measure);\n}\n\n/**\n * Add absolute position to all notes in a measure\n */\nexport function withAbsolutePositions(measure: Measure): NoteWithPosition[] {\n const result: NoteWithPosition[] = [];\n const state = createPositionState();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n const notePosition = entry.chord ? state.lastNonChordPosition : state.position;\n result.push({\n ...entry,\n absolutePosition: notePosition,\n });\n }\n updatePositionForEntry(state, entry);\n }\n\n return result;\n}\n\n/**\n * Get chords (groups of simultaneously sounding notes)\n */\nexport function getChords(measure: Measure, filter?: VoiceFilter): Chord[] {\n const notesWithPos = withAbsolutePositions(measure);\n\n // Filter by voice/staff if specified\n const filteredNotes = filter\n ? notesWithPos.filter((note) => {\n if (filter.voice !== undefined && note.voice !== filter.voice) return false;\n if (filter.staff !== undefined && (note.staff ?? 1) !== filter.staff) return false;\n return true;\n })\n : notesWithPos;\n\n // Group by position\n const chordMap = new Map<number, NoteWithPosition[]>();\n\n for (const note of filteredNotes) {\n const pos = note.absolutePosition;\n if (!chordMap.has(pos)) {\n chordMap.set(pos, []);\n }\n chordMap.get(pos)!.push(note);\n }\n\n // Convert to Chord array\n const chords: Chord[] = [];\n\n for (const [position, notes] of chordMap.entries()) {\n // All notes in a chord should have the same duration (using first note's duration)\n const duration = notes[0].duration;\n\n chords.push({\n position,\n duration,\n notes: notes.map(({ absolutePosition, ...note }) => note),\n });\n }\n\n // Sort by position\n return chords.sort((a, b) => a.position - b.position);\n}\n\n/**\n * Iterate over all notes in a score\n */\nexport function* iterateNotes(score: Score): Generator<NoteIteratorItem> {\n for (const part of score.parts) {\n for (const measure of part.measures) {\n const state = createPositionState();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n const notePosition = entry.chord ? state.lastNonChordPosition : state.position;\n yield {\n part,\n measure,\n note: entry,\n position: notePosition,\n };\n }\n updatePositionForEntry(state, entry);\n }\n }\n }\n}\n\n/**\n * Get all notes from a score as an array\n */\nexport function getAllNotes(score: Score): NoteIteratorItem[] {\n return Array.from(iterateNotes(score));\n}\n\n/**\n * Get unique voices used in a measure\n */\nexport function getVoices(measure: Measure): number[] {\n const voices = new Set<number>();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n voices.add(entry.voice);\n }\n }\n\n return Array.from(voices).sort((a, b) => a - b);\n}\n\n/**\n * Get unique staves used in a measure\n */\nexport function getStaves(measure: Measure): number[] {\n const staves = new Set<number>();\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n staves.add(entry.staff ?? 1);\n }\n }\n\n return Array.from(staves).sort((a, b) => a - b);\n}\n\n/**\n * Check if a measure contains any notes\n */\nexport function hasNotes(measure: Measure): boolean {\n return measure.entries.some((entry) => entry.type === 'note');\n}\n\n/**\n * Check if a measure is a rest (no pitched notes)\n */\nexport function isRestMeasure(measure: Measure): boolean {\n const notes = measure.entries.filter((entry): entry is NoteEntry => entry.type === 'note');\n return notes.length === 0 || notes.every((note) => !note.pitch);\n}\n\n/**\n * Options for normalized position calculation\n */\nexport interface NormalizedPositionOptions {\n baseDivisions: number;\n currentDivisions?: number;\n}\n\n/**\n * Get a normalized position of a note using a common base divisions\n * This is useful when comparing positions across measures with different divisions\n */\nexport function getNormalizedPosition(\n note: NoteEntry,\n measure: Measure,\n options: NormalizedPositionOptions\n): number {\n const absolutePosition = getAbsolutePosition(note, measure);\n const currentDivisions = options.currentDivisions ?? measure.attributes?.divisions ?? 1;\n\n // Convert from current divisions to base divisions\n return (absolutePosition * options.baseDivisions) / currentDivisions;\n}\n\n/**\n * Get normalized duration of a note using a common base divisions\n */\nexport function getNormalizedDuration(\n note: NoteEntry,\n options: NormalizedPositionOptions\n): number {\n const currentDivisions = options.currentDivisions ?? 1;\n return (note.duration * options.baseDivisions) / currentDivisions;\n}\n"],"mappings":"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","names":[]}
|