@capgo/native-audio 6.4.4 → 6.4.6

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.
@@ -12,7 +12,7 @@ public class AudioAsset {
12
12
  private final ArrayList<AudioDispatcher> audioList;
13
13
  private int playIndex = 0;
14
14
  private final String assetId;
15
- private final NativeAudio owner;
15
+ protected final NativeAudio owner;
16
16
 
17
17
  AudioAsset(
18
18
  NativeAudio owner,
@@ -29,7 +29,11 @@ import com.getcapacitor.PluginMethod;
29
29
  import com.getcapacitor.annotation.CapacitorPlugin;
30
30
  import com.getcapacitor.annotation.Permission;
31
31
  import java.io.File;
32
+ import java.io.FileDescriptor;
33
+ import java.io.FileInputStream;
34
+ import java.io.IOException;
32
35
  import java.net.URI;
36
+ import java.net.URL;
33
37
  import java.util.ArrayList;
34
38
  import java.util.HashMap;
35
39
 
@@ -448,88 +452,132 @@ public class NativeAudio
448
452
  initSoundPool();
449
453
 
450
454
  String audioId = call.getString(ASSET_ID);
451
-
452
- boolean isLocalUrl = Boolean.TRUE.equals(call.getBoolean("isUrl", false));
453
-
454
455
  if (!isStringValid(audioId)) {
455
456
  call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
456
457
  return;
457
458
  }
458
459
 
459
- if (!audioAssetList.containsKey(audioId)) {
460
- String assetPath = call.getString(ASSET_PATH);
460
+ String assetPath = call.getString(ASSET_PATH);
461
+ if (!isStringValid(assetPath)) {
462
+ call.reject(
463
+ ERROR_ASSET_PATH_MISSING + " - " + audioId + " - " + assetPath
464
+ );
465
+ return;
466
+ }
461
467
 
462
- if (!isStringValid(assetPath)) {
463
- call.reject(
464
- ERROR_ASSET_PATH_MISSING + " - " + audioId + " - " + assetPath
465
- );
466
- return;
467
- }
468
+ boolean isLocalUrl = call.getBoolean("isUrl", false);
469
+ boolean isComplex = call.getBoolean("isComplex", false);
468
470
 
469
- String fullPath = assetPath; //"raw/".concat(assetPath);
471
+ Log.d(
472
+ "AudioPlugin",
473
+ "Debug: audioId = " +
474
+ audioId +
475
+ ", assetPath = " +
476
+ assetPath +
477
+ ", isLocalUrl = " +
478
+ isLocalUrl
479
+ );
470
480
 
481
+ if (audioAssetList.containsKey(audioId)) {
482
+ call.reject(ERROR_AUDIO_EXISTS + " - " + audioId);
483
+ return;
484
+ }
485
+
486
+ if (isComplex) {
471
487
  volume = call.getFloat(VOLUME, 1F);
472
488
  audioChannelNum = call.getInt(AUDIO_CHANNEL_NUM, 1);
489
+ }
473
490
 
474
- AssetFileDescriptor assetFileDescriptor;
475
- if (isLocalUrl) {
476
- File f = new File(new URI(fullPath));
477
- ParcelFileDescriptor p = ParcelFileDescriptor.open(
478
- f,
479
- ParcelFileDescriptor.MODE_READ_ONLY
480
- );
481
- assetFileDescriptor = new AssetFileDescriptor(p, 0, -1);
482
- } else {
483
- try {
484
- Uri uri = Uri.parse(fullPath); // Now Uri class should be recognized
485
- if (
486
- uri.getScheme() != null &&
487
- (uri.getScheme().equals("http") ||
488
- uri.getScheme().equals("https"))
489
- ) {
490
- // It's a remote URL
491
- RemoteAudioAsset remoteAudioAsset = new RemoteAudioAsset(
492
- this,
493
- audioId,
494
- uri,
495
- audioChannelNum,
496
- volume
491
+ if (isLocalUrl) {
492
+ // Handle URL (both remote and local file URLs)
493
+ Log.d("AudioPlugin", "Debug: Handling URL");
494
+ try {
495
+ Uri uri = Uri.parse(assetPath);
496
+ if (
497
+ uri.getScheme() != null &&
498
+ (uri.getScheme().equals("http") || uri.getScheme().equals("https"))
499
+ ) {
500
+ // Remote URL
501
+ Log.d(
502
+ "AudioPlugin",
503
+ "Debug: Remote URL detected: " + uri.toString()
504
+ );
505
+ RemoteAudioAsset remoteAudioAsset = new RemoteAudioAsset(
506
+ this,
507
+ audioId,
508
+ uri,
509
+ audioChannelNum,
510
+ volume
511
+ );
512
+ audioAssetList.put(audioId, remoteAudioAsset);
513
+ } else if (
514
+ uri.getScheme() != null && uri.getScheme().equals("file")
515
+ ) {
516
+ // Local file URL
517
+ Log.d("AudioPlugin", "Debug: Local file URL detected");
518
+ File file = new File(uri.getPath());
519
+ if (!file.exists()) {
520
+ Log.e(
521
+ "AudioPlugin",
522
+ "Error: File does not exist - " + file.getAbsolutePath()
497
523
  );
498
- audioAssetList.put(audioId, remoteAudioAsset);
499
- call.resolve(status);
524
+ call.reject(ERROR_ASSET_PATH_MISSING + " - " + assetPath);
500
525
  return;
501
- } else {
502
- // It's a local file path
503
- // Check if fullPath starts with "public/" and prepend if necessary
504
- if (!fullPath.startsWith("public/")) {
505
- fullPath = "public/".concat(fullPath);
506
- }
507
- Context ctx = getContext().getApplicationContext(); // Use getContext() directly
508
- AssetManager am = ctx.getResources().getAssets();
509
- // Remove the redefinition of assetFileDescriptor
510
- assetFileDescriptor = am.openFd(fullPath);
511
526
  }
512
- } catch (Exception e) {
513
- call.reject("Error loading audio", e);
514
- return;
527
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
528
+ file,
529
+ ParcelFileDescriptor.MODE_READ_ONLY
530
+ );
531
+ AssetFileDescriptor afd = new AssetFileDescriptor(
532
+ pfd,
533
+ 0,
534
+ AssetFileDescriptor.UNKNOWN_LENGTH
535
+ );
536
+ AudioAsset asset = new AudioAsset(
537
+ this,
538
+ audioId,
539
+ afd,
540
+ audioChannelNum,
541
+ volume
542
+ );
543
+ audioAssetList.put(audioId, asset);
544
+ } else {
545
+ throw new IllegalArgumentException(
546
+ "Invalid URL scheme: " + uri.getScheme()
547
+ );
515
548
  }
549
+ call.resolve(status);
550
+ } catch (Exception e) {
551
+ Log.e("AudioPlugin", "Error handling URL", e);
552
+ call.reject("Error handling URL: " + e.getMessage());
516
553
  }
517
-
518
- AudioAsset asset = new AudioAsset(
519
- this,
520
- audioId,
521
- assetFileDescriptor,
522
- audioChannelNum,
523
- volume
524
- );
525
- audioAssetList.put(audioId, asset);
526
-
527
- call.resolve(status);
528
554
  } else {
529
- call.reject(ERROR_AUDIO_EXISTS);
555
+ // Handle asset in public folder
556
+ Log.d("AudioPlugin", "Debug: Handling asset in public folder");
557
+ if (!assetPath.startsWith("public/")) {
558
+ assetPath = "public/" + assetPath;
559
+ }
560
+ try {
561
+ Context ctx = getContext().getApplicationContext();
562
+ AssetManager am = ctx.getResources().getAssets();
563
+ AssetFileDescriptor assetFileDescriptor = am.openFd(assetPath);
564
+ AudioAsset asset = new AudioAsset(
565
+ this,
566
+ audioId,
567
+ assetFileDescriptor,
568
+ audioChannelNum,
569
+ volume
570
+ );
571
+ audioAssetList.put(audioId, asset);
572
+ call.resolve(status);
573
+ } catch (IOException e) {
574
+ Log.e("AudioPlugin", "Error opening asset: " + assetPath, e);
575
+ call.reject(ERROR_ASSET_PATH_MISSING + " - " + assetPath);
576
+ }
530
577
  }
531
578
  } catch (Exception ex) {
532
- call.reject(ex.getMessage());
579
+ Log.e("AudioPlugin", "Error in preloadAsset", ex);
580
+ call.reject("Error in preloadAsset: " + ex.getMessage());
533
581
  }
534
582
  }
535
583
 
@@ -2,10 +2,17 @@ package ee.forgr.audio;
2
2
 
3
3
  import android.media.MediaPlayer;
4
4
  import android.net.Uri;
5
+ import android.util.Log;
6
+ import java.util.ArrayList;
5
7
 
6
8
  public class RemoteAudioAsset extends AudioAsset {
7
9
 
8
- private MediaPlayer mediaPlayer;
10
+ private static final String TAG = "RemoteAudioAsset";
11
+ private final ArrayList<MediaPlayer> mediaPlayers;
12
+ private int playIndex = 0;
13
+ private final Uri uri;
14
+ private float volume;
15
+ private boolean isPrepared = false;
9
16
 
10
17
  public RemoteAudioAsset(
11
18
  NativeAudio owner,
@@ -14,74 +21,155 @@ public class RemoteAudioAsset extends AudioAsset {
14
21
  int audioChannelNum,
15
22
  float volume
16
23
  ) throws Exception {
17
- super(owner, assetId, null, audioChannelNum, volume);
18
- mediaPlayer = new MediaPlayer();
19
- mediaPlayer.setDataSource(owner.getContext(), uri);
20
- mediaPlayer.setVolume(volume, volume);
21
- mediaPlayer.prepareAsync(); // Prepare asynchronously to not block the main thread
24
+ super(owner, assetId, null, 0, volume);
25
+ this.uri = uri;
26
+ this.volume = volume;
27
+ this.mediaPlayers = new ArrayList<>();
28
+
29
+ if (audioChannelNum < 1) {
30
+ audioChannelNum = 1;
31
+ }
32
+
33
+ for (int i = 0; i < audioChannelNum; i++) {
34
+ MediaPlayer mediaPlayer = new MediaPlayer();
35
+ mediaPlayers.add(mediaPlayer);
36
+ initializeMediaPlayer(mediaPlayer);
37
+ }
38
+ }
39
+
40
+ private void initializeMediaPlayer(MediaPlayer mediaPlayer) {
41
+ try {
42
+ mediaPlayer.setDataSource(owner.getContext(), uri);
43
+ mediaPlayer.setVolume(volume, volume);
44
+ mediaPlayer.setOnPreparedListener(mp -> {
45
+ isPrepared = true;
46
+ Log.d(TAG, "MediaPlayer prepared for " + uri.toString());
47
+ });
48
+ mediaPlayer.setOnErrorListener((mp, what, extra) -> {
49
+ Log.e(TAG, "MediaPlayer error: " + what + ", " + extra);
50
+ return false;
51
+ });
52
+ mediaPlayer.prepareAsync();
53
+ } catch (Exception e) {
54
+ Log.e(TAG, "Error initializing MediaPlayer", e);
55
+ }
22
56
  }
23
57
 
24
58
  @Override
25
59
  public void play(Double time) throws Exception {
26
- if (mediaPlayer != null) {
27
- if (time != null) {
28
- mediaPlayer.seekTo((int) (time * 1000));
29
- }
30
- mediaPlayer.start();
60
+ if (mediaPlayers.isEmpty()) {
61
+ throw new Exception("No MediaPlayer available");
62
+ }
63
+
64
+ MediaPlayer mediaPlayer = mediaPlayers.get(playIndex);
65
+ if (!isPrepared) {
66
+ Log.d(TAG, "MediaPlayer not yet prepared, waiting...");
67
+ mediaPlayer.setOnPreparedListener(mp -> {
68
+ isPrepared = true;
69
+ try {
70
+ playInternal(mediaPlayer, time);
71
+ } catch (Exception e) {
72
+ Log.e(TAG, "Error playing after prepare", e);
73
+ }
74
+ });
31
75
  } else {
32
- throw new Exception("MediaPlayer is null");
76
+ playInternal(mediaPlayer, time);
77
+ }
78
+
79
+ playIndex = (playIndex + 1) % mediaPlayers.size();
80
+ }
81
+
82
+ private void playInternal(MediaPlayer mediaPlayer, Double time)
83
+ throws Exception {
84
+ if (time != null) {
85
+ mediaPlayer.seekTo((int) (time * 1000));
33
86
  }
87
+ mediaPlayer.start();
34
88
  }
35
89
 
36
90
  @Override
37
91
  public boolean pause() throws Exception {
38
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
39
- mediaPlayer.pause();
40
- return true; // Return true to indicate that the audio was paused
92
+ boolean wasPlaying = false;
93
+ for (MediaPlayer mediaPlayer : mediaPlayers) {
94
+ if (mediaPlayer.isPlaying()) {
95
+ mediaPlayer.pause();
96
+ wasPlaying = true;
97
+ }
41
98
  }
42
- return false;
99
+ return wasPlaying;
43
100
  }
44
101
 
45
102
  @Override
46
103
  public void resume() throws Exception {
47
- if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
48
- mediaPlayer.start();
104
+ for (MediaPlayer mediaPlayer : mediaPlayers) {
105
+ if (!mediaPlayer.isPlaying()) {
106
+ mediaPlayer.start();
107
+ }
49
108
  }
50
109
  }
51
110
 
52
111
  @Override
53
112
  public void stop() throws Exception {
54
- if (mediaPlayer != null) {
55
- mediaPlayer.stop();
56
- mediaPlayer.prepare(); // Call prepare to reset the MediaPlayer to the Idle state
113
+ for (MediaPlayer mediaPlayer : mediaPlayers) {
114
+ if (mediaPlayer.isPlaying()) {
115
+ mediaPlayer.stop();
116
+ }
117
+ // Reset the MediaPlayer to make it ready for future playback
118
+ mediaPlayer.reset();
119
+ initializeMediaPlayer(mediaPlayer);
57
120
  }
121
+ isPrepared = false;
58
122
  }
59
123
 
60
124
  @Override
61
125
  public void loop() throws Exception {
62
- if (mediaPlayer != null) {
126
+ if (!mediaPlayers.isEmpty()) {
127
+ MediaPlayer mediaPlayer = mediaPlayers.get(playIndex);
63
128
  mediaPlayer.setLooping(true);
64
129
  mediaPlayer.start();
130
+ playIndex = (playIndex + 1) % mediaPlayers.size();
65
131
  }
66
132
  }
67
133
 
68
134
  @Override
69
135
  public void unload() throws Exception {
70
- if (mediaPlayer != null) {
136
+ for (MediaPlayer mediaPlayer : mediaPlayers) {
71
137
  mediaPlayer.release();
72
- mediaPlayer = null;
73
138
  }
139
+ mediaPlayers.clear();
74
140
  }
75
141
 
76
142
  @Override
77
143
  public void setVolume(float volume) throws Exception {
78
- if (mediaPlayer != null) {
144
+ this.volume = volume;
145
+ for (MediaPlayer mediaPlayer : mediaPlayers) {
79
146
  mediaPlayer.setVolume(volume, volume);
80
147
  }
81
148
  }
82
149
 
83
150
  @Override
84
151
  public boolean isPlaying() throws Exception {
85
- return mediaPlayer != null && mediaPlayer.isPlaying();
152
+ for (MediaPlayer mediaPlayer : mediaPlayers) {
153
+ if (mediaPlayer.isPlaying()) {
154
+ return true;
155
+ }
156
+ }
157
+ return false;
158
+ }
159
+
160
+ @Override
161
+ public double getDuration() {
162
+ if (!mediaPlayers.isEmpty() && isPrepared) {
163
+ return mediaPlayers.get(0).getDuration() / 1000.0;
164
+ }
165
+ return 0;
166
+ }
167
+
168
+ @Override
169
+ public double getCurrentPosition() {
170
+ if (!mediaPlayers.isEmpty() && isPrepared) {
171
+ return mediaPlayers.get(0).getCurrentPosition() / 1000.0;
172
+ }
173
+ return 0;
86
174
  }
87
175
  }
@@ -293,7 +293,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate {
293
293
  let channels: Int?
294
294
  let volume: Float?
295
295
  let delay: Float?
296
- var isLocalUrl: Bool = call.getBool("isUrl") ?? false // Existing flag for local URLs
296
+ var isLocalUrl: Bool = call.getBool("isUrl") ?? false
297
297
 
298
298
  if audioId != "" {
299
299
  var assetPath: String = call.getString(Constant.AssetPathKey) ?? ""
@@ -324,25 +324,27 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate {
324
324
  self.audioList[audioId] = remoteAudioAsset
325
325
  call.resolve()
326
326
  } else if isLocalUrl == false {
327
- // if assetPath dont start with public/ add it
327
+ // Handle public folder
328
+ // if assetPath doesnt start with public/ add it
328
329
  assetPath = assetPath.starts(with: "public/") ? assetPath : "public/" + assetPath
329
330
 
330
331
  let assetPathSplit = assetPath.components(separatedBy: ".")
331
332
  basePath = Bundle.main.path(forResource: assetPathSplit[0], ofType: assetPathSplit[1])
332
333
  } else {
333
- let url = URL(string: assetPath)
334
- basePath = url!.path
334
+ // Handle local file URL
335
+ let fileURL = URL(fileURLWithPath: assetPath)
336
+ basePath = fileURL.path
335
337
  }
336
338
 
337
- if FileManager.default.fileExists(atPath: basePath ?? "") {
339
+ if let basePath = basePath, FileManager.default.fileExists(atPath: basePath) {
338
340
  if !complex {
339
- let soundFileUrl = URL(fileURLWithPath: basePath ?? "")
341
+ let soundFileUrl = URL(fileURLWithPath: basePath)
340
342
  var soundId = SystemSoundID()
341
343
  AudioServicesCreateSystemSoundID(soundFileUrl as CFURL, &soundId)
342
344
  self.audioList[audioId] = NSNumber(value: Int32(soundId))
343
345
  call.resolve()
344
346
  } else {
345
- let audioAsset: AudioAsset = AudioAsset(
347
+ let audioAsset = AudioAsset(
346
348
  owner: self,
347
349
  withAssetId: audioId, withPath: basePath, withChannels: channels,
348
350
  withVolume: volume, withFadeDelay: delay)
@@ -350,7 +352,26 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate {
350
352
  call.resolve()
351
353
  }
352
354
  } else {
353
- call.reject(Constant.ErrorAssetPath + " - " + assetPath)
355
+ if FileManager.default.fileExists(atPath: assetPath) {
356
+ // Use the original assetPath
357
+ if !complex {
358
+ let soundFileUrl = URL(fileURLWithPath: assetPath)
359
+ var soundId = SystemSoundID()
360
+ AudioServicesCreateSystemSoundID(soundFileUrl as CFURL, &soundId)
361
+ self.audioList[audioId] = NSNumber(value: Int32(soundId))
362
+ call.resolve()
363
+ } else {
364
+ let audioAsset = AudioAsset(
365
+ owner: self,
366
+ withAssetId: audioId, withPath: assetPath, withChannels: channels,
367
+ withVolume: volume, withFadeDelay: delay)
368
+ self.audioList[audioId] = audioAsset
369
+ call.resolve()
370
+ }
371
+ } else {
372
+ let attributes = try? FileManager.default.attributesOfItem(atPath: assetPath)
373
+ call.reject(Constant.ErrorAssetPath + " - " + assetPath)
374
+ }
354
375
  }
355
376
  } else {
356
377
  call.reject(Constant.ErrorAssetAlreadyLoaded + " - " + audioId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/native-audio",
3
- "version": "6.4.4",
3
+ "version": "6.4.6",
4
4
  "description": "A native plugin for native audio engine",
5
5
  "main": "dist/plugin.js",
6
6
  "module": "dist/esm/index.js",
@@ -82,5 +82,6 @@
82
82
  },
83
83
  "publishConfig": {
84
84
  "access": "public"
85
- }
85
+ },
86
+ "packageManager": "pnpm@9.7.0+sha512.dc09430156b427f5ecfc79888899e1c39d2d690f004be70e05230b72cb173d96839587545d09429b55ac3c429c801b4dc3c0e002f653830a420fa2dd4e3cf9cf"
86
87
  }