@capgo/native-audio 6.4.4 → 6.4.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }