@sudobility/music_player 0.1.0 → 0.1.2

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.
Files changed (159) hide show
  1. package/CLAUDE.md +335 -0
  2. package/dist/core.d.ts +33 -0
  3. package/dist/core.d.ts.map +1 -0
  4. package/dist/core.js +33 -0
  5. package/dist/core.js.map +1 -0
  6. package/dist/engine.d.ts +103 -0
  7. package/dist/engine.d.ts.map +1 -0
  8. package/dist/engine.js +2 -0
  9. package/dist/engine.js.map +1 -0
  10. package/dist/mocks/index.d.ts +46 -0
  11. package/dist/mocks/index.d.ts.map +1 -0
  12. package/dist/mocks/index.js +93 -0
  13. package/dist/mocks/index.js.map +1 -0
  14. package/dist/player.d.ts +81 -0
  15. package/dist/player.d.ts.map +1 -0
  16. package/dist/player.js +209 -0
  17. package/dist/player.js.map +1 -0
  18. package/dist/rn/audio/offline-render.d.ts +29 -0
  19. package/dist/rn/audio/offline-render.d.ts.map +1 -0
  20. package/dist/rn/audio/offline-render.js +134 -0
  21. package/dist/rn/audio/offline-render.js.map +1 -0
  22. package/dist/rn/index.d.ts +50 -0
  23. package/dist/rn/index.d.ts.map +1 -0
  24. package/dist/rn/index.js +39 -0
  25. package/dist/rn/index.js.map +1 -0
  26. package/dist/rn/playback/audio-api.d.ts +93 -0
  27. package/dist/rn/playback/audio-api.d.ts.map +1 -0
  28. package/dist/rn/playback/audio-api.js +24 -0
  29. package/dist/rn/playback/audio-api.js.map +1 -0
  30. package/dist/rn/playback/expression-table.d.ts +22 -0
  31. package/dist/rn/playback/expression-table.d.ts.map +1 -0
  32. package/dist/rn/playback/expression-table.js +926 -0
  33. package/dist/rn/playback/expression-table.js.map +1 -0
  34. package/dist/rn/playback/expression.d.ts +48 -0
  35. package/dist/rn/playback/expression.d.ts.map +1 -0
  36. package/dist/rn/playback/expression.js +120 -0
  37. package/dist/rn/playback/expression.js.map +1 -0
  38. package/dist/rn/playback/gm-pack-name.d.ts +40 -0
  39. package/dist/rn/playback/gm-pack-name.d.ts.map +1 -0
  40. package/dist/rn/playback/gm-pack-name.js +61 -0
  41. package/dist/rn/playback/gm-pack-name.js.map +1 -0
  42. package/dist/rn/playback/pack-library.d.ts +44 -0
  43. package/dist/rn/playback/pack-library.d.ts.map +1 -0
  44. package/dist/rn/playback/pack-library.js +85 -0
  45. package/dist/rn/playback/pack-library.js.map +1 -0
  46. package/dist/rn/playback/pack-names.json +130 -0
  47. package/dist/rn/playback/sample-engine.d.ts +216 -0
  48. package/dist/rn/playback/sample-engine.d.ts.map +1 -0
  49. package/dist/rn/playback/sample-engine.js +669 -0
  50. package/dist/rn/playback/sample-engine.js.map +1 -0
  51. package/dist/rn/playback/sample-pack.d.ts +71 -0
  52. package/dist/rn/playback/sample-pack.d.ts.map +1 -0
  53. package/dist/rn/playback/sample-pack.js +112 -0
  54. package/dist/rn/playback/sample-pack.js.map +1 -0
  55. package/dist/rn/playback/sustain-loop.d.ts +24 -0
  56. package/dist/rn/playback/sustain-loop.d.ts.map +1 -0
  57. package/dist/rn/playback/sustain-loop.js +111 -0
  58. package/dist/rn/playback/sustain-loop.js.map +1 -0
  59. package/dist/rn/playback/voice-plan.d.ts +60 -0
  60. package/dist/rn/playback/voice-plan.d.ts.map +1 -0
  61. package/dist/rn/playback/voice-plan.js +38 -0
  62. package/dist/rn/playback/voice-plan.js.map +1 -0
  63. package/dist/shared/bus.d.ts +73 -0
  64. package/dist/shared/bus.d.ts.map +1 -0
  65. package/dist/shared/bus.js +83 -0
  66. package/dist/shared/bus.js.map +1 -0
  67. package/dist/shared/midi.d.ts +18 -0
  68. package/dist/shared/midi.d.ts.map +1 -0
  69. package/dist/shared/midi.js +25 -0
  70. package/dist/shared/midi.js.map +1 -0
  71. package/dist/shared/mix.d.ts +75 -0
  72. package/dist/shared/mix.d.ts.map +1 -0
  73. package/dist/shared/mix.js +115 -0
  74. package/dist/shared/mix.js.map +1 -0
  75. package/dist/shared/note-queue.d.ts +43 -0
  76. package/dist/shared/note-queue.d.ts.map +1 -0
  77. package/dist/shared/note-queue.js +54 -0
  78. package/dist/shared/note-queue.js.map +1 -0
  79. package/dist/shared/plan.d.ts +34 -0
  80. package/dist/shared/plan.d.ts.map +1 -0
  81. package/dist/shared/plan.js +134 -0
  82. package/dist/shared/plan.js.map +1 -0
  83. package/dist/shared/pump-window.d.ts +38 -0
  84. package/dist/shared/pump-window.d.ts.map +1 -0
  85. package/dist/shared/pump-window.js +13 -0
  86. package/dist/shared/pump-window.js.map +1 -0
  87. package/dist/shared/render-events.d.ts +25 -0
  88. package/dist/shared/render-events.d.ts.map +1 -0
  89. package/dist/shared/render-events.js +79 -0
  90. package/dist/shared/render-events.js.map +1 -0
  91. package/dist/shared/sounding-set.d.ts +45 -0
  92. package/dist/shared/sounding-set.d.ts.map +1 -0
  93. package/dist/shared/sounding-set.js +81 -0
  94. package/dist/shared/sounding-set.js.map +1 -0
  95. package/dist/shared/test-plan.d.ts +36 -0
  96. package/dist/shared/test-plan.d.ts.map +1 -0
  97. package/dist/shared/test-plan.js +83 -0
  98. package/dist/shared/test-plan.js.map +1 -0
  99. package/dist/singleton.d.ts +23 -0
  100. package/dist/singleton.d.ts.map +1 -0
  101. package/dist/singleton.js +26 -0
  102. package/dist/singleton.js.map +1 -0
  103. package/dist/types.d.ts +84 -0
  104. package/dist/types.d.ts.map +1 -0
  105. package/dist/types.js +2 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/web/audio/offline-synth.d.ts +24 -0
  108. package/dist/web/audio/offline-synth.d.ts.map +1 -0
  109. package/dist/web/audio/offline-synth.js +92 -0
  110. package/dist/web/audio/offline-synth.js.map +1 -0
  111. package/dist/web/audio/soundfont-render.d.ts +31 -0
  112. package/dist/web/audio/soundfont-render.d.ts.map +1 -0
  113. package/dist/web/audio/soundfont-render.js +130 -0
  114. package/dist/web/audio/soundfont-render.js.map +1 -0
  115. package/dist/web/audio/synth-types.d.ts +24 -0
  116. package/dist/web/audio/synth-types.d.ts.map +1 -0
  117. package/dist/web/audio/synth-types.js +2 -0
  118. package/dist/web/audio/synth-types.js.map +1 -0
  119. package/dist/web/index.d.ts +40 -0
  120. package/dist/web/index.d.ts.map +1 -0
  121. package/dist/web/index.js +59 -0
  122. package/dist/web/index.js.map +1 -0
  123. package/dist/web/playback/channel-allocator.d.ts +54 -0
  124. package/dist/web/playback/channel-allocator.d.ts.map +1 -0
  125. package/dist/web/playback/channel-allocator.js +72 -0
  126. package/dist/web/playback/channel-allocator.js.map +1 -0
  127. package/dist/web/playback/click.d.ts +9 -0
  128. package/dist/web/playback/click.d.ts.map +1 -0
  129. package/dist/web/playback/click.js +61 -0
  130. package/dist/web/playback/click.js.map +1 -0
  131. package/dist/web/playback/clock.d.ts +36 -0
  132. package/dist/web/playback/clock.d.ts.map +1 -0
  133. package/dist/web/playback/clock.js +64 -0
  134. package/dist/web/playback/clock.js.map +1 -0
  135. package/dist/web/playback/governor.d.ts +45 -0
  136. package/dist/web/playback/governor.d.ts.map +1 -0
  137. package/dist/web/playback/governor.js +33 -0
  138. package/dist/web/playback/governor.js.map +1 -0
  139. package/dist/web/playback/quiet-stub-notices.d.ts +55 -0
  140. package/dist/web/playback/quiet-stub-notices.d.ts.map +1 -0
  141. package/dist/web/playback/quiet-stub-notices.js +93 -0
  142. package/dist/web/playback/quiet-stub-notices.js.map +1 -0
  143. package/dist/web/playback/soundfont-engine.d.ts +281 -0
  144. package/dist/web/playback/soundfont-engine.d.ts.map +1 -0
  145. package/dist/web/playback/soundfont-engine.js +735 -0
  146. package/dist/web/playback/soundfont-engine.js.map +1 -0
  147. package/dist/web/playback/soundfont-loader.d.ts +30 -0
  148. package/dist/web/playback/soundfont-loader.d.ts.map +1 -0
  149. package/dist/web/playback/soundfont-loader.js +68 -0
  150. package/dist/web/playback/soundfont-loader.js.map +1 -0
  151. package/dist/web/playback/synth-host.d.ts +142 -0
  152. package/dist/web/playback/synth-host.d.ts.map +1 -0
  153. package/dist/web/playback/synth-host.js +293 -0
  154. package/dist/web/playback/synth-host.js.map +1 -0
  155. package/package.json +18 -3
  156. package/dist/index.d.ts +0 -14
  157. package/dist/index.d.ts.map +0 -1
  158. package/dist/index.js +0 -2
  159. package/dist/index.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ import { PlaybackBus } from '../shared/bus.js';
2
+ export class MockMusicPlayer {
3
+ calls = [];
4
+ /** The score it was last handed, for tests to assert on. */
5
+ loadedScore = null;
6
+ position = new Set();
7
+ soundingListeners = new Set();
8
+ transport = new Set();
9
+ loadState = new Set();
10
+ /** A real bus, so a consumer's tests can publish into it exactly as the engine would. */
11
+ bus = new PlaybackBus();
12
+ async load(score, opts = {}) {
13
+ this.loadedScore = score;
14
+ this.calls.push(opts.visibleTrackIds
15
+ ? `load(visible:${opts.visibleTrackIds.length})`
16
+ : 'load');
17
+ }
18
+ async play() {
19
+ this.calls.push('play');
20
+ }
21
+ pause() {
22
+ this.calls.push('pause');
23
+ }
24
+ stop() {
25
+ this.calls.push('stop');
26
+ }
27
+ seek(tick) {
28
+ this.calls.push(`seek(${tick})`);
29
+ }
30
+ setLoop(range) {
31
+ this.calls.push(range ? `setLoop(${range.startTick},${range.endTick})` : 'clearLoop');
32
+ }
33
+ setTempoMultiplier(m) {
34
+ this.calls.push(`setTempoMultiplier(${m})`);
35
+ }
36
+ setMetronome(on) {
37
+ this.calls.push(`setMetronome(${on})`);
38
+ }
39
+ setMasterVolume(v) {
40
+ this.calls.push(`setMasterVolume(${v})`);
41
+ }
42
+ applyMix(tracks) {
43
+ this.calls.push(`applyMix(${tracks.length})`);
44
+ }
45
+ setVisibleTracks(ids) {
46
+ this.calls.push(`setVisibleTracks(${ids.length})`);
47
+ }
48
+ noteOn(midi, program, isPercussion = false) {
49
+ this.calls.push(`noteOn(${midi},${program},${isPercussion})`);
50
+ }
51
+ noteOff(midi) {
52
+ this.calls.push(`noteOff(${midi})`);
53
+ }
54
+ onPosition(fn) {
55
+ this.position.add(fn);
56
+ return () => this.position.delete(fn);
57
+ }
58
+ onSounding(fn) {
59
+ this.soundingListeners.add(fn);
60
+ return () => this.soundingListeners.delete(fn);
61
+ }
62
+ onTransport(fn) {
63
+ this.transport.add(fn);
64
+ return () => this.transport.delete(fn);
65
+ }
66
+ onLoadState(fn) {
67
+ this.loadState.add(fn);
68
+ return () => this.loadState.delete(fn);
69
+ }
70
+ dispose() {
71
+ this.calls.push('dispose');
72
+ }
73
+ // ---- test drivers: let a suite push events the way a real engine would ----
74
+ emitPosition(tick) {
75
+ this.bus.publishPosition(tick);
76
+ for (const fn of this.position)
77
+ fn(tick);
78
+ }
79
+ emitSounding(notes) {
80
+ this.bus.publishSounding(notes);
81
+ for (const fn of this.soundingListeners)
82
+ fn(notes);
83
+ }
84
+ emitTransport(state) {
85
+ for (const fn of this.transport)
86
+ fn(state);
87
+ }
88
+ emitLoadState(state) {
89
+ for (const fn of this.loadState)
90
+ fn(state);
91
+ }
92
+ }
93
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mocks/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import type { PlaybackLoadState, PlaybackTrack, Score, ScoreRange, SoundingNote, TransportPlaybackState } from '@sudobility/music_types';
2
+ import type { PlaybackEngine } from './engine.js';
3
+ import { PlaybackBus } from './shared/bus.js';
4
+ import type { IMusicPlayer, Unsubscribe } from './types.js';
5
+ export declare class MusicPlayer implements IMusicPlayer {
6
+ private readonly engine;
7
+ /**
8
+ * Where position and sounding notes go instead of the host's store.
9
+ *
10
+ * Both arrive far too often to route through React state — see `bus.ts`.
11
+ */
12
+ readonly bus: PlaybackBus;
13
+ /**
14
+ * How the loaded plan's performance time maps onto the written score.
15
+ *
16
+ * Replaced whenever the plan is — a repeat added or removed changes it. The
17
+ * identity default means a player with nothing loaded translates nothing.
18
+ */
19
+ private timeline;
20
+ /** The loaded plan's tempo conversion, so the playhead can be told how fast ticks pass. */
21
+ private tempo;
22
+ /** The transport's half/double-speed control, kept here since the store no longer holds it for us. */
23
+ private multiplier;
24
+ /** Mirrors the engine, so `load` can tell a mix change from a reload without asking the host. */
25
+ private state;
26
+ private score;
27
+ private visibleTrackIds;
28
+ private readonly loadStateListeners;
29
+ constructor(engine: PlaybackEngine);
30
+ load(score: Score, opts?: {
31
+ visibleTrackIds?: readonly string[];
32
+ }): Promise<void>;
33
+ play(): Promise<void>;
34
+ pause(): void;
35
+ stop(): void;
36
+ /**
37
+ * Seeks to a *written* position.
38
+ *
39
+ * The engine is given the first performance position of that tick, which is
40
+ * what "play from here" means to somebody reading the page — the first time
41
+ * through, not the repeat. The caret is the host's to move.
42
+ */
43
+ seek(scoreTick: number): void;
44
+ setLoop(range: ScoreRange | null): void;
45
+ /** The whole score, for a host that wants a loop but has no selection to derive one from. */
46
+ wholeScoreRange(): ScoreRange | null;
47
+ setTempoMultiplier(multiplier: number): void;
48
+ setMetronome(enabled: boolean): void;
49
+ setMasterVolume(volume: number): void;
50
+ applyMix(tracks: readonly PlaybackTrack[]): void;
51
+ setVisibleTracks(visibleTrackIds: readonly string[]): void;
52
+ noteOn(midi: number, program: number, isPercussion?: boolean): void;
53
+ noteOff(midi: number): void;
54
+ onPosition(fn: (scoreTick: number) => void): Unsubscribe;
55
+ onSounding(fn: (notes: readonly SoundingNote[]) => void): Unsubscribe;
56
+ onTransport(fn: (state: TransportPlaybackState) => void): Unsubscribe;
57
+ onLoadState(fn: (state: PlaybackLoadState) => void): Unsubscribe;
58
+ dispose(): void;
59
+ /**
60
+ * How many score ticks pass per real second at the current position.
61
+ *
62
+ * Read off the plan's own tempo conversion — the same one the engine
63
+ * schedules against, so the playhead's smoothing and the audio agree about
64
+ * how fast the music is going. The multiplier is the transport's half/double
65
+ * speed control: the engine divides logical seconds by it, so one real second
66
+ * is that many logical seconds of music.
67
+ */
68
+ private ticksPerSecond;
69
+ /**
70
+ * A track sounds only if it is visible *and* not muted.
71
+ *
72
+ * Visibility used to reach the notation and the print sheet but never the
73
+ * audio, so hiding a track removed it from view while it went on playing.
74
+ * Combining the two axes here rather than writing visibility into the
75
+ * channel's mute means an explicit mute survives being hidden and shown
76
+ * again — the score keeps the user's intent, and this only ever derives from
77
+ * it.
78
+ */
79
+ private applyTrackAudibility;
80
+ }
81
+ //# sourceMappingURL=player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,aAAa,EACb,KAAK,EACL,UAAU,EACV,YAAY,EACZ,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,cAAc,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D,qBAAa,WAAY,YAAW,YAAY;IAgClC,OAAO,CAAC,QAAQ,CAAC,MAAM;IA/BnC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,cAAqB;IAEjC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAA2D;IAE3E,2FAA2F;IAC3F,OAAO,CAAC,KAAK,CAAsC;IAEnD,sGAAsG;IACtG,OAAO,CAAC,UAAU,CAAK;IAEvB,iGAAiG;IACjG,OAAO,CAAC,KAAK,CAAqC;IAElD,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAE/B;gBAEyB,MAAM,EAAE,cAAc;IAsC7C,IAAI,CACR,KAAK,EAAE,KAAK,EACZ,IAAI,GAAE;QAAE,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAO,GACjD,OAAO,CAAC,IAAI,CAAC;IA4BV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI;IAIZ;;;;;;OAMG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAK7B,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI;IAIvC,6FAA6F;IAC7F,eAAe,IAAI,UAAU,GAAG,IAAI;IAKpC,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAK5C,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIpC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIrC,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI;IAIhD,gBAAgB,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAK1D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,IAAI;IAMjE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3B,UAAU,CAAC,EAAE,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW;IAIxD,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,KAAK,IAAI,GAAG,WAAW;IAIrE,WAAW,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,GAAG,WAAW;IAIrE,WAAW,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,WAAW;IAKhE,OAAO,IAAI,IAAI;IAKf;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;CAS7B"}
package/dist/player.js ADDED
@@ -0,0 +1,209 @@
1
+ /**
2
+ * The transport: everything `PlaybackController` did that was not the store.
3
+ *
4
+ * It came from music_lib, where it read Zustand directly for the score, the
5
+ * visible tracks, the selection and the caret. Those four are editing state and
6
+ * stayed behind; what arrives here is score maths and engine driving. The host
7
+ * binds the two together — see music_lib's playback adapter.
8
+ */
9
+ import { scoreEndTick, sourceTickFor, performanceTickFor, } from '@sudobility/music_types';
10
+ import { getMusicPositionSource } from '@sudobility/music_types';
11
+ import { PlaybackBus } from './shared/bus.js';
12
+ import { playbackPlan, playbackTracks, resolveVoice } from './shared/plan.js';
13
+ export class MusicPlayer {
14
+ engine;
15
+ /**
16
+ * Where position and sounding notes go instead of the host's store.
17
+ *
18
+ * Both arrive far too often to route through React state — see `bus.ts`.
19
+ */
20
+ bus = new PlaybackBus();
21
+ /**
22
+ * How the loaded plan's performance time maps onto the written score.
23
+ *
24
+ * Replaced whenever the plan is — a repeat added or removed changes it. The
25
+ * identity default means a player with nothing loaded translates nothing.
26
+ */
27
+ timeline = { segments: [], durationTicks: 0 };
28
+ /** The loaded plan's tempo conversion, so the playhead can be told how fast ticks pass. */
29
+ tempo = null;
30
+ /** The transport's half/double-speed control, kept here since the store no longer holds it for us. */
31
+ multiplier = 1;
32
+ /** Mirrors the engine, so `load` can tell a mix change from a reload without asking the host. */
33
+ state = 'stopped';
34
+ score = null;
35
+ visibleTrackIds = null;
36
+ loadStateListeners = new Set();
37
+ constructor(engine) {
38
+ this.engine = engine;
39
+ engine.setObserver({
40
+ /*
41
+ Translated here, and only here.
42
+
43
+ The engine reports *performance* time, which differs from written time
44
+ the moment a repeat exists. Converting at this one boundary means the
45
+ caret, the following-scroll, the bar/beat readout and the position
46
+ scrubber all keep receiving a score tick and none of them has to know
47
+ repeats exist. Without repeats the timeline is the identity and this
48
+ costs a single comparison.
49
+ */
50
+ onPositionTick: tick => {
51
+ const scoreTick = sourceTickFor(this.timeline, tick);
52
+ // Refreshed every report rather than once at play, so a fermata or a
53
+ // tempo change mid-piece projects at the speed actually in force.
54
+ getMusicPositionSource().setRate(this.ticksPerSecond(scoreTick));
55
+ this.bus.publishPosition(scoreTick);
56
+ },
57
+ onActiveNotes: notes => this.bus.publishSounding(notes),
58
+ onStateChange: state => {
59
+ this.state = state;
60
+ // Smoothing only applies while the transport advances, and the playhead
61
+ // re-anchors on the transition so a pause does not leave it projecting
62
+ // forward across the time spent stopped.
63
+ getMusicPositionSource().setPlaying(state === 'playing', this.ticksPerSecond(this.bus.positionTick));
64
+ this.bus.publishTransport(state);
65
+ },
66
+ // Optional on the contract: an engine with nothing to load never calls it.
67
+ onLoadStateChange: state => {
68
+ for (const listener of this.loadStateListeners)
69
+ listener(state);
70
+ },
71
+ });
72
+ }
73
+ async load(score, opts = {}) {
74
+ this.score = score;
75
+ if (opts.visibleTrackIds !== undefined) {
76
+ this.visibleTrackIds = opts.visibleTrackIds;
77
+ }
78
+ if (this.state === 'playing') {
79
+ // Tracks only: rebuilding every note to change a gain would undo the
80
+ // reason this branch exists. The host's edit lock is what makes it safe —
81
+ // while playing, the only score change that can reach here is a mix
82
+ // change, and anything arriving from outside that path must `stop()`
83
+ // first or it will be heard as the old score.
84
+ this.engine.applyMix(playbackTracks(score));
85
+ return;
86
+ }
87
+ const plan = playbackPlan(score);
88
+ // Kept in step with what the engine holds: a repeat added or removed
89
+ // changes the mapping, and a stale one would put the caret in the wrong
90
+ // bar for every frame until the next load.
91
+ this.timeline = plan.timeline;
92
+ this.tempo = plan.tempo;
93
+ await this.engine.load(plan);
94
+ // `load` reseeds every channel's mute from `Track.muted`, so hidden tracks
95
+ // would sound again without this.
96
+ this.applyTrackAudibility();
97
+ }
98
+ async play() {
99
+ await this.engine.play();
100
+ }
101
+ pause() {
102
+ this.engine.pause();
103
+ }
104
+ stop() {
105
+ this.engine.stop();
106
+ }
107
+ /**
108
+ * Seeks to a *written* position.
109
+ *
110
+ * The engine is given the first performance position of that tick, which is
111
+ * what "play from here" means to somebody reading the page — the first time
112
+ * through, not the repeat. The caret is the host's to move.
113
+ */
114
+ seek(scoreTick) {
115
+ if (!this.score)
116
+ return;
117
+ this.engine.seek(performanceTickFor(this.timeline, Math.max(0, scoreTick)));
118
+ }
119
+ setLoop(range) {
120
+ this.engine.setLoop(range);
121
+ }
122
+ /** The whole score, for a host that wants a loop but has no selection to derive one from. */
123
+ wholeScoreRange() {
124
+ if (!this.score)
125
+ return null;
126
+ return { startTick: 0, endTick: scoreEndTick(this.score), trackIds: [] };
127
+ }
128
+ setTempoMultiplier(multiplier) {
129
+ this.multiplier = multiplier;
130
+ this.engine.setTempoMultiplier(multiplier);
131
+ }
132
+ setMetronome(enabled) {
133
+ this.engine.setMetronome(enabled);
134
+ }
135
+ setMasterVolume(volume) {
136
+ this.engine.setMasterVolume(volume);
137
+ }
138
+ applyMix(tracks) {
139
+ this.engine.applyMix([...tracks]);
140
+ }
141
+ setVisibleTracks(visibleTrackIds) {
142
+ this.visibleTrackIds = visibleTrackIds;
143
+ this.applyTrackAudibility();
144
+ }
145
+ noteOn(midi, program, isPercussion = false) {
146
+ // Resolved here: the engine has no GM tables, and a percussion program
147
+ // addresses a kit rather than an instrument.
148
+ this.engine.noteOn(midi, resolveVoice(program, isPercussion));
149
+ }
150
+ noteOff(midi) {
151
+ this.engine.noteOff(midi);
152
+ }
153
+ onPosition(fn) {
154
+ return this.bus.onPosition(fn);
155
+ }
156
+ onSounding(fn) {
157
+ return this.bus.onSounding(fn);
158
+ }
159
+ onTransport(fn) {
160
+ return this.bus.onTransport(fn);
161
+ }
162
+ onLoadState(fn) {
163
+ this.loadStateListeners.add(fn);
164
+ return () => this.loadStateListeners.delete(fn);
165
+ }
166
+ dispose() {
167
+ this.loadStateListeners.clear();
168
+ this.engine.dispose();
169
+ }
170
+ /**
171
+ * How many score ticks pass per real second at the current position.
172
+ *
173
+ * Read off the plan's own tempo conversion — the same one the engine
174
+ * schedules against, so the playhead's smoothing and the audio agree about
175
+ * how fast the music is going. The multiplier is the transport's half/double
176
+ * speed control: the engine divides logical seconds by it, so one real second
177
+ * is that many logical seconds of music.
178
+ */
179
+ ticksPerSecond(tick) {
180
+ if (!this.tempo)
181
+ return 0;
182
+ // A second of music, measured at the playhead: how many ticks fit between
183
+ // now and one second from now, which handles a tempo change mid-piece
184
+ // without needing to know the tempo map's shape.
185
+ const perSecond = this.tempo.secondsToTicks(this.tempo.ticksToSeconds(tick) + 1) - tick;
186
+ return Math.max(0, perSecond * this.multiplier);
187
+ }
188
+ /**
189
+ * A track sounds only if it is visible *and* not muted.
190
+ *
191
+ * Visibility used to reach the notation and the print sheet but never the
192
+ * audio, so hiding a track removed it from view while it went on playing.
193
+ * Combining the two axes here rather than writing visibility into the
194
+ * channel's mute means an explicit mute survives being hidden and shown
195
+ * again — the score keeps the user's intent, and this only ever derives from
196
+ * it.
197
+ */
198
+ applyTrackAudibility() {
199
+ const score = this.score;
200
+ if (!score)
201
+ return;
202
+ const visible = this.visibleTrackIds;
203
+ for (const track of score.tracks) {
204
+ const hidden = visible !== null && !visible.includes(track.id);
205
+ this.engine.setTrackMute(track.id, track.muted || hidden);
206
+ }
207
+ }
208
+ }
209
+ //# sourceMappingURL=player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player.js","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Offline audio rendering on React Native.
3
+ *
4
+ * Export goes through the same samples as playback, for the same reason the web
5
+ * export goes through the same soundfont: the file is supposed to be a
6
+ * recording of what was heard, and two voicing paths drift apart the first time
7
+ * one is tuned.
8
+ *
9
+ * The engine could not simply be reused, because it schedules against a *live*
10
+ * context's clock and pumps notes 200ms at a time — rendering an export that
11
+ * way would take as long as the piece. `RenderPlan` is already fully resolved
12
+ * (seconds, not ticks; mute and solo applied by `renderEvents` in music_lib),
13
+ * so everything can be scheduled up front against an `OfflineAudioContext` and
14
+ * rendered as fast as the device manages. What is shared is the part that
15
+ * matters: `PackLibrary` picks the sample, and `planVoice` decides the gain and
16
+ * the release.
17
+ */
18
+ import type { AudioRenderer, RenderTrack } from '@sudobility/music_types';
19
+ import type { AudioApi } from '../playback/audio-api.js';
20
+ export type OfflineRendererDeps = {
21
+ loadAudioApi?: () => Promise<AudioApi>;
22
+ fetchPack?: (url: string) => Promise<string>;
23
+ packBase?: string;
24
+ percussionBase?: string;
25
+ };
26
+ /** The pack a track's notes come from — the same rule live playback uses. */
27
+ export declare function packNameForRenderTrack(track: RenderTrack): string | null;
28
+ export declare function createRNSoundfontRenderer(deps?: OfflineRendererDeps): AudioRenderer;
29
+ //# sourceMappingURL=offline-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline-render.d.ts","sourceRoot":"","sources":["../../../src/rn/audio/offline-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,aAAa,EAA4B,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAQpG,OAAO,KAAK,EAAE,QAAQ,EAAwC,MAAM,0BAA0B,CAAC;AAM/F,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAMxE;AASD,wBAAgB,yBAAyB,CAAC,IAAI,GAAE,mBAAwB,GAAG,aAAa,CA0EvF"}
@@ -0,0 +1,134 @@
1
+ import { headroomTrimFor } from '../../shared/mix.js';
2
+ import { gmPackName, percussionPackName } from '../playback/gm-pack-name.js';
3
+ import { PackLibrary } from '../playback/pack-library.js';
4
+ import { RELEASE_SECONDS, planVoice } from '../playback/voice-plan.js';
5
+ import { sustains } from '../playback/expression.js';
6
+ import { applySustainLoop } from '../playback/sustain-loop.js';
7
+ import { loadAudioApi } from '../playback/audio-api.js';
8
+ const SAMPLE_RATE = 44100;
9
+ /** Stereo, so panning survives into the file. */
10
+ const CHANNELS = 2;
11
+ /** The pack a track's notes come from — the same rule live playback uses. */
12
+ export function packNameForRenderTrack(track) {
13
+ // The kit-versus-instrument distinction is already resolved into
14
+ // `voiceProgram`/`voiceName` by music_lib, which owns the GM tables.
15
+ return track.isPercussion
16
+ ? percussionPackName(track.voiceProgram)
17
+ : gmPackName(track.voiceProgram, track.voiceName);
18
+ }
19
+ function defaultFetchPack(url) {
20
+ return fetch(url).then((r) => {
21
+ if (!r.ok)
22
+ throw new Error(`Sample pack ${url} responded ${r.status}`);
23
+ return r.text();
24
+ });
25
+ }
26
+ export function createRNSoundfontRenderer(deps = {}) {
27
+ return {
28
+ async render(plan) {
29
+ const api = await (deps.loadAudioApi ?? loadAudioApi)();
30
+ if (!api.OfflineAudioContext) {
31
+ throw new Error('react-native-audio-api is missing OfflineAudioContext; export needs >=0.13.');
32
+ }
33
+ const library = new PackLibrary({
34
+ fetchPack: deps.fetchPack ?? defaultFetchPack,
35
+ decodeAudioData: (bytes) => api.decodeAudioData(bytes),
36
+ packBase: deps.packBase,
37
+ percussionBase: deps.percussionBase,
38
+ });
39
+ const packByTrack = new Map();
40
+ for (const track of plan.tracks) {
41
+ const name = packNameForRenderTrack(track);
42
+ if (name)
43
+ packByTrack.set(track.id, name);
44
+ }
45
+ // Only the packs notes are actually played from: a plan lists every track
46
+ // so the headroom is right, including silent ones, and downloading a pack
47
+ // for a track that sounds nothing would be minutes of nothing.
48
+ const sounding = new Set([...plan.events].map((e) => packByTrack.get(e.trackId)).filter(Boolean));
49
+ for (const name of sounding)
50
+ await library.ensure(name);
51
+ // Headroom is sized by how many tracks *exist*, not how many sound — the
52
+ // same rule live playback applies, or an export of a muted-heavy score
53
+ // comes out a couple of dB louder than what was heard.
54
+ const headroom = headroomTrimFor(plan.tracks.length);
55
+ // The tail of the last note has to fit inside the file.
56
+ const seconds = plan.durationSec + RELEASE_SECONDS;
57
+ const ctx = new api.OfflineAudioContext({
58
+ numberOfChannels: CHANNELS,
59
+ length: Math.ceil(seconds * SAMPLE_RATE),
60
+ sampleRate: SAMPLE_RATE,
61
+ });
62
+ const master = ctx.createGain();
63
+ master.gain.value = headroom;
64
+ master.connect(ctx.destination);
65
+ const trackById = new Map(plan.tracks.map((t) => [t.id, t]));
66
+ for (const event of plan.events) {
67
+ const packName = packByTrack.get(event.trackId);
68
+ const track = trackById.get(event.trackId);
69
+ if (!packName || !track)
70
+ continue;
71
+ const voicing = library.voice(packName, event.midi, track.isPercussion);
72
+ if (!voicing)
73
+ continue;
74
+ // `velocity` arrives 0..1 here but `planVoice` takes a 0..127 MIDI
75
+ // value, so it is scaled back up rather than passed through — getting
76
+ // this backwards renders a silent file, which is why it has a test.
77
+ const voice = planVoice({
78
+ atSeconds: event.startSec,
79
+ durationSeconds: event.durationSec,
80
+ velocity: event.velocity * 127,
81
+ trackGain: track.volume,
82
+ choice: voicing.choice,
83
+ // Same expression as playback — velocity curve, per-instrument
84
+ // release, velocity-to-brightness — so the file is a recording of
85
+ // what was heard. Percussion opts out for the same reason it does live.
86
+ program: track.isPercussion ? undefined : track.midiProgram,
87
+ });
88
+ const mayLoop = !track.isPercussion && sustains(track.midiProgram);
89
+ buildOfflineVoice(ctx, master, voicing.buffer, voice, track.pan, mayLoop);
90
+ }
91
+ const rendered = await ctx.startRendering();
92
+ return { samples: toMono(rendered), sampleRate: rendered.sampleRate };
93
+ },
94
+ };
95
+ }
96
+ function buildOfflineVoice(ctx, master, buffer, voice, pan, mayLoop) {
97
+ const source = ctx.createBufferSource();
98
+ source.buffer = buffer;
99
+ if (voice.detuneCents !== 0)
100
+ source.detune.value = voice.detuneCents;
101
+ if (mayLoop)
102
+ applySustainLoop(source, buffer, voice.releaseAt - voice.startAt, voice.sampleMidi);
103
+ const amp = ctx.createGain();
104
+ amp.gain.setValueAtTime(voice.gain, voice.startAt);
105
+ amp.gain.setValueAtTime(voice.gain, voice.releaseAt);
106
+ amp.gain.linearRampToValueAtTime(0, voice.endAt);
107
+ const panner = ctx.createStereoPanner();
108
+ panner.pan.value = Math.max(-1, Math.min(1, pan));
109
+ if (voice.cutoffHz === null) {
110
+ source.connect(amp);
111
+ }
112
+ else {
113
+ const filter = ctx.createBiquadFilter();
114
+ filter.type = 'lowpass';
115
+ filter.frequency.value = voice.cutoffHz;
116
+ source.connect(filter);
117
+ filter.connect(amp);
118
+ }
119
+ amp.connect(panner);
120
+ panner.connect(master);
121
+ source.start(voice.startAt);
122
+ source.stop(voice.endAt);
123
+ }
124
+ /** The `DecodedAudio` contract is mono; the render is stereo so panning is real. */
125
+ function toMono(buffer) {
126
+ const out = new Float32Array(buffer.length);
127
+ for (let c = 0; c < buffer.numberOfChannels; c += 1) {
128
+ const data = buffer.getChannelData(c);
129
+ for (let i = 0; i < data.length; i += 1)
130
+ out[i] += data[i] / buffer.numberOfChannels;
131
+ }
132
+ return out;
133
+ }
134
+ //# sourceMappingURL=offline-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline-render.js","sourceRoot":"","sources":["../../../src/rn/audio/offline-render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The React Native player: a sample-based FluidR3 engine.
3
+ *
4
+ * Playback is samples rather than the web's libfluidsynth-in-an-AudioWorklet —
5
+ * React Native has neither WebAssembly nor `addModule`, so that engine cannot
6
+ * be ported, only replaced. It is replaced with the *same font*, pre-rendered
7
+ * per note, so a phone plays the recordings a browser plays. See
8
+ * `playback/sample-engine.ts`.
9
+ */
10
+ import { RNSamplePlaybackEngine } from './playback/sample-engine.js';
11
+ import { createRNSoundfontRenderer } from './audio/offline-render.js';
12
+ import { MusicPlayer } from '../player.js';
13
+ export * from '../types.js';
14
+ export * from '../singleton.js';
15
+ export { MusicPlayer, RNSamplePlaybackEngine, createRNSoundfontRenderer };
16
+ export { renderEvents } from '../shared/render-events.js';
17
+ export { playbackPlan, playbackTracks, resolveVoice } from '../shared/plan.js';
18
+ /**
19
+ * `expression-table` is deliberately *not* re-exported here: the app that owns
20
+ * the soundfont needs `MEASURED_FROM_SHA256` to check the two still agree, and
21
+ * reaching it through this entry drags in the RN optional peers, which a web
22
+ * app does not install. It has its own dependency-free subpath,
23
+ * `@sudobility/music_player/rn/expression-table`.
24
+ */
25
+ export type RNMusicPlayerOptions = {
26
+ /**
27
+ * Where the instrument packs are served from. An app that would rather not
28
+ * depend on a third party at runtime can copy them and point this at its own
29
+ * CDN — they are CC-BY 3.0, so that is allowed, and it is the recommended
30
+ * setup for anything shipping to a store.
31
+ */
32
+ packBase?: string;
33
+ /**
34
+ * Where the drum-kit packs are served from. **Required for percussion**;
35
+ * there is no default because nobody publishes usably-licensed GM percussion.
36
+ */
37
+ percussionBase?: string;
38
+ };
39
+ /** The React Native player, built fresh. Hand it to `initializeMusicPlayer`. */
40
+ export declare function createMusicPlayer(options?: RNMusicPlayerOptions): MusicPlayer;
41
+ /**
42
+ * Renders a plan to PCM, faster than realtime.
43
+ *
44
+ * Scheduled up front against an `OfflineAudioContext` rather than in real time.
45
+ * Sharing `PackLibrary` and `planVoice` with the engine is what keeps the file
46
+ * a recording of what was heard. The caller encodes and saves; this package
47
+ * does not touch files.
48
+ */
49
+ export declare function renderSamples(options?: RNMusicPlayerOptions): (plan: import("@sudobility/music_types").RenderPlan) => Promise<import("@sudobility/music_types").DecodedAudio>;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rn/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/E;;;;;;GAMG;AAEH,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,oBAAyB,GACjC,WAAW,CAOb;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,mHAK/D"}