react-native-audiosprites 0.4.0 → 0.5.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
@@ -27,6 +27,20 @@ audiosprite --output src/__tests__/sounds/mygameaudio --format howler --loop "bg
27
27
 
28
28
  This command will generate `mygameaudio.json`, `mygameaudio.mp3`, `mygameaudio.ogg`, `mygameaudio.m4a`, and `mygameaudio.ac3` in the `src/__tests__/sounds/` directory.
29
29
 
30
+ /**
31
+ * ⚠️ ANDROID RESOURCE CONFLICT FIX ⚠️
32
+ * * Android's build system (Gradle) generates resource IDs based on filenames WITHOUT extensions.
33
+ * If we generate "game_go_v2.mp3" and "game_go_v2.json", Android sees them both
34
+ * as the resource ID `R.raw.game_go_v2`, causing a "Duplicate resources" build error.
35
+ * * To fix this, make a script to automatically rename the generated JSON file to:
36
+ * `${outputFileName}_data.json`
37
+ * * Result:
38
+ * - game_go_v2.mp3 -> R.raw.game_go_v2
39
+ * - game_go_v2_data.json -> R.raw.game_go_v2_data
40
+ */
41
+
42
+ ![Example Demo](example_demo.png)
43
+
30
44
  ### Looping Sounds
31
45
 
32
46
  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.
@@ -41,6 +55,40 @@ When you play a looping sound, it will play continuously until you stop it using
41
55
 
42
56
  Then, you can use the `AudioSpritePlayer` to play the sounds from the sprite.
43
57
 
58
+ ### Type Safety for Native Audio Extensions
59
+
60
+ This library includes robust type definitions for `react-native-audio-api` extensions, such as `createBufferQueueSource`. These types are automatically generated and synchronized with the native audio library version `0.10.1`.
61
+
62
+ ### Audio Routing and Volume Control (Mixer Graph)
63
+
64
+ The library features a mixer graph that allows you to control the volume of Sound Effects (SFX) and Music separately, as well as a global Master volume.
65
+
66
+ - `sfx`: Short sound effects (coins, jumps, UI). High priority.
67
+ - `music`: Background tracks. Lower priority.
68
+
69
+ You can specify the channel when playing a sound:
70
+
71
+ ```typescript
72
+ // Play on 'sfx' channel (default)
73
+ player.play('coin_sound');
74
+
75
+ // Play on 'music' channel
76
+ player.play('bg_music', { channel: 'music' });
77
+ ```
78
+
79
+ You can control the volume dynamically:
80
+
81
+ ```typescript
82
+ // Set Music Volume (0.0 to 1.0)
83
+ player.setMusicVolume(0.5);
84
+
85
+ // Set SFX Volume (0.0 to 1.0)
86
+ player.setSFXVolume(0.8);
87
+
88
+ // Set Master Volume (affects both)
89
+ player.volume = 1.0;
90
+ ```
91
+
44
92
  ### Browser Environment
45
93
 
46
94
  ```typescript
@@ -159,7 +207,12 @@ export default function App() {
159
207
  const playSound = (soundName: string) => {
160
208
  const player = playerRef.current;
161
209
  if (player && isLoaded) {
162
- player.play(soundName);
210
+ // Play on music channel
211
+ if (soundName === 'bg_loop') {
212
+ player.play(soundName, { channel: 'music' });
213
+ } else {
214
+ player.play(soundName);
215
+ }
163
216
  console.log(`Playing sound: ${soundName}`);
164
217
  } else {
165
218
  console.warn('Player not loaded yet.');
@@ -263,6 +316,315 @@ Made with [create-react-native-library](https://github.com/callstack/react-nativ
263
316
 
264
317
  # Translations
265
318
 
319
+ ## Telugu
320
+
321
+ ![react-native-audiosprites_poster](react-native-audiosprites_poster.jpg)
322
+
323
+ # react-native-audiosprites
324
+
325
+ 'audiosprite' టూల్ ద్వారా రూపొందించబడిన ఆడియో స్ప్రైట్‌ల కోసం ఒక యూనివర్సల్ ప్లేయర్.
326
+ ఒకే సమయంలో అనేక శబ్దాలను ప్లే చేయడానికి మద్దతు ఇస్తుంది!
327
+
328
+ ## ఇన్‌స్టాలేషన్
329
+
330
+ ```sh
331
+ npm install react-native-audiosprites
332
+ ```
333
+
334
+ ```sh
335
+ yarn add react-native-audiosprites
336
+ ```
337
+
338
+ ## వినియోగం
339
+
340
+ ముందుగా, మీరు `audiosprite` టూల్‌ను ఉపయోగించి ఆడియో స్ప్రైట్ మరియు JSON మానిఫెస్ట్ ఫైల్‌ను రూపొందించాలి.
341
+
342
+ మీరు [`audiosprite`](https://www.npmjs.com/package/audiosprite)ని గ్లోబల్‌గా ఇన్‌స్టాల్ చేశారని అనుకుందాం:
343
+
344
+ ```sh
345
+ 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
346
+ ```
347
+
348
+ ఈ కమాండ్ `src/__tests__/sounds/` డైరెక్టరీలో `mygameaudio.json`, `mygameaudio.mp3`, `mygameaudio.ogg`, `mygameaudio.m4a`, మరియు `mygameaudio.ac3` ఫైళ్లను సృష్టిస్తుంది.
349
+
350
+ /**
351
+ * ⚠️ ANDROID వనరుల సంఘర్షణ పరిష్కారం ⚠️
352
+ * * Android బిల్డ్ సిస్టమ్ (Gradle) ఎక్స్‌టెన్షన్‌లు లేకుండా ఫైల్‌నేమ్‌ల ఆధారంగా వనరుల IDలను సృష్టిస్తుంది.
353
+ * ఒకవేళ మనం "game_go_v2.mp3" మరియు "game_go_v2.json"ను రూపొందిస్తే, Android రెండింటినీ
354
+ * `R.raw.game_go_v2` అనే ఒకే వనరు IDగా చూస్తుంది, దీనివల్ల "డూప్లికేట్ వనరులు" బిల్డ్ ఎర్రర్ వస్తుంది.
355
+ * * దీన్ని పరిష్కరించడానికి, రూపొందించిన JSON ఫైల్ పేరును ఈ విధంగా మార్చాలి:
356
+ * `${outputFileName}_data.json`
357
+ * * ఫలితం:
358
+ * - game_go_v2.mp3 -> R.raw.game_go_v2
359
+ * - game_go_v2_data.json -> R.raw.game_go_v2_data
360
+ */
361
+
362
+ ### లూపింగ్ సౌండ్స్ (Looping Sounds)
363
+
364
+ మీరు `audiosprite` కమాండ్‌తో `--loop` ఎంపికను ఉపయోగించి లూపింగ్ శబ్దాలను సృష్టించవచ్చు. `--loop` ఎంపిక యొక్క విలువ మీరు లూప్ చేయాలనుకుంటున్న శబ్దం పేరు అయి ఉండాలి.
365
+
366
+ ఉదాహరణకు, `bg_music` శబ్దాన్ని లూప్ చేయడానికి, మీరు ఈ కింది కమాండ్‌ను ఉపయోగిస్తారు:
367
+
368
+ ```sh
369
+ 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
370
+ ```
371
+
372
+ మీరు లూపింగ్ శబ్దాన్ని ప్లే చేసినప్పుడు, మీరు `player.stop()` పద్ధతిని ఉపయోగించి దాన్ని ఆపే వరకు అది నిరంతరాయంగా ప్లే అవుతూనే ఉంటుంది. వెబ్ మరియు మొబైల్ ప్లాట్‌ఫారమ్‌లలో లూపింగ్ ఫంక్షనాలిటీకి మద్దతు ఉంది.
373
+
374
+ తరువాత, మీరు స్ప్రైట్ నుండి శబ్దాలను ప్లే చేయడానికి `AudioSpritePlayer`ని ఉపయోగించవచ్చు.
375
+
376
+ ### ఆడియో రూటింగ్ మరియు వాల్యూమ్ నియంత్రణ (మిక్సర్ గ్రాఫ్)
377
+
378
+ ఈ లైబ్రరీ మిక్సర్ గ్రాఫ్ ఫీచర్‌ను కలిగి ఉంది, ఇది సౌండ్ ఎఫెక్ట్స్ (SFX) మరియు మ్యూజిక్ వాల్యూమ్‌లను విడివిడిగా, అలాగే గ్లోబల్ మాస్టర్ వాల్యూమ్‌ను నియంత్రించడానికి మిమ్మల్ని అనుమతిస్తుంది.
379
+
380
+ - `sfx`: చిన్న సౌండ్ ఎఫెక్ట్స్ (కాయిన్స్, జంప్స్, UI). అధిక ప్రాధాన్యత.
381
+ - `music`: నేపథ్య ట్రాక్‌లు (Background tracks). తక్కువ ప్రాధాన్యత.
382
+
383
+ శబ్దాన్ని ప్లే చేస్తున్నప్పుడు మీరు ఛానెల్‌ని పేర్కొనవచ్చు:
384
+
385
+ ```typescript
386
+ // 'sfx' ఛానెల్‌లో ప్లే చేయండి (డిఫాల్ట్)
387
+ player.play('coin_sound');
388
+
389
+ // 'music' ఛానెల్‌లో ప్లే చేయండి
390
+ player.play('bg_music', { channel: 'music' });
391
+ ```
392
+
393
+ మీరు వాల్యూమ్‌ను డైనమిక్‌గా నియంత్రించవచ్చు:
394
+
395
+ ```typescript
396
+ // మ్యూజిక్ వాల్యూమ్‌ను సెట్ చేయండి (0.0 నుండి 1.0)
397
+ player.setMusicVolume(0.5);
398
+
399
+ // SFX వాల్యూమ్‌ను సెట్ చేయండి (0.0 నుండి 1.0)
400
+ player.setSFXVolume(0.8);
401
+
402
+ // మాస్టర్ వాల్యూమ్‌ను సెట్ చేయండి (రెండింటినీ ప్రభావితం చేస్తుంది)
403
+ player.volume = 1.0;
404
+ ```
405
+
406
+ ### బ్రౌజర్ ఎన్విరాన్మెంట్ (Browser Environment)
407
+
408
+ ```typescript
409
+ import { AudioSpritePlayer } from 'react-native-audiosprites';
410
+
411
+ const player = new AudioSpritePlayer({
412
+ platform: 'web',
413
+ });
414
+
415
+ async function playSound(soundName: string) {
416
+ try {
417
+ // ఆడియో స్ప్రైట్ మానిఫెస్ట్ మరియు ఆడియో ఫైళ్లను లోడ్ చేయండి
418
+ // మీ audiosprite.json ఫైల్ పాత్‌ను సరిచూసుకోండి
419
+ await player.load('./src/__tests__/sounds/mygameaudio.json');
420
+ console.log('ఆడియో స్ప్రైట్ విజయవంతంగా లోడ్ అయింది.');
421
+
422
+ // స్ప్రైట్‌మ్యాప్ నుండి శబ్దాన్ని ప్లే చేయండి
423
+ player.play(soundName);
424
+ console.log(`శబ్దాన్ని ప్లే చేస్తోంది: ${soundName}`);
425
+ } catch (error) {
426
+ console.error('శబ్దాన్ని ప్లే చేయడంలో లోపం:', error);
427
+ }
428
+ }
429
+
430
+ function stopSound() {
431
+ player.stop();
432
+ console.log('లూపింగ్ శబ్దం ఆగిపోయింది.');
433
+ }
434
+
435
+ // వినియోగ ఉదాహరణ:
436
+ playSound('Sound_1');
437
+ // playSound('Sound_2');
438
+ // లూపింగ్ శబ్దాన్ని ఆపడానికి:
439
+ // stopSound();
440
+ ```
441
+
442
+ ### రియాక్ట్ నేటివ్ ఎన్విరాన్మెంట్ (React Native Environment)
443
+
444
+ రియాక్ట్ నేటివ్ కోసం, ఆడియో ప్లేబ్యాక్ మరియు అసెట్ లోడింగ్‌ను నిర్వహించడానికి మీకు `react-native-audio-api` మరియు `expo-asset` అవసరం.
445
+
446
+ ముందుగా, డిపెండెన్సీలను ఇన్‌స్టాల్ చేయండి:
447
+
448
+ ```sh
449
+ npm install react-native-audio-api expo-asset expo-file-system
450
+ # లేదా
451
+ yarn add react-native-audio-api expo-asset expo-file-system
452
+ ```
453
+
454
+ `react-native-audio-api` డాక్యుమెంటేషన్ ప్రకారం `metro.config.js`ని మార్చండి: https://docs.swmansion.com/react-native-audio-api/docs/fundamentals/getting-started
455
+
456
+ ```js
457
+ module.exports = wrapWithAudioAPIMetroConfig(config);
458
+ ```
459
+
460
+ తరువాత, మీరు దీన్ని మీ కాంపోనెంట్‌లో ఉపయోగించవచ్చు:
461
+
462
+ ```typescript
463
+ import { StyleSheet, View, Text, Platform, TouchableOpacity } from 'react-native';
464
+ import { AudioSpritePlayer } from 'react-native-audiosprites';
465
+ import { AudioManager, AudioContext } from 'react-native-audio-api';
466
+ import { useEffect, useState, useRef } from 'react';
467
+ import { Asset } from 'expo-asset';
468
+ import { fetch } from 'expo/fetch';
469
+ import manifest from '../assets/mygameaudio.json';
470
+
471
+ // ఆడియో అసెట్‌ను ఇంపోర్ట్ చేయండి
472
+ const audioAsset = require('../assets/mygameaudio.mp3');
473
+
474
+ export default function App() {
475
+ const [isLoaded, setIsLoaded] = useState(false);
476
+ const playerRef = useRef<AudioSpritePlayer | null>(null);
477
+
478
+ useEffect(() => {
479
+ const loadPlayer = async () => {
480
+ const asset = Asset.fromModule(audioAsset);
481
+ await asset.downloadAsync();
482
+ const audioUri = asset.localUri || asset.uri;
483
+
484
+ if (!audioUri) {
485
+ console.error('ఆడియో URIని పొందడంలో విఫలమైంది.');
486
+ return;
487
+ }
488
+
489
+ if (Platform.OS === 'ios') {
490
+ try {
491
+ await AudioManager.setAudioSessionOptions({
492
+ iosCategory: 'playback',
493
+ iosOptions: ['mixWithOthers'],
494
+ });
495
+ await AudioManager.setAudioSessionActivity(true);
496
+ } catch (e) {
497
+ console.error('AudioSession ఎంపికలను కాన్ఫిగర్ చేయడంలో విఫలమైంది:', e);
498
+ }
499
+ }
500
+
501
+ const audioContext = new AudioContext();
502
+ const audioPlayer = new AudioSpritePlayer({
503
+ audioContext,
504
+ fetch: fetch.bind(globalThis),
505
+ platform: Platform.OS,
506
+ });
507
+
508
+ try {
509
+ await audioPlayer.load(manifest, audioUri);
510
+ playerRef.current = audioPlayer;
511
+ setIsLoaded(true);
512
+ console.log('ఆడియో స్ప్రైట్ విజయవంతంగా లోడ్ అయింది.');
513
+ } catch (error) {
514
+ console.error('ఆడియో స్ప్రైట్‌ను లోడ్ చేయడంలో విఫలమైంది:', error);
515
+ }
516
+ };
517
+
518
+ loadPlayer();
519
+ }, []);
520
+
521
+ const playSound = (soundName: string) => {
522
+ const player = playerRef.current;
523
+ if (player && isLoaded) {
524
+ if (soundName === 'bg_loop') {
525
+ player.play(soundName, { channel: 'music' });
526
+ } else {
527
+ player.play(soundName);
528
+ }
529
+ console.log(`శబ్దాన్ని ప్లే చేస్తోంది: ${soundName}`);
530
+ } else {
531
+ console.warn('ప్లేయర్ ఇంకా లోడ్ కాలేదు.');
532
+ }
533
+ };
534
+
535
+ const stopBGM = () => {
536
+ const player = playerRef.current;
537
+ if (player) {
538
+ player.stop();
539
+ }
540
+ };
541
+
542
+ return (
543
+ <View style={styles.container}>
544
+ <Text>AudioSprite Player ఉదాహరణ</Text>
545
+ <TouchableOpacity
546
+ onPress={() => loadPlayer()}
547
+ style={styles.button}
548
+ disabled={!isLoaded}
549
+ >
550
+ <Text style={styles.buttonText}>ప్లేయర్‌ని లోడ్ చేయండి</Text>
551
+ </TouchableOpacity>
552
+ <TouchableOpacity
553
+ onPress={() => playSound('Sound_1')}
554
+ style={styles.button}
555
+ disabled={!isLoaded}
556
+ >
557
+ <Text style={styles.buttonText}>Sound 1 ప్లే చేయండి</Text>
558
+ </TouchableOpacity>
559
+ <TouchableOpacity
560
+ onPress={() => playSound('Sound_2')}
561
+ style={styles.button}
562
+ disabled={!isLoaded}
563
+ >
564
+ <Text style={styles.buttonText}>Sound 2 ప్లే చేయండి</Text>
565
+ </TouchableOpacity>
566
+ <TouchableOpacity
567
+ onPress={() => playSound('bg_loop')}
568
+ style={styles.button}
569
+ disabled={!isLoaded}
570
+ >
571
+ <Text style={styles.buttonText}>బ్యాక్‌గ్రౌండ్ లూప్‌ను ప్లే చేయండి</Text>
572
+ </TouchableOpacity>
573
+ <TouchableOpacity
574
+ onPress={stopBGM}
575
+ style={styles.button}
576
+ disabled={!isLoaded}
577
+ >
578
+ <Text style={styles.buttonText}>BGM ఆపు</Text>
579
+ </TouchableOpacity>
580
+ </View>
581
+ );
582
+ }
583
+
584
+ const styles = StyleSheet.create({
585
+ container: {
586
+ flex: 1,
587
+ alignItems: 'center',
588
+ justifyContent: 'center',
589
+ },
590
+ button: {
591
+ backgroundColor: '#DDDDDD',
592
+ padding: 10,
593
+ marginVertical: 5,
594
+ borderRadius: 5,
595
+ },
596
+ buttonText: {
597
+ color: '#000000',
598
+ textAlign: 'center',
599
+ },
600
+ });
601
+ ```
602
+
603
+ ## స్ఫూర్తి (Inspiration)
604
+
605
+ https://github.com/goldfire/howler.js
606
+ జనరేట్ చేయబడిన json new Howl({
607
+ sprite: {
608
+ key1: [offset, duration, (loop)]
609
+ },
610
+ }); తో కూడా పనిచేస్తుంది.
611
+
612
+ ## సహకారం (Contributing)
613
+
614
+ - [డెవలప్‌మెంట్ వర్క్‌ఫ్లో](CONTRIBUTING.md#development-workflow)
615
+ - [పుల్ రిక్వెస్ట్ పంపడం](CONTRIBUTING.md#sending-a-pull-request)
616
+ - [ప్రవర్తనా నియమావళి](CODE_OF_CONDUCT.md)
617
+
618
+ ## లైసెన్స్ (License)
619
+
620
+ MIT
621
+
622
+ ## క్రెడిట్స్ (Credits)
623
+
624
+ [Shaker, Woda, Conga, Bongo, Templeblock.wav](https://freesound.org/people/kwazi/sounds/34115/) by [kwazi](https://freesound.org/people/kwazi/) | లైసెన్స్: [అట్రిబ్యూషన్ 3.0](http://creativecommons.org/licenses/by/3.0/)
625
+
626
+ [create-react-native-library](https://github.com/callstack/react-native-builder-bob)తో తయారు చేయబడింది
627
+
266
628
  ## Spanish
267
629
 
268
630
  ![react-native-audiosprites_poster](react-native-audiosprites_poster.jpg)
@@ -294,6 +656,18 @@ audiosprite --output src/__tests__/sounds/mygameaudio --format howler --loop "bg
294
656
 
295
657
  Este comando generará `mygameaudio.json`, `mygameaudio.mp3`, `mygameaudio.ogg`, `mygameaudio.m4a` y `mygameaudio.ac3` en el directorio `src/__tests__/sounds/`.
296
658
 
659
+ /**
660
+ * ⚠️ SOLUCIÓN DE CONFLICTO DE RECURSOS DE ANDROID ⚠️
661
+ * * El sistema de compilación de Android (Gradle) genera ID de recursos basados en nombres de archivo SIN extensiones.
662
+ * Si generamos "game_go_v2.mp3" y "game_go_v2.json", Android los ve a ambos
663
+ * como el ID de recurso `R.raw.game_go_v2`, lo que causa un error de compilación de "Recursos duplicados".
664
+ * * Para solucionar esto, renombre el archivo JSON generado a:
665
+ * `${outputFileName}_data.json`
666
+ * * Resultado:
667
+ * - game_go_v2.mp3 -> R.raw.game_go_v2
668
+ * - game_go_v2_data.json -> R.raw.game_go_v2_data
669
+ */
670
+
297
671
  ### Sonidos en Bucle
298
672
 
299
673
  Puedes crear sonidos en bucle usando la opción `--loop` con el comando `audiosprite`. El valor de la opción `--loop` debe ser el nombre del sonido que deseas repetir.
@@ -426,7 +800,11 @@ export default function App() {
426
800
  const playSound = (soundName: string) => {
427
801
  const player = playerRef.current;
428
802
  if (player && isLoaded) {
429
- player.play(soundName);
803
+ if (soundName === 'bg_loop') {
804
+ player.play(soundName, { channel: 'music' });
805
+ } else {
806
+ player.play(soundName);
807
+ }
430
808
  console.log(`Reproduciendo sonido: ${soundName}`);
431
809
  } else {
432
810
  console.warn('El reproductor aún no se ha cargado.');
@@ -557,6 +935,18 @@ audiosprite --output src/__tests__/sounds/mygameaudio --format howler --loop "bg
557
935
 
558
936
  Эта команда создаст `mygameaudio.json`, `mygameaudio.mp3`, `mygameaudio.ogg`, `mygameaudio.m4a` и `mygameaudio.ac3` в каталоге `src/__tests__/sounds/`.
559
937
 
938
+ /**
939
+ * ⚠️ ИСПРАВЛЕНИЕ КОНФЛИКТА РЕСУРСОВ ANDROID ⚠️
940
+ * * Система сборки Android (Gradle) генерирует идентификаторы ресурсов на основе имен файлов БЕЗ расширений.
941
+ * Если мы сгенерируем "game_go_v2.mp3" и "game_go_v2.json", Android увидит их обоих
942
+ * как идентификатор ресурса `R.raw.game_go_v2`, что вызовет ошибку сборки "Дублирующиеся ресурсы".
943
+ * * Чтобы исправить это, переименуйте сгенерированный файл JSON в:
944
+ * `${outputFileName}_data.json`
945
+ * * Результат:
946
+ * - game_go_v2.mp3 -> R.raw.game_go_v2
947
+ * - game_go_v2_data.json -> R.raw.game_go_v2_data
948
+ */
949
+
560
950
  ### Зацикленные звуки
561
951
 
562
952
  Вы можете создавать зацикленные звуки, используя опцию `--loop` с командой `audiosprite`. Значением опции `--loop` должно быть имя звука, который вы хотите зациклить.
@@ -689,7 +1079,11 @@ export default function App() {
689
1079
  const playSound = (soundName: string) => {
690
1080
  const player = playerRef.current;
691
1081
  if (player && isLoaded) {
692
- player.play(soundName);
1082
+ if (soundName === 'bg_loop') {
1083
+ player.play(soundName, { channel: 'music' });
1084
+ } else {
1085
+ player.play(soundName);
1086
+ }
693
1087
  console.log(`Воспроизведение звука: ${soundName}`);
694
1088
  } else {
695
1089
  console.warn('Проигрыватель еще не загружен.');
@@ -820,6 +1214,18 @@ audiosprite --output src/__tests__/sounds/mygameaudio --format howler --loop "bg
820
1214
 
821
1215
  यह कमांड `src/__tests__/sounds/` डायरेक्टरी में `mygameaudio.json`, `mygameaudio.mp3`, `mygameaudio.ogg`, `mygameaudio.m4a`, और `mygameaudio.ac3` उत्पन्न करेगा।
822
1216
 
1217
+ /**
1218
+ * ⚠️ Android संसाधन संघर्ष सुधार ⚠️
1219
+ * * Android की बिल्ड सिस्टम (Gradle) फ़ाइल नामों के आधार पर संसाधन ID उत्पन्न करती है बिना एक्सटेंशन के।
1220
+ * यदि हम "game_go_v2.mp3" और "game_go_v2.json" उत्पन्न करते हैं, तो Android उन दोनों को
1221
+ * `R.raw.game_go_v2` संसाधन ID के रूप में देखता है, जिससे "डुप्लिकेट संसाधन" बिल्ड त्रुटि होती है।
1222
+ * * इसे ठीक करने के लिए, उत्पन्न JSON फ़ाइल का नाम बदलकर यह करें:
1223
+ * `${outputFileName}_data.json`
1224
+ * * परिणाम:
1225
+ * - game_go_v2.mp3 -> R.raw.game_go_v2
1226
+ * - game_go_v2_data.json -> R.raw.game_go_v2_data
1227
+ */
1228
+
823
1229
  ### लूपिंग ध्वनियाँ
824
1230
 
825
1231
  आप `audiosprite` कमांड के साथ `--loop` विकल्प का उपयोग करके लूपिंग ध्वनियाँ बना सकते हैं। `--loop` विकल्प का मान उस ध्वनि का नाम होना चाहिए जिसे आप लूप करना चाहते हैं।
@@ -952,7 +1358,11 @@ export default function App() {
952
1358
  const playSound = (soundName: string) => {
953
1359
  const player = playerRef.current;
954
1360
  if (player && isLoaded) {
955
- player.play(soundName);
1361
+ if (soundName === 'bg_loop') {
1362
+ player.play(soundName, { channel: 'music' });
1363
+ } else {
1364
+ player.play(soundName);
1365
+ }
956
1366
  console.log(`ध्वनि बज रही है: ${soundName}`);
957
1367
  } else {
958
1368
  console.warn('खिलाड़ी अभी तक लोड नहीं हुआ है।');
@@ -1083,6 +1493,18 @@ audiosprite --output src/__tests__/sounds/mygameaudio --format howler --loop "bg
1083
1493
 
1084
1494
  此命令将在 `src/__tests__/sounds/` 目录中生成 `mygameaudio.json`、`mygameaudio.mp3`、`mygameaudio.ogg`、`mygameaudio.m4a` 和 `mygameaudio.ac3`。
1085
1495
 
1496
+ /**
1497
+ * ⚠️ ANDROID 资源冲突修复 ⚠️
1498
+ * * Android 的构建系统 (Gradle) 基于不带扩展名的文件名生成资源 ID。
1499
+ * 如果我们生成 "game_go_v2.mp3" 和 "game_go_v2.json",Android 会将它们都视为
1500
+ * 资源 ID `R.raw.game_go_v2`,从而导致“重复资源”构建错误。
1501
+ * * 要修复此问题,请将生成的 JSON 文件重命名为:
1502
+ * `${outputFileName}_data.json`
1503
+ * * 结果:
1504
+ * - game_go_v2.mp3 -> R.raw.game_go_v2
1505
+ * - game_go_v2_data.json -> R.raw.game_go_v2_data
1506
+ */
1507
+
1086
1508
  ### 循环声音
1087
1509
 
1088
1510
  您可以使用 `audiosprite` 命令的 `--loop` 选项创建循环声音。`--loop` 选项的值应该是您想要循环的声音的名称。
@@ -1215,7 +1637,11 @@ export default function App() {
1215
1637
  const playSound = (soundName: string) => {
1216
1638
  const player = playerRef.current;
1217
1639
  if (player && isLoaded) {
1218
- player.play(soundName);
1640
+ if (soundName === 'bg_loop') {
1641
+ player.play(soundName, { channel: 'music' });
1642
+ } else {
1643
+ player.play(soundName);
1644
+ }
1219
1645
  console.log(`正在播放声音: ${soundName}`);
1220
1646
  } else {
1221
1647
  console.warn('播放器尚未加载。');
@@ -1313,4 +1739,4 @@ key1: [offset, duration, (loop)]
1313
1739
 
1314
1740
  [Shaker, Woda, Conga, Bongo, Templeblock.wav](https://freesound.org/people/kwazi/sounds/34115/) by [kwazi](https://freesound.org/people/kwazi/) | 执照: [署名 3.0](http://creativecommons.org/licenses/by/3.0/)
1315
1741
 
1316
- 使用 [create-react-native-library](https://github.com/callstack/react-native-builder-bob) 制作
1742
+ 使用 [create-react-native-library](https://github.com/callstack/react-native-builder-bob) 制作
@@ -5,12 +5,28 @@
5
5
  * Requires an AudioContext and fetch to be injected.
6
6
  * Uses AudioBufferQueueSourceNode and buffer splitting for mobile stability.
7
7
  */
8
+
9
+ /**
10
+ * Defines the available audio routing channels.
11
+ * - 'sfx': Short sound effects (coins, jumps, UI). High priority, usually usually non-looping.
12
+ * - 'music': Background tracks. Lower priority, usually looping, separate volume control.
13
+ */
14
+
15
+ /**
16
+ * Configuration options for playing a specific sound sprite.
17
+ */
18
+
8
19
  export class AudioSpritePlayer {
9
20
  // The full audio buffer
10
21
 
11
22
  // Cache for the small, pre-split AudioBuffers used by mobile's QueueSourceNode
12
23
  spriteBufferCache = {};
13
24
  loopingSource = null;
25
+ sourcePool = []; // NEW: Pool for non-looping sources
26
+ maxPoolSize = 5; // Adjust this based on testing (5 is a good start)
27
+
28
+ // NEW: The Mixer
29
+
14
30
  constructor({
15
31
  audioContext,
16
32
  fetch,
@@ -40,6 +56,20 @@ export class AudioSpritePlayer {
40
56
  // @ts-ignore
41
57
  this.audioContext = new AudioContext();
42
58
  }
59
+
60
+ // 1. Create Master Output
61
+ this.masterGain = this.audioContext.createGain();
62
+ this.masterGain.gain.value = 1.0;
63
+ this.masterGain.connect(this.audioContext.destination);
64
+
65
+ // 2. Create Channels
66
+ this.sfxGain = this.audioContext.createGain();
67
+ this.sfxGain.gain.value = 1.0;
68
+ this.sfxGain.connect(this.masterGain); // Connect to Master
69
+
70
+ this.musicGain = this.audioContext.createGain();
71
+ this.musicGain.gain.value = 1.0;
72
+ this.musicGain.connect(this.masterGain); // Connect to Master
43
73
  }
44
74
 
45
75
  /**
@@ -100,6 +130,59 @@ export class AudioSpritePlayer {
100
130
  // console.log(`Cached sprite buffer for ${soundName}, frames: ${durationFrames}`);
101
131
  }
102
132
  }
133
+
134
+ /**
135
+ * Retrieves a source node from the pool or creates a new one if the pool is under capacity.
136
+ */
137
+ _getOrCreateSourceNode() {
138
+ if (this.platform === 'web') {
139
+ // Web logic remains simple: always create a standard AudioBufferSourceNode
140
+ const source = this.audioContext.createBufferSource();
141
+ // source.connect(this.audioContext.destination);
142
+ return source;
143
+ }
144
+
145
+ // Mobile/QueueSourceNode logic
146
+ let source;
147
+ if (this.sourcePool.length > 0) {
148
+ source = this.sourcePool.pop(); // Reuse an available source
149
+ // Disconnect the previous onEnded handler if it had one
150
+ source.onEnded = null;
151
+ // console.log('Reusing source from pool. Pool size:', this.sourcePool.length);
152
+ } else if (this.sourcePool.length < this.maxPoolSize) {
153
+ // Create a new source if pool is not full
154
+ if (!this.audioContext.createBufferQueueSource) {
155
+ console.error('RNAS Error: createBufferQueueSource is not available. Cannot create pool.');
156
+ return null;
157
+ }
158
+ source = this.audioContext.createBufferQueueSource();
159
+ // source.connect(this.audioContext.destination);
160
+ // console.log('Created new source. Pool size:', this.sourcePool.length);
161
+ } else {
162
+ // If pool is full, we might have to block or fail.
163
+ // For simplicity, we'll return null and warn, but a robust system
164
+ // might implement a "wait-and-retry" mechanism.
165
+ console.warn('RNAS Warning: Source pool is full. Dropping non-looping sound.');
166
+ return null;
167
+ }
168
+
169
+ // Define the cleanup/recycling function
170
+ const cleanupAndRecycle = () => {
171
+ // Only recycle if we are under maxPoolSize and it's not the looping source
172
+ if (this.sourcePool.length < this.maxPoolSize) {
173
+ this.sourcePool.push(source);
174
+ // console.log('Recycled source. Pool size:', this.sourcePool.length);
175
+ } else {
176
+ // If pool is full, let it be garbage collected
177
+ source.disconnect();
178
+ // console.log('Source disconnected (GC candidate).');
179
+ }
180
+ };
181
+
182
+ // Assign the cleanup function to run when the sound finishes playing
183
+ source.onEnded = cleanupAndRecycle;
184
+ return source;
185
+ }
103
186
  async load(json, audio) {
104
187
  try {
105
188
  let decodedBuffer;
@@ -157,7 +240,7 @@ export class AudioSpritePlayer {
157
240
  throw error; // Re-throw for user to catch
158
241
  }
159
242
  }
160
- play(soundName) {
243
+ play(soundName, options) {
161
244
  if (!this.audioBuffer || !this.manifest) {
162
245
  console.warn('Audio sprite not loaded. Call load() first.');
163
246
  return;
@@ -179,6 +262,15 @@ export class AudioSpritePlayer {
179
262
  console.warn(`Sound "${soundName}" has invalid duration.`);
180
263
  return;
181
264
  }
265
+
266
+ // 1. Determine Channel (Default to 'sfx')
267
+ const channel = options?.channel || 'sfx';
268
+ const targetNode = channel === 'music' ? this.musicGain : this.sfxGain;
269
+
270
+ // 2. Determine Loop (Options override Manifest)
271
+ // defined in your existing code as: const sound = this.manifest.sprite[soundName];
272
+ // const manifestLoop = sound[2];
273
+ const loop = options?.loop !== undefined ? options.loop : sound[2];
182
274
  let source;
183
275
  const spriteBuffer = this.spriteBufferCache[soundName];
184
276
 
@@ -192,12 +284,12 @@ export class AudioSpritePlayer {
192
284
  console.error('RNAS Error: createBufferQueueSource is not available on this native platform.');
193
285
  return;
194
286
  }
195
- const loop = sound[2];
196
287
  if (loop) {
197
288
  // Always use AudioBufferQueueSourceNode
198
289
  source = this.audioContext.createBufferQueueSource();
199
290
  source.enqueueBuffer(spriteBuffer);
200
- source.connect(this.audioContext.destination);
291
+ // source.connect(this.audioContext.destination);
292
+ source.connect(targetNode);
201
293
 
202
294
  // Manual looping using onEnded
203
295
  const loopHandler = () => {
@@ -217,12 +309,19 @@ export class AudioSpritePlayer {
217
309
  source.start(0); // Start immediately
218
310
  this.loopingSource = source; // Store reference to looping source
219
311
  } else {
220
- // For non-looping sounds on mobile, use AudioBufferQueueSourceNode
221
- source = this.audioContext.createBufferQueueSource();
312
+ // **NEW: USE POOL FOR NON-LOOPING MOBILE SOUNDS**
313
+ source = this._getOrCreateSourceNode();
314
+ if (!source) return; // Dropped sound because pool was full
315
+
316
+ // Reconnect to the correct channel (since pooled nodes might have been connected elsewhere)
317
+ source.disconnect();
318
+ source.connect(targetNode);
319
+
320
+ // Must re-enqueue the buffer since the source was reused
222
321
  source.enqueueBuffer(spriteBuffer);
223
- source.connect(this.audioContext.destination);
224
- source.start(1);
225
- console.log('non loop', soundName);
322
+
323
+ // Start immediately (start time 0 for BufferQueueSourceNode means "as soon as possible")
324
+ source.start(0);
226
325
  }
227
326
  } else {
228
327
  // 🌐 WEB LOGIC (Standard Web Audio API)
@@ -232,8 +331,10 @@ export class AudioSpritePlayer {
232
331
  return;
233
332
  }
234
333
  source.buffer = spriteBuffer;
235
- source.connect(this.audioContext.destination);
236
- const loop = sound[2]; // audiosprite stores loop as the third element in the array
334
+ // source.connect(this.audioContext.destination);
335
+ source.connect(targetNode);
336
+
337
+ // const loop = sound[2]; // audiosprite stores loop as the third element in the array
237
338
  if (loop) {
238
339
  source.loop = true;
239
340
  source.loopStart = 0; // Relative to the spriteBuffer
@@ -277,5 +378,41 @@ export class AudioSpritePlayer {
277
378
  console.log('RNAS: No looping audio to stop.');
278
379
  }
279
380
  }
381
+
382
+ /**
383
+ * Sets the volume for background music only.
384
+ * @param value 0.0 to 1.0
385
+ */
386
+ setMusicVolume(value) {
387
+ if (this.musicGain) {
388
+ // secure against uninitialized context
389
+ this.musicGain.gain.setTargetAtTime(value, this.audioContext.currentTime, 0.02);
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Sets the volume for sound effects only.
395
+ * @param value 0.0 to 1.0
396
+ */
397
+ setSFXVolume(value) {
398
+ if (this.sfxGain) {
399
+ this.sfxGain.gain.setTargetAtTime(value, this.audioContext.currentTime, 0.02);
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Sets the Global Master Volume (affects both Music and SFX).
405
+ * @param value 0.0 to 1.0
406
+ */
407
+ set volume(value) {
408
+ if (this.masterGain) {
409
+ this.masterGain.gain.setTargetAtTime(value, this.audioContext.currentTime, 0.02);
410
+ }
411
+ }
412
+
413
+ // Master Volume getter
414
+ get volume() {
415
+ return this.masterGain?.gain?.value ?? 1.0;
416
+ }
280
417
  }
281
418
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
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","typedArrayView","Uint8Array","buffer","error","play","state","resume","catch","e","duration","source","createBufferQueueSource","loop","enqueueBuffer","connect","destination","loopHandler","start","onEnded","stop","loopStart","loopEnd","getManifest","getAudioBuffer"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["AudioSpritePlayer","spriteBufferCache","loopingSource","sourcePool","maxPoolSize","constructor","audioContext","fetch","platform","AudioContext","Error","audioBuffer","manifest","createBufferSource","name","console","log","masterGain","createGain","gain","value","connect","destination","sfxGain","musicGain","_cacheSpriteBuffers","sampleRate","numChannels","numberOfChannels","soundName","sprite","sound","startFrame","Math","floor","endFrame","ceil","durationFrames","warn","spriteBuffer","createBuffer","i","sourceData","getChannelData","destinationData","segment","subarray","set","_getOrCreateSourceNode","source","length","pop","onEnded","createBufferQueueSource","error","cleanupAndRecycle","push","disconnect","load","json","audio","decodedBuffer","response","ok","statusText","urls","audioFileName","audioUrl","URL","url","href","audioResponse","arrayBuffer","decodeAudioData","typedArrayView","Uint8Array","buffer","play","options","state","resume","catch","e","duration","channel","targetNode","loop","undefined","enqueueBuffer","loopHandler","start","stop","loopStart","loopEnd","getManifest","getAudioBuffer","setMusicVolume","setTargetAtTime","currentTime","setSFXVolume","volume"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -3,6 +3,28 @@
3
3
  * Requires an AudioContext and fetch to be injected.
4
4
  * Uses AudioBufferQueueSourceNode and buffer splitting for mobile stability.
5
5
  */
6
+ /**
7
+ * Defines the available audio routing channels.
8
+ * - 'sfx': Short sound effects (coins, jumps, UI). High priority, usually usually non-looping.
9
+ * - 'music': Background tracks. Lower priority, usually looping, separate volume control.
10
+ */
11
+ export type AudioChannel = 'sfx' | 'music';
12
+ /**
13
+ * Configuration options for playing a specific sound sprite.
14
+ */
15
+ export interface PlayOptions {
16
+ /**
17
+ * Specifies which mixer channel to route the audio through.
18
+ * Use 'music' for BGM to ensure it responds to music volume settings.
19
+ * @default 'sfx'
20
+ */
21
+ channel?: AudioChannel;
22
+ /**
23
+ * Force the sound to loop (or not), overriding the setting
24
+ * defined in the original audiosprite JSON manifest.
25
+ */
26
+ loop?: boolean;
27
+ }
6
28
  export declare class AudioSpritePlayer {
7
29
  audioContext: any | null;
8
30
  fetch: any | null;
@@ -11,6 +33,11 @@ export declare class AudioSpritePlayer {
11
33
  platform: string;
12
34
  private spriteBufferCache;
13
35
  private loopingSource;
36
+ private sourcePool;
37
+ private maxPoolSize;
38
+ masterGain: any;
39
+ sfxGain: any;
40
+ musicGain: any;
14
41
  constructor({ audioContext, fetch, platform, }: {
15
42
  audioContext: any | null;
16
43
  fetch: any;
@@ -40,8 +67,12 @@ export declare class AudioSpritePlayer {
40
67
  * }
41
68
  */
42
69
  private _cacheSpriteBuffers;
70
+ /**
71
+ * Retrieves a source node from the pool or creates a new one if the pool is under capacity.
72
+ */
73
+ private _getOrCreateSourceNode;
43
74
  load(json: any, audio?: any): Promise<void>;
44
- play(soundName: string): void;
75
+ play(soundName: string, options?: PlayOptions): void;
45
76
  getManifest(): any;
46
77
  getAudioBuffer(): any;
47
78
  /**
@@ -49,5 +80,21 @@ export declare class AudioSpritePlayer {
49
80
  * If a looping sound is playing, it will be stopped immediately.
50
81
  */
51
82
  stop(): void;
83
+ /**
84
+ * Sets the volume for background music only.
85
+ * @param value 0.0 to 1.0
86
+ */
87
+ setMusicVolume(value: number): void;
88
+ /**
89
+ * Sets the volume for sound effects only.
90
+ * @param value 0.0 to 1.0
91
+ */
92
+ setSFXVolume(value: number): void;
93
+ /**
94
+ * Sets the Global Master Volume (affects both Music and SFX).
95
+ * @param value 0.0 to 1.0
96
+ */
97
+ set volume(value: number);
98
+ get volume(): number;
52
99
  }
53
100
  //# 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;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;IA0EjC,IAAI,CAAC,SAAS,EAAE,MAAM;IAoHtB,WAAW;IAIX,cAAc;IAId;;;OAGG;IACH,IAAI;CASL"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,OAAO,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,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;IACzC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAAa;IAGzB,UAAU,EAAE,GAAG,CAAC;IAChB,OAAO,EAAE,GAAG,CAAC;IACb,SAAS,EAAE,GAAG,CAAC;gBAEV,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;IAgDD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,mBAAmB;IA8C3B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAuDxB,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG;IA0EjC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW;IAoI7C,WAAW;IAIX,cAAc;IAId;;;OAGG;IACH,IAAI;IAUJ;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM;IAW5B;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM;IAU1B;;;OAGG;IACH,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAQvB;IAGD,IAAI,MAAM,IAAI,MAAM,CAEnB;CACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-audiosprites",
3
- "version": "0.4.0",
4
- "description": "audio sprites ",
3
+ "version": "0.5.0",
4
+ "description": "audio sprites for react native ios, android and web",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
7
7
  "exports": {
@@ -36,6 +36,8 @@
36
36
  "typecheck": "tsc",
37
37
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
38
38
  "clean": "del-cli lib",
39
+ "types:sync": "node scripts/generate-audio-types.mjs",
40
+ "prebuild": "npm run types:sync",
39
41
  "prepare": "bob build",
40
42
  "release:alpha": "release-it --preRelease=alpha",
41
43
  "release": "release-it --only-version",
@@ -81,11 +83,13 @@
81
83
  "@eslint/eslintrc": "^3.3.1",
82
84
  "@eslint/js": "^9.35.0",
83
85
  "@evilmartians/lefthook": "^1.12.3",
86
+ "@react-native-community/slider": "^5.1.1",
84
87
  "@react-native/babel-preset": "0.81.1",
85
88
  "@react-native/eslint-config": "^0.81.1",
86
89
  "@release-it/conventional-changelog": "^10.0.1",
87
90
  "@types/jest": "^29.5.14",
88
91
  "@types/react": "^19.1.12",
92
+ "arktype": "^2.1.29",
89
93
  "commitlint": "^19.8.1",
90
94
  "del-cli": "^6.0.0",
91
95
  "eslint": "^9.35.0",
@@ -95,9 +99,10 @@
95
99
  "prettier": "^3.6.2",
96
100
  "react": "19.1.0",
97
101
  "react-native": "0.81.5",
98
- "react-native-audio-api": "^0.9.3",
102
+ "react-native-audio-api": "0.10.1",
99
103
  "react-native-builder-bob": "^0.40.13",
100
104
  "release-it": "^19.0.4",
105
+ "ts-morph": "^27.0.2",
101
106
  "typescript": "^5.9.2"
102
107
  },
103
108
  "peerDependencies": {
package/src/index.tsx CHANGED
@@ -3,6 +3,32 @@
3
3
  * Requires an AudioContext and fetch to be injected.
4
4
  * Uses AudioBufferQueueSourceNode and buffer splitting for mobile stability.
5
5
  */
6
+
7
+ /**
8
+ * Defines the available audio routing channels.
9
+ * - 'sfx': Short sound effects (coins, jumps, UI). High priority, usually usually non-looping.
10
+ * - 'music': Background tracks. Lower priority, usually looping, separate volume control.
11
+ */
12
+ export type AudioChannel = 'sfx' | 'music';
13
+
14
+ /**
15
+ * Configuration options for playing a specific sound sprite.
16
+ */
17
+ export interface PlayOptions {
18
+ /**
19
+ * Specifies which mixer channel to route the audio through.
20
+ * Use 'music' for BGM to ensure it responds to music volume settings.
21
+ * @default 'sfx'
22
+ */
23
+ channel?: AudioChannel;
24
+
25
+ /**
26
+ * Force the sound to loop (or not), overriding the setting
27
+ * defined in the original audiosprite JSON manifest.
28
+ */
29
+ loop?: boolean;
30
+ }
31
+
6
32
  export class AudioSpritePlayer {
7
33
  audioContext: any | null;
8
34
  fetch: any | null;
@@ -12,6 +38,13 @@ export class AudioSpritePlayer {
12
38
  // Cache for the small, pre-split AudioBuffers used by mobile's QueueSourceNode
13
39
  private spriteBufferCache: Record<string, any> = {};
14
40
  private loopingSource: any | null = null;
41
+ private sourcePool: any[] = []; // NEW: Pool for non-looping sources
42
+ private maxPoolSize: number = 5; // Adjust this based on testing (5 is a good start)
43
+
44
+ // NEW: The Mixer
45
+ public masterGain: any;
46
+ public sfxGain: any;
47
+ public musicGain: any;
15
48
 
16
49
  constructor({
17
50
  audioContext,
@@ -53,6 +86,20 @@ export class AudioSpritePlayer {
53
86
  // @ts-ignore
54
87
  this.audioContext = new AudioContext();
55
88
  }
89
+
90
+ // 1. Create Master Output
91
+ this.masterGain = this.audioContext.createGain();
92
+ this.masterGain.gain.value = 1.0;
93
+ this.masterGain.connect(this.audioContext.destination);
94
+
95
+ // 2. Create Channels
96
+ this.sfxGain = this.audioContext.createGain();
97
+ this.sfxGain.gain.value = 1.0;
98
+ this.sfxGain.connect(this.masterGain); // Connect to Master
99
+
100
+ this.musicGain = this.audioContext.createGain();
101
+ this.musicGain.gain.value = 1.0;
102
+ this.musicGain.connect(this.masterGain); // Connect to Master
56
103
  }
57
104
 
58
105
  /**
@@ -124,6 +171,64 @@ export class AudioSpritePlayer {
124
171
  }
125
172
  }
126
173
 
174
+ /**
175
+ * Retrieves a source node from the pool or creates a new one if the pool is under capacity.
176
+ */
177
+ private _getOrCreateSourceNode(): any {
178
+ if (this.platform === 'web') {
179
+ // Web logic remains simple: always create a standard AudioBufferSourceNode
180
+ const source = this.audioContext.createBufferSource();
181
+ // source.connect(this.audioContext.destination);
182
+ return source;
183
+ }
184
+
185
+ // Mobile/QueueSourceNode logic
186
+ let source;
187
+ if (this.sourcePool.length > 0) {
188
+ source = this.sourcePool.pop(); // Reuse an available source
189
+ // Disconnect the previous onEnded handler if it had one
190
+ source.onEnded = null;
191
+ // console.log('Reusing source from pool. Pool size:', this.sourcePool.length);
192
+ } else if (this.sourcePool.length < this.maxPoolSize) {
193
+ // Create a new source if pool is not full
194
+ if (!this.audioContext.createBufferQueueSource) {
195
+ console.error(
196
+ 'RNAS Error: createBufferQueueSource is not available. Cannot create pool.'
197
+ );
198
+ return null;
199
+ }
200
+ source = this.audioContext.createBufferQueueSource();
201
+ // source.connect(this.audioContext.destination);
202
+ // console.log('Created new source. Pool size:', this.sourcePool.length);
203
+ } else {
204
+ // If pool is full, we might have to block or fail.
205
+ // For simplicity, we'll return null and warn, but a robust system
206
+ // might implement a "wait-and-retry" mechanism.
207
+ console.warn(
208
+ 'RNAS Warning: Source pool is full. Dropping non-looping sound.'
209
+ );
210
+ return null;
211
+ }
212
+
213
+ // Define the cleanup/recycling function
214
+ const cleanupAndRecycle = () => {
215
+ // Only recycle if we are under maxPoolSize and it's not the looping source
216
+ if (this.sourcePool.length < this.maxPoolSize) {
217
+ this.sourcePool.push(source);
218
+ // console.log('Recycled source. Pool size:', this.sourcePool.length);
219
+ } else {
220
+ // If pool is full, let it be garbage collected
221
+ source.disconnect();
222
+ // console.log('Source disconnected (GC candidate).');
223
+ }
224
+ };
225
+
226
+ // Assign the cleanup function to run when the sound finishes playing
227
+ source.onEnded = cleanupAndRecycle;
228
+
229
+ return source;
230
+ }
231
+
127
232
  async load(json: any, audio?: any) {
128
233
  try {
129
234
  let decodedBuffer: any;
@@ -198,7 +303,7 @@ export class AudioSpritePlayer {
198
303
  }
199
304
  }
200
305
 
201
- play(soundName: string) {
306
+ play(soundName: string, options?: PlayOptions) {
202
307
  if (!this.audioBuffer || !this.manifest) {
203
308
  console.warn('Audio sprite not loaded. Call load() first.');
204
309
  return;
@@ -223,6 +328,15 @@ export class AudioSpritePlayer {
223
328
  return;
224
329
  }
225
330
 
331
+ // 1. Determine Channel (Default to 'sfx')
332
+ const channel = options?.channel || 'sfx';
333
+ const targetNode = channel === 'music' ? this.musicGain : this.sfxGain;
334
+
335
+ // 2. Determine Loop (Options override Manifest)
336
+ // defined in your existing code as: const sound = this.manifest.sprite[soundName];
337
+ // const manifestLoop = sound[2];
338
+ const loop = options?.loop !== undefined ? options.loop : sound[2];
339
+
226
340
  let source: any;
227
341
  const spriteBuffer = this.spriteBufferCache[soundName];
228
342
 
@@ -242,13 +356,12 @@ export class AudioSpritePlayer {
242
356
  return;
243
357
  }
244
358
 
245
- const loop = sound[2];
246
-
247
359
  if (loop) {
248
360
  // Always use AudioBufferQueueSourceNode
249
361
  source = this.audioContext.createBufferQueueSource();
250
362
  source.enqueueBuffer(spriteBuffer);
251
- source.connect(this.audioContext.destination);
363
+ // source.connect(this.audioContext.destination);
364
+ source.connect(targetNode);
252
365
 
253
366
  // Manual looping using onEnded
254
367
  const loopHandler = () => {
@@ -268,12 +381,19 @@ export class AudioSpritePlayer {
268
381
  source.start(0); // Start immediately
269
382
  this.loopingSource = source; // Store reference to looping source
270
383
  } else {
271
- // For non-looping sounds on mobile, use AudioBufferQueueSourceNode
272
- source = this.audioContext.createBufferQueueSource();
384
+ // **NEW: USE POOL FOR NON-LOOPING MOBILE SOUNDS**
385
+ source = this._getOrCreateSourceNode();
386
+ if (!source) return; // Dropped sound because pool was full
387
+
388
+ // Reconnect to the correct channel (since pooled nodes might have been connected elsewhere)
389
+ source.disconnect();
390
+ source.connect(targetNode);
391
+
392
+ // Must re-enqueue the buffer since the source was reused
273
393
  source.enqueueBuffer(spriteBuffer);
274
- source.connect(this.audioContext.destination);
275
- source.start(1);
276
- console.log('non loop', soundName);
394
+
395
+ // Start immediately (start time 0 for BufferQueueSourceNode means "as soon as possible")
396
+ source.start(0);
277
397
  }
278
398
  } else {
279
399
  // 🌐 WEB LOGIC (Standard Web Audio API)
@@ -287,9 +407,10 @@ export class AudioSpritePlayer {
287
407
  }
288
408
 
289
409
  source.buffer = spriteBuffer;
290
- source.connect(this.audioContext.destination);
410
+ // source.connect(this.audioContext.destination);
411
+ source.connect(targetNode);
291
412
 
292
- const loop = sound[2]; // audiosprite stores loop as the third element in the array
413
+ // const loop = sound[2]; // audiosprite stores loop as the third element in the array
293
414
  if (loop) {
294
415
  source.loop = true;
295
416
  source.loopStart = 0; // Relative to the spriteBuffer
@@ -335,4 +456,52 @@ export class AudioSpritePlayer {
335
456
  console.log('RNAS: No looping audio to stop.');
336
457
  }
337
458
  }
459
+
460
+ /**
461
+ * Sets the volume for background music only.
462
+ * @param value 0.0 to 1.0
463
+ */
464
+ setMusicVolume(value: number) {
465
+ if (this.musicGain) {
466
+ // secure against uninitialized context
467
+ this.musicGain.gain.setTargetAtTime(
468
+ value,
469
+ this.audioContext.currentTime,
470
+ 0.02
471
+ );
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Sets the volume for sound effects only.
477
+ * @param value 0.0 to 1.0
478
+ */
479
+ setSFXVolume(value: number) {
480
+ if (this.sfxGain) {
481
+ this.sfxGain.gain.setTargetAtTime(
482
+ value,
483
+ this.audioContext.currentTime,
484
+ 0.02
485
+ );
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Sets the Global Master Volume (affects both Music and SFX).
491
+ * @param value 0.0 to 1.0
492
+ */
493
+ set volume(value: number) {
494
+ if (this.masterGain) {
495
+ this.masterGain.gain.setTargetAtTime(
496
+ value,
497
+ this.audioContext.currentTime,
498
+ 0.02
499
+ );
500
+ }
501
+ }
502
+
503
+ // Master Volume getter
504
+ get volume(): number {
505
+ return this.masterGain?.gain?.value ?? 1.0;
506
+ }
338
507
  }