react-native-audiosprites 0.2.1 → 0.3.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 CHANGED
@@ -22,10 +22,22 @@ First, you need to generate an audio sprite and a JSON manifest file using the `
22
22
  Assuming you have [`audiosprite`](https://www.npmjs.com/package/audiosprite) installed globally:
23
23
 
24
24
  ```sh
25
- audiosprite --output audiosprite --format howler --path ./src/__tests__/ Sound_1.m4a Sound_2.m4a Sound_3.m4a Sound_4.m4a
25
+ audiosprite --output src/__tests__/sounds/mygameaudio --format howler --loop "bg_loop" src/__tests__/sounds/bg_loop.wav src/__tests__/sounds/Sound_1.m4a src/__tests__/sounds/Sound_2.m4a src/__tests__/sounds/Sound_3.m4a src/__tests__/sounds/Sound_4.m4a
26
26
  ```
27
27
 
28
- This command will generate `audiosprite.json`, `audiosprite.mp3`, `audiosprite.ogg`, `audiosprite.m4a`, and `audiosprite.ac3` in the `src/__tests__/` directory. The `--path` argument is important as it tells the player where to find the audio files relative to the JSON manifest.
28
+ This command will generate `mygameaudio.json`, `mygameaudio.mp3`, `mygameaudio.ogg`, `mygameaudio.m4a`, and `mygameaudio.ac3` in the `src/__tests__/sounds/` directory.
29
+
30
+ ### Looping Sounds
31
+
32
+ You can create looping sounds by using the `--loop` option with the `audiosprite` command. The value of the `--loop` option should be the name of the sound you want to loop.
33
+
34
+ For example, to loop the `bg_music` sound, you would use the following command:
35
+
36
+ ```sh
37
+ audiosprite --output audiosprite --format howler --loop "bg_music" --path ./src/__tests__/ Sound_1.m4a Sound_2.m4a Sound_3.m4a Sound_4.m4a bg_music.wav
38
+ ```
39
+
40
+ When you play a looping sound, it will play continuously until you stop it using the `player.stop()` method. The looping functionality is supported on both web and mobile platforms.
29
41
 
30
42
  Then, you can use the `AudioSpritePlayer` to play the sounds from the sprite.
31
43
 
@@ -42,7 +54,7 @@ async function playSound(soundName: string) {
42
54
  try {
43
55
  // Load the audio sprite manifest and audio files
44
56
  // Adjust the path to your audiosprite.json file
45
- await player.load('./src/__tests__/audiosprite.json');
57
+ await player.load('./src/__tests__/sounds/mygameaudio.json');
46
58
  console.log('Audio sprite loaded successfully.');
47
59
 
48
60
  // Play a sound from the spritemap
@@ -53,9 +65,16 @@ async function playSound(soundName: string) {
53
65
  }
54
66
  }
55
67
 
68
+ function stopSound() {
69
+ player.stop();
70
+ console.log('Stopped looping sound.');
71
+ }
72
+
56
73
  // Example usage:
57
74
  playSound('Sound_1');
58
75
  // playSound('Sound_2');
76
+ // To stop a looping sound:
77
+ // stopSound();
59
78
  ```
60
79
 
61
80
  ### React Native Environment
@@ -79,16 +98,16 @@ module.exports = wrapWithAudioAPIMetroConfig(config);
79
98
  Then, you can use it in your component:
80
99
 
81
100
  ```typescript
82
- import { StyleSheet, View, Text, Button, Platform } from 'react-native';
101
+ import { StyleSheet, View, Text, Platform, TouchableOpacity } from 'react-native';
83
102
  import { AudioSpritePlayer } from 'react-native-audiosprites';
84
103
  import { AudioManager, AudioContext } from 'react-native-audio-api';
85
104
  import { useEffect, useState, useRef } from 'react';
86
105
  import { Asset } from 'expo-asset';
87
106
  import { fetch } from 'expo/fetch';
88
- import manifest from '../assets/audiosprite.json';
107
+ import manifest from '../assets/mygameaudio.json';
89
108
 
90
109
  // Import the audio asset
91
- const audioAsset = require('../assets/audiosprite.mp3');
110
+ const audioAsset = require('../assets/mygameaudio.mp3');
92
111
 
93
112
  export default function App() {
94
113
  const [isLoaded, setIsLoaded] = useState(false);
@@ -147,19 +166,51 @@ export default function App() {
147
166
  }
148
167
  };
149
168
 
169
+ const stopBGM = () => {
170
+ const player = playerRef.current;
171
+ if (player) {
172
+ player.stop();
173
+ }
174
+ };
175
+
150
176
  return (
151
177
  <View style={styles.container}>
152
178
  <Text>AudioSprite Player Example</Text>
153
- <Button
154
- title="Play Sound 1"
179
+ <TouchableOpacity
180
+ onPress={() => loadPlayer()}
181
+ style={styles.button}
182
+ disabled={!isLoaded}
183
+ >
184
+ <Text style={styles.buttonText}>Load Player</Text>
185
+ </TouchableOpacity>
186
+ <TouchableOpacity
155
187
  onPress={() => playSound('Sound_1')}
188
+ style={styles.button}
156
189
  disabled={!isLoaded}
157
- />
158
- <Button
159
- title="Play Sound 2"
190
+ >
191
+ <Text style={styles.buttonText}>Play Sound 1</Text>
192
+ </TouchableOpacity>
193
+ <TouchableOpacity
160
194
  onPress={() => playSound('Sound_2')}
195
+ style={styles.button}
161
196
  disabled={!isLoaded}
162
- />
197
+ >
198
+ <Text style={styles.buttonText}>Play Sound 2</Text>
199
+ </TouchableOpacity>
200
+ <TouchableOpacity
201
+ onPress={() => playSound('bg_loop')}
202
+ style={styles.button}
203
+ disabled={!isLoaded}
204
+ >
205
+ <Text style={styles.buttonText}>Play Background Loop</Text>
206
+ </TouchableOpacity>
207
+ <TouchableOpacity
208
+ onPress={stopBGM}
209
+ style={styles.button}
210
+ disabled={!isLoaded}
211
+ >
212
+ <Text style={styles.buttonText}>Stop BGM</Text>
213
+ </TouchableOpacity>
163
214
  </View>
164
215
  );
165
216
  }
@@ -170,9 +221,28 @@ const styles = StyleSheet.create({
170
221
  alignItems: 'center',
171
222
  justifyContent: 'center',
172
223
  },
224
+ button: {
225
+ backgroundColor: '#DDDDDD',
226
+ padding: 10,
227
+ marginVertical: 5,
228
+ borderRadius: 5,
229
+ },
230
+ buttonText: {
231
+ color: '#000000',
232
+ textAlign: 'center',
233
+ },
173
234
  });
174
235
  ```
175
236
 
237
+ ## Inspiration
238
+
239
+ https://github.com/goldfire/howler.js
240
+ Generated json also works with new Howl({
241
+ sprite: {
242
+ key1: [offset, duration, (loop)]
243
+ },
244
+ });
245
+
176
246
  ## Contributing
177
247
 
178
248
  - [Development workflow](CONTRIBUTING.md#development-workflow)
@@ -183,6 +253,8 @@ const styles = StyleSheet.create({
183
253
 
184
254
  MIT
185
255
 
186
- ---
256
+ ## Credits
257
+
258
+ [Shaker, Woda, Conga, Bongo, Templeblock.wav](https://freesound.org/people/kwazi/sounds/34115/) by [kwazi](https://freesound.org/people/kwazi/) | License: [Attribution 3.0](http://creativecommons.org/licenses/by/3.0/)
187
259
 
188
260
  Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -10,6 +10,7 @@ export class AudioSpritePlayer {
10
10
 
11
11
  // Cache for the small, pre-split AudioBuffers used by mobile's QueueSourceNode
12
12
  spriteBufferCache = {};
13
+ loopingSource = null;
13
14
  constructor({
14
15
  audioContext,
15
16
  fetch,
@@ -44,20 +45,39 @@ export class AudioSpritePlayer {
44
45
  /**
45
46
  * Caches pre-split AudioBuffers for each sprite, which is necessary
46
47
  * for stable playback on mobile using the BufferQueueSourceNode.
48
+ *
49
+ * This method iterates through the audio sprite manifest and creates a separate
50
+ * AudioBuffer for each sound sprite. These smaller buffers are then stored
51
+ * in `this.spriteBufferCache` for efficient playback on mobile platforms,
52
+ * especially when using `AudioBufferQueueSourceNode`.
53
+ *
54
+ * The `audiosprite` manifest is expected to have the following structure for each sprite:
55
+ * `[start_time_ms, duration_ms, loop_boolean (optional)]`
56
+ *
57
+ * @example
58
+ * // Example audiosprite manifest structure:
59
+ * {
60
+ * "urls": ["audio.mp3", "audio.ogg"],
61
+ * "sprite": {
62
+ * "sound1": [0, 1000, false], // start at 0ms, duration 1000ms, no loop
63
+ * "sound2": [1500, 500, true], // start at 1500ms, duration 500ms, loop
64
+ * "background": [2000, 30000, true] // start at 2000ms, duration 30000ms, loop
65
+ * }
66
+ * }
47
67
  */
48
68
  _cacheSpriteBuffers() {
49
- if (!this.audioBuffer || !this.manifest || this.platform === 'web') {
69
+ if (!this.audioBuffer || !this.manifest) {
50
70
  return; // Only necessary for mobile platforms
51
71
  }
52
72
  const sampleRate = this.audioBuffer.sampleRate;
53
73
  const numChannels = this.audioBuffer.numberOfChannels;
54
74
  this.spriteBufferCache = {};
55
- for (const soundName in this.manifest.spritemap) {
56
- const sound = this.manifest.spritemap[soundName];
75
+ for (const soundName in this.manifest.sprite) {
76
+ const sound = this.manifest.sprite[soundName];
57
77
 
58
- // Calculate frame indices
59
- const startFrame = Math.floor(sound.start * sampleRate);
60
- const endFrame = Math.ceil(sound.end * sampleRate);
78
+ // Calculate frame indices based on audiosprite format: [start, duration, loop]
79
+ const startFrame = Math.floor(sound[0] * sampleRate / 1000); // Convert ms to frames
80
+ const endFrame = Math.ceil((sound[0] + sound[1]) * sampleRate / 1000); // Convert ms to frames
61
81
  const durationFrames = endFrame - startFrame;
62
82
  if (durationFrames <= 0) {
63
83
  console.warn(`Sprite "${soundName}" has zero or negative duration. Skipping.`);
@@ -91,10 +111,10 @@ export class AudioSpritePlayer {
91
111
  throw new Error(`Failed to fetch manifest: ${response.statusText}`);
92
112
  }
93
113
  this.manifest = await response.json();
94
- if (!this.manifest.resources || !this.manifest.spritemap) {
95
- throw new Error('Invalid audiosprite manifest format. Missing "resources" or "spritemap".');
114
+ if (!this.manifest.urls || !this.manifest.sprite) {
115
+ throw new Error('Invalid audiosprite manifest format. Missing "urls" or "sprite".');
96
116
  }
97
- const audioFileName = this.manifest.resources[0];
117
+ const audioFileName = this.manifest.urls[0];
98
118
  const audioUrl = new URL(audioFileName, response.url).href;
99
119
  const audioResponse = await this.fetch(audioUrl);
100
120
  if (!audioResponse.ok) {
@@ -104,8 +124,8 @@ export class AudioSpritePlayer {
104
124
  decodedBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
105
125
  } else {
106
126
  this.manifest = json;
107
- if (!this.manifest.resources || !this.manifest.spritemap) {
108
- throw new Error('Invalid audiosprite manifest format. Missing "resources" or "spritemap".');
127
+ if (!this.manifest.urls || !this.manifest.sprite) {
128
+ throw new Error('Invalid audiosprite manifest format. Missing "urls" or "sprite".');
109
129
  }
110
130
  let arrayBuffer;
111
131
  if (typeof audio === 'string') {
@@ -143,21 +163,21 @@ export class AudioSpritePlayer {
143
163
  console.error('Failed to resume AudioContext:', e);
144
164
  });
145
165
  }
146
- const sound = this.manifest.spritemap[soundName];
166
+ const sound = this.manifest.sprite[soundName];
147
167
  if (!sound) {
148
168
  console.warn(`Sound "${soundName}" not found in spritemap.`);
149
169
  return;
150
170
  }
151
- const duration = sound.end - sound.start;
171
+ const duration = sound[1];
152
172
  if (duration <= 0) {
153
173
  console.warn(`Sound "${soundName}" has invalid duration.`);
154
174
  return;
155
175
  }
156
176
  let source;
177
+ const spriteBuffer = this.spriteBufferCache[soundName];
157
178
 
158
179
  // 🚨 MOBILE LOGIC: Use AudioBufferQueueSourceNode with cached split buffer
159
180
  if (this.platform !== 'web') {
160
- const spriteBuffer = this.spriteBufferCache[soundName];
161
181
  if (!spriteBuffer) {
162
182
  console.error(`RNAS Error: Split buffer for "${soundName}" not found in cache.`);
163
183
  return;
@@ -166,14 +186,33 @@ export class AudioSpritePlayer {
166
186
  console.error('RNAS Error: createBufferQueueSource is not available on this native platform.');
167
187
  return;
168
188
  }
169
- source = this.audioContext.createBufferQueueSource();
170
-
171
- // Mobile Implementation: Enqueue the specific, short sprite buffer
172
- source.enqueueBuffer(spriteBuffer);
173
- source.connect(this.audioContext.destination);
189
+ const loop = sound[2];
190
+ if (loop) {
191
+ // Always use AudioBufferQueueSourceNode
192
+ source = this.audioContext.createBufferQueueSource();
193
+ source.enqueueBuffer(spriteBuffer);
194
+ source.connect(this.audioContext.destination);
174
195
 
175
- // This will play the short buffer from its start to its end.
176
- source.start(1);
196
+ // Manual looping using onEnded
197
+ const loopHandler = () => {
198
+ // Only re-enqueue if this is still the active looping source
199
+ if (this.loopingSource === source) {
200
+ source.enqueueBuffer(spriteBuffer);
201
+ // Restart the source immediately after re-enqueueing
202
+ source.start(0);
203
+ }
204
+ };
205
+ source.onEnded = loopHandler;
206
+ source.start(0); // Start immediately
207
+ this.loopingSource = source; // Store reference to looping source
208
+ } else {
209
+ // For non-looping sounds on mobile, use AudioBufferQueueSourceNode
210
+ source = this.audioContext.createBufferQueueSource();
211
+ source.enqueueBuffer(spriteBuffer);
212
+ source.connect(this.audioContext.destination);
213
+ source.start(1);
214
+ console.log('non loop', soundName);
215
+ }
177
216
  } else {
178
217
  // 🌐 WEB LOGIC (Standard Web Audio API)
179
218
  source = this.audioContext.createBufferSource();
@@ -181,16 +220,24 @@ export class AudioSpritePlayer {
181
220
  console.error('RNAS Error: createBufferSource() returned an invalid object on web. Aborting playback.');
182
221
  return;
183
222
  }
184
- source.buffer = this.audioBuffer;
223
+ source.buffer = spriteBuffer;
185
224
  source.connect(this.audioContext.destination);
186
-
187
- // Use the 'audiosprite' format: start(when, offset, duration)
188
- source.start(0,
189
- // Start playing now
190
- sound.start,
191
- // The offset
192
- duration // The calculated duration
193
- );
225
+ const loop = sound[2]; // audiosprite stores loop as the third element in the array
226
+ if (loop) {
227
+ source.loop = true;
228
+ source.loopStart = 0; // Relative to the spriteBuffer
229
+ source.loopEnd = sound[1] / 1000; // Duration of the spriteBuffer
230
+ source.start(0); // Start immediately, no offset for the individual spriteBuffer
231
+ this.loopingSource = source; // Store reference to looping source
232
+ } else {
233
+ // Use the 'audiosprite' format: start(when, offset, duration)
234
+ source.start(0,
235
+ // Start playing now
236
+ 0,
237
+ // The offset in seconds (relative to the spriteBuffer)
238
+ sound[1] / 1000 // The calculated duration in seconds
239
+ );
240
+ }
194
241
  }
195
242
  console.log(`RNAS: played ${soundName} on ${this.platform}`);
196
243
  }
@@ -200,5 +247,19 @@ export class AudioSpritePlayer {
200
247
  getAudioBuffer() {
201
248
  return this.audioBuffer;
202
249
  }
250
+
251
+ /**
252
+ * Stops the currently looping audio sprite.
253
+ * If a looping sound is playing, it will be stopped immediately.
254
+ */
255
+ stop() {
256
+ if (this.loopingSource) {
257
+ this.loopingSource.stop();
258
+ this.loopingSource = null;
259
+ console.log('RNAS: Looping audio stopped.');
260
+ } else {
261
+ console.log('RNAS: No looping audio to stop.');
262
+ }
263
+ }
203
264
  }
204
265
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["AudioSpritePlayer","spriteBufferCache","constructor","audioContext","fetch","platform","AudioContext","Error","audioBuffer","manifest","createBufferSource","name","console","log","_cacheSpriteBuffers","sampleRate","numChannels","numberOfChannels","soundName","spritemap","sound","startFrame","Math","floor","start","endFrame","ceil","end","durationFrames","warn","spriteBuffer","createBuffer","i","sourceData","getChannelData","destinationData","segment","subarray","set","load","json","audio","decodedBuffer","response","ok","statusText","resources","audioFileName","audioUrl","URL","url","href","audioResponse","arrayBuffer","decodeAudioData","error","play","state","resume","catch","e","duration","source","createBufferQueueSource","enqueueBuffer","connect","destination","buffer","getManifest","getAudioBuffer"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["AudioSpritePlayer","spriteBufferCache","loopingSource","constructor","audioContext","fetch","platform","AudioContext","Error","audioBuffer","manifest","createBufferSource","name","console","log","_cacheSpriteBuffers","sampleRate","numChannels","numberOfChannels","soundName","sprite","sound","startFrame","Math","floor","endFrame","ceil","durationFrames","warn","spriteBuffer","createBuffer","i","sourceData","getChannelData","destinationData","segment","subarray","set","load","json","audio","decodedBuffer","response","ok","statusText","urls","audioFileName","audioUrl","URL","url","href","audioResponse","arrayBuffer","decodeAudioData","error","play","state","resume","catch","e","duration","source","createBufferQueueSource","loop","enqueueBuffer","connect","destination","loopHandler","start","onEnded","buffer","loopStart","loopEnd","getManifest","getAudioBuffer","stop"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -10,6 +10,7 @@ export declare class AudioSpritePlayer {
10
10
  manifest: any | null;
11
11
  platform: string;
12
12
  private spriteBufferCache;
13
+ private loopingSource;
13
14
  constructor({ audioContext, fetch, platform, }: {
14
15
  audioContext: any | null;
15
16
  fetch: any;
@@ -18,11 +19,35 @@ export declare class AudioSpritePlayer {
18
19
  /**
19
20
  * Caches pre-split AudioBuffers for each sprite, which is necessary
20
21
  * for stable playback on mobile using the BufferQueueSourceNode.
22
+ *
23
+ * This method iterates through the audio sprite manifest and creates a separate
24
+ * AudioBuffer for each sound sprite. These smaller buffers are then stored
25
+ * in `this.spriteBufferCache` for efficient playback on mobile platforms,
26
+ * especially when using `AudioBufferQueueSourceNode`.
27
+ *
28
+ * The `audiosprite` manifest is expected to have the following structure for each sprite:
29
+ * `[start_time_ms, duration_ms, loop_boolean (optional)]`
30
+ *
31
+ * @example
32
+ * // Example audiosprite manifest structure:
33
+ * {
34
+ * "urls": ["audio.mp3", "audio.ogg"],
35
+ * "sprite": {
36
+ * "sound1": [0, 1000, false], // start at 0ms, duration 1000ms, no loop
37
+ * "sound2": [1500, 500, true], // start at 1500ms, duration 500ms, loop
38
+ * "background": [2000, 30000, true] // start at 2000ms, duration 30000ms, loop
39
+ * }
40
+ * }
21
41
  */
22
42
  private _cacheSpriteBuffers;
23
43
  load(json: any, audio?: any): Promise<void>;
24
44
  play(soundName: string): void;
25
45
  getManifest(): any;
26
46
  getAudioBuffer(): any;
47
+ /**
48
+ * Stops the currently looping audio sprite.
49
+ * If a looping sound is playing, it will be stopped immediately.
50
+ */
51
+ stop(): void;
27
52
  }
28
53
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,YAAY,EAAE,GAAG,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,GAAG,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,iBAAiB,CAA2B;gBAExC,EACV,YAAY,EACZ,KAAK,EACL,QAAQ,GACT,EAAE;QACD,YAAY,EAAE,GAAG,GAAG,IAAI,CAAC;QACzB,KAAK,EAAE,GAAG,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;KAClB;IAkCD;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA8CrB,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG;IAkEjC,IAAI,CAAC,SAAS,EAAE,MAAM;IA+EtB,WAAW;IAIX,cAAc;CAGf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,YAAY,EAAE,GAAG,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,GAAG,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,aAAa,CAAoB;gBAE7B,EACV,YAAY,EACZ,KAAK,EACL,QAAQ,GACT,EAAE;QACD,YAAY,EAAE,GAAG,GAAG,IAAI,CAAC;QACzB,KAAK,EAAE,GAAG,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;KAClB;IAkCD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,mBAAmB;IA8CrB,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG;IAkEjC,IAAI,CAAC,SAAS,EAAE,MAAM;IA2GtB,WAAW;IAIX,cAAc;IAId;;;OAGG;IACH,IAAI;CASL"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audiosprites",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "audio sprites ",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
package/src/index.tsx CHANGED
@@ -11,6 +11,7 @@ export class AudioSpritePlayer {
11
11
  platform: string;
12
12
  // Cache for the small, pre-split AudioBuffers used by mobile's QueueSourceNode
13
13
  private spriteBufferCache: Record<string, any> = {};
14
+ private loopingSource: any | null = null;
14
15
 
15
16
  constructor({
16
17
  audioContext,
@@ -57,9 +58,28 @@ export class AudioSpritePlayer {
57
58
  /**
58
59
  * Caches pre-split AudioBuffers for each sprite, which is necessary
59
60
  * for stable playback on mobile using the BufferQueueSourceNode.
61
+ *
62
+ * This method iterates through the audio sprite manifest and creates a separate
63
+ * AudioBuffer for each sound sprite. These smaller buffers are then stored
64
+ * in `this.spriteBufferCache` for efficient playback on mobile platforms,
65
+ * especially when using `AudioBufferQueueSourceNode`.
66
+ *
67
+ * The `audiosprite` manifest is expected to have the following structure for each sprite:
68
+ * `[start_time_ms, duration_ms, loop_boolean (optional)]`
69
+ *
70
+ * @example
71
+ * // Example audiosprite manifest structure:
72
+ * {
73
+ * "urls": ["audio.mp3", "audio.ogg"],
74
+ * "sprite": {
75
+ * "sound1": [0, 1000, false], // start at 0ms, duration 1000ms, no loop
76
+ * "sound2": [1500, 500, true], // start at 1500ms, duration 500ms, loop
77
+ * "background": [2000, 30000, true] // start at 2000ms, duration 30000ms, loop
78
+ * }
79
+ * }
60
80
  */
61
81
  private _cacheSpriteBuffers() {
62
- if (!this.audioBuffer || !this.manifest || this.platform === 'web') {
82
+ if (!this.audioBuffer || !this.manifest) {
63
83
  return; // Only necessary for mobile platforms
64
84
  }
65
85
 
@@ -67,12 +87,12 @@ export class AudioSpritePlayer {
67
87
  const numChannels = this.audioBuffer.numberOfChannels;
68
88
  this.spriteBufferCache = {};
69
89
 
70
- for (const soundName in this.manifest.spritemap) {
71
- const sound = this.manifest.spritemap[soundName];
90
+ for (const soundName in this.manifest.sprite) {
91
+ const sound = this.manifest.sprite[soundName];
72
92
 
73
- // Calculate frame indices
74
- const startFrame = Math.floor(sound.start * sampleRate);
75
- const endFrame = Math.ceil(sound.end * sampleRate);
93
+ // Calculate frame indices based on audiosprite format: [start, duration, loop]
94
+ const startFrame = Math.floor((sound[0] * sampleRate) / 1000); // Convert ms to frames
95
+ const endFrame = Math.ceil(((sound[0] + sound[1]) * sampleRate) / 1000); // Convert ms to frames
76
96
  const durationFrames = endFrame - startFrame;
77
97
 
78
98
  if (durationFrames <= 0) {
@@ -116,13 +136,13 @@ export class AudioSpritePlayer {
116
136
  }
117
137
  this.manifest = await response.json();
118
138
 
119
- if (!this.manifest.resources || !this.manifest.spritemap) {
139
+ if (!this.manifest.urls || !this.manifest.sprite) {
120
140
  throw new Error(
121
- 'Invalid audiosprite manifest format. Missing "resources" or "spritemap".'
141
+ 'Invalid audiosprite manifest format. Missing "urls" or "sprite".'
122
142
  );
123
143
  }
124
144
 
125
- const audioFileName = this.manifest.resources[0];
145
+ const audioFileName = this.manifest.urls[0];
126
146
  const audioUrl = new URL(audioFileName, response.url).href;
127
147
 
128
148
  const audioResponse = await this.fetch(audioUrl);
@@ -136,9 +156,9 @@ export class AudioSpritePlayer {
136
156
  decodedBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
137
157
  } else {
138
158
  this.manifest = json;
139
- if (!this.manifest.resources || !this.manifest.spritemap) {
159
+ if (!this.manifest.urls || !this.manifest.sprite) {
140
160
  throw new Error(
141
- 'Invalid audiosprite manifest format. Missing "resources" or "spritemap".'
161
+ 'Invalid audiosprite manifest format. Missing "urls" or "sprite".'
142
162
  );
143
163
  }
144
164
 
@@ -183,24 +203,23 @@ export class AudioSpritePlayer {
183
203
  });
184
204
  }
185
205
 
186
- const sound = this.manifest.spritemap[soundName];
206
+ const sound = this.manifest.sprite[soundName];
187
207
  if (!sound) {
188
208
  console.warn(`Sound "${soundName}" not found in spritemap.`);
189
209
  return;
190
210
  }
191
211
 
192
- const duration = sound.end - sound.start;
212
+ const duration = sound[1];
193
213
  if (duration <= 0) {
194
214
  console.warn(`Sound "${soundName}" has invalid duration.`);
195
215
  return;
196
216
  }
197
217
 
198
218
  let source: any;
219
+ const spriteBuffer = this.spriteBufferCache[soundName];
199
220
 
200
221
  // 🚨 MOBILE LOGIC: Use AudioBufferQueueSourceNode with cached split buffer
201
222
  if (this.platform !== 'web') {
202
- const spriteBuffer = this.spriteBufferCache[soundName];
203
-
204
223
  if (!spriteBuffer) {
205
224
  console.error(
206
225
  `RNAS Error: Split buffer for "${soundName}" not found in cache.`
@@ -215,15 +234,35 @@ export class AudioSpritePlayer {
215
234
  return;
216
235
  }
217
236
 
218
- source = this.audioContext.createBufferQueueSource();
219
-
220
- // Mobile Implementation: Enqueue the specific, short sprite buffer
221
- source.enqueueBuffer(spriteBuffer);
222
-
223
- source.connect(this.audioContext.destination);
237
+ const loop = sound[2];
238
+
239
+ if (loop) {
240
+ // Always use AudioBufferQueueSourceNode
241
+ source = this.audioContext.createBufferQueueSource();
242
+ source.enqueueBuffer(spriteBuffer);
243
+ source.connect(this.audioContext.destination);
244
+
245
+ // Manual looping using onEnded
246
+ const loopHandler = () => {
247
+ // Only re-enqueue if this is still the active looping source
248
+ if (this.loopingSource === source) {
249
+ source.enqueueBuffer(spriteBuffer);
250
+ // Restart the source immediately after re-enqueueing
251
+ source.start(0);
252
+ }
253
+ };
254
+ source.onEnded = loopHandler;
224
255
 
225
- // This will play the short buffer from its start to its end.
226
- source.start(1);
256
+ source.start(0); // Start immediately
257
+ this.loopingSource = source; // Store reference to looping source
258
+ } else {
259
+ // For non-looping sounds on mobile, use AudioBufferQueueSourceNode
260
+ source = this.audioContext.createBufferQueueSource();
261
+ source.enqueueBuffer(spriteBuffer);
262
+ source.connect(this.audioContext.destination);
263
+ source.start(1);
264
+ console.log('non loop', soundName);
265
+ }
227
266
  } else {
228
267
  // 🌐 WEB LOGIC (Standard Web Audio API)
229
268
  source = this.audioContext.createBufferSource();
@@ -235,15 +274,24 @@ export class AudioSpritePlayer {
235
274
  return;
236
275
  }
237
276
 
238
- source.buffer = this.audioBuffer;
277
+ source.buffer = spriteBuffer;
239
278
  source.connect(this.audioContext.destination);
240
279
 
241
- // Use the 'audiosprite' format: start(when, offset, duration)
242
- source.start(
243
- 0, // Start playing now
244
- sound.start, // The offset
245
- duration // The calculated duration
246
- );
280
+ const loop = sound[2]; // audiosprite stores loop as the third element in the array
281
+ if (loop) {
282
+ source.loop = true;
283
+ source.loopStart = 0; // Relative to the spriteBuffer
284
+ source.loopEnd = sound[1] / 1000; // Duration of the spriteBuffer
285
+ source.start(0); // Start immediately, no offset for the individual spriteBuffer
286
+ this.loopingSource = source; // Store reference to looping source
287
+ } else {
288
+ // Use the 'audiosprite' format: start(when, offset, duration)
289
+ source.start(
290
+ 0, // Start playing now
291
+ 0, // The offset in seconds (relative to the spriteBuffer)
292
+ sound[1] / 1000 // The calculated duration in seconds
293
+ );
294
+ }
247
295
  }
248
296
 
249
297
  console.log(`RNAS: played ${soundName} on ${this.platform}`);
@@ -256,4 +304,18 @@ export class AudioSpritePlayer {
256
304
  getAudioBuffer() {
257
305
  return this.audioBuffer;
258
306
  }
307
+
308
+ /**
309
+ * Stops the currently looping audio sprite.
310
+ * If a looping sound is playing, it will be stopped immediately.
311
+ */
312
+ stop() {
313
+ if (this.loopingSource) {
314
+ this.loopingSource.stop();
315
+ this.loopingSource = null;
316
+ console.log('RNAS: Looping audio stopped.');
317
+ } else {
318
+ console.log('RNAS: No looping audio to stop.');
319
+ }
320
+ }
259
321
  }