@capgo/native-audio 6.1.35 → 6.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +39 -10
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +87 -0
- package/ios/Plugin/AudioAsset.swift +1 -1
- package/ios/Plugin/Plugin.swift +10 -9
- package/ios/Plugin/RemoteAudioAsset.swift +74 -0
- package/package.json +1 -1
@@ -20,6 +20,7 @@ import android.content.Context;
|
|
20
20
|
import android.content.res.AssetFileDescriptor;
|
21
21
|
import android.content.res.AssetManager;
|
22
22
|
import android.media.AudioManager;
|
23
|
+
import android.net.Uri;
|
23
24
|
import android.os.Build;
|
24
25
|
import android.os.ParcelFileDescriptor;
|
25
26
|
import android.util.Log;
|
@@ -419,13 +420,15 @@ public class NativeAudio
|
|
419
420
|
private void preloadAsset(PluginCall call) {
|
420
421
|
double volume = 1.0;
|
421
422
|
int audioChannelNum = 1;
|
423
|
+
JSObject status = new JSObject();
|
424
|
+
status.put("STATUS", "OK");
|
422
425
|
|
423
426
|
try {
|
424
427
|
initSoundPool();
|
425
428
|
|
426
429
|
String audioId = call.getString(ASSET_ID);
|
427
430
|
|
428
|
-
boolean
|
431
|
+
boolean isLocalUrl = Boolean.TRUE.equals(call.getBoolean("isUrl", false));
|
429
432
|
|
430
433
|
if (!isStringValid(audioId)) {
|
431
434
|
call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
|
@@ -457,7 +460,7 @@ public class NativeAudio
|
|
457
460
|
}
|
458
461
|
|
459
462
|
AssetFileDescriptor assetFileDescriptor;
|
460
|
-
if (
|
463
|
+
if (isLocalUrl) {
|
461
464
|
File f = new File(new URI(fullPath));
|
462
465
|
ParcelFileDescriptor p = ParcelFileDescriptor.open(
|
463
466
|
f,
|
@@ -465,13 +468,41 @@ public class NativeAudio
|
|
465
468
|
);
|
466
469
|
assetFileDescriptor = new AssetFileDescriptor(p, 0, -1);
|
467
470
|
} else {
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
+
try {
|
472
|
+
Uri uri = Uri.parse(fullPath); // Now Uri class should be recognized
|
473
|
+
if (
|
474
|
+
uri.getScheme() != null &&
|
475
|
+
(
|
476
|
+
uri.getScheme().equals("http") ||
|
477
|
+
uri.getScheme().equals("https")
|
478
|
+
)
|
479
|
+
) {
|
480
|
+
// It's a remote URL
|
481
|
+
RemoteAudioAsset remoteAudioAsset = new RemoteAudioAsset(
|
482
|
+
this,
|
483
|
+
audioId,
|
484
|
+
uri,
|
485
|
+
audioChannelNum,
|
486
|
+
(float) volume
|
487
|
+
);
|
488
|
+
audioAssetList.put(audioId, remoteAudioAsset);
|
489
|
+
call.resolve(status);
|
490
|
+
return;
|
491
|
+
} else {
|
492
|
+
// It's a local file path
|
493
|
+
// Check if fullPath starts with "public/" and prepend if necessary
|
494
|
+
if (!fullPath.startsWith("public/")) {
|
495
|
+
fullPath = "public/".concat(fullPath);
|
496
|
+
}
|
497
|
+
Context ctx = getContext().getApplicationContext(); // Use getContext() directly
|
498
|
+
AssetManager am = ctx.getResources().getAssets();
|
499
|
+
// Remove the redefinition of assetFileDescriptor
|
500
|
+
assetFileDescriptor = am.openFd(fullPath);
|
501
|
+
}
|
502
|
+
} catch (Exception e) {
|
503
|
+
call.reject("Error loading audio", e);
|
504
|
+
return;
|
471
505
|
}
|
472
|
-
Context ctx = (Application) this.getContext().getApplicationContext();
|
473
|
-
AssetManager am = ctx.getResources().getAssets();
|
474
|
-
assetFileDescriptor = am.openFd(fullPath);
|
475
506
|
}
|
476
507
|
|
477
508
|
AudioAsset asset = new AudioAsset(
|
@@ -483,8 +514,6 @@ public class NativeAudio
|
|
483
514
|
);
|
484
515
|
audioAssetList.put(audioId, asset);
|
485
516
|
|
486
|
-
JSObject status = new JSObject();
|
487
|
-
status.put("STATUS", "OK");
|
488
517
|
call.resolve(status);
|
489
518
|
} else {
|
490
519
|
call.reject(ERROR_AUDIO_EXISTS);
|
@@ -0,0 +1,87 @@
|
|
1
|
+
package ee.forgr.audio;
|
2
|
+
|
3
|
+
import android.media.MediaPlayer;
|
4
|
+
import android.net.Uri;
|
5
|
+
|
6
|
+
public class RemoteAudioAsset extends AudioAsset {
|
7
|
+
|
8
|
+
private MediaPlayer mediaPlayer;
|
9
|
+
|
10
|
+
public RemoteAudioAsset(
|
11
|
+
NativeAudio owner,
|
12
|
+
String assetId,
|
13
|
+
Uri uri,
|
14
|
+
int audioChannelNum,
|
15
|
+
float volume
|
16
|
+
) 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
|
22
|
+
}
|
23
|
+
|
24
|
+
@Override
|
25
|
+
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();
|
31
|
+
} else {
|
32
|
+
throw new Exception("MediaPlayer is null");
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
@Override
|
37
|
+
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
|
41
|
+
}
|
42
|
+
return false;
|
43
|
+
}
|
44
|
+
|
45
|
+
@Override
|
46
|
+
public void resume() throws Exception {
|
47
|
+
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
|
48
|
+
mediaPlayer.start();
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
@Override
|
53
|
+
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
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
@Override
|
61
|
+
public void loop() throws Exception {
|
62
|
+
if (mediaPlayer != null) {
|
63
|
+
mediaPlayer.setLooping(true);
|
64
|
+
mediaPlayer.start();
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
@Override
|
69
|
+
public void unload() throws Exception {
|
70
|
+
if (mediaPlayer != null) {
|
71
|
+
mediaPlayer.release();
|
72
|
+
mediaPlayer = null;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
@Override
|
77
|
+
public void setVolume(float volume) throws Exception {
|
78
|
+
if (mediaPlayer != null) {
|
79
|
+
mediaPlayer.setVolume(volume, volume);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
@Override
|
84
|
+
public boolean isPlaying() throws Exception {
|
85
|
+
return mediaPlayer != null && mediaPlayer.isPlaying();
|
86
|
+
}
|
87
|
+
}
|
@@ -75,7 +75,7 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
75
75
|
player = channels[playIndex]
|
76
76
|
player.currentTime = time
|
77
77
|
player.numberOfLoops = 0
|
78
|
-
if
|
78
|
+
if delay > 0 {
|
79
79
|
player.play(atTime: player.deviceCurrentTime + delay)
|
80
80
|
} else {
|
81
81
|
player.play()
|
package/ios/Plugin/Plugin.swift
CHANGED
@@ -35,12 +35,9 @@ public class NativeAudio: CAPPlugin {
|
|
35
35
|
}
|
36
36
|
|
37
37
|
let focus = call.getBool(Constant.FocusAudio) ?? false
|
38
|
-
|
39
38
|
do {
|
40
|
-
|
41
39
|
if focus {
|
42
|
-
|
43
|
-
try self.session.setCategory(AVAudioSession.Category.playback)
|
40
|
+
try self.session.setCategory(AVAudioSession.Category.playback, options: .duckOthers)
|
44
41
|
|
45
42
|
}
|
46
43
|
|
@@ -78,7 +75,7 @@ public class NativeAudio: CAPPlugin {
|
|
78
75
|
|
79
76
|
if focus {
|
80
77
|
|
81
|
-
try self.session.setCategory(AVAudioSession.Category.ambient)
|
78
|
+
try self.session.setCategory(AVAudioSession.Category.ambient, options: .duckOthers)
|
82
79
|
|
83
80
|
} else {
|
84
81
|
|
@@ -263,7 +260,7 @@ public class NativeAudio: CAPPlugin {
|
|
263
260
|
let channels: Int?
|
264
261
|
let volume: Float?
|
265
262
|
let delay: Float?
|
266
|
-
|
263
|
+
var isLocalUrl: Bool = call.getBool("isUrl") ?? false // Existing flag for local URLs
|
267
264
|
|
268
265
|
if audioId != "" {
|
269
266
|
var assetPath: String = call.getString(Constant.AssetPathKey) ?? ""
|
@@ -272,12 +269,11 @@ public class NativeAudio: CAPPlugin {
|
|
272
269
|
volume = call.getFloat("volume") ?? 1.0
|
273
270
|
channels = call.getInt("channels") ?? 1
|
274
271
|
delay = call.getFloat("delay") ?? 1.0
|
275
|
-
isUrl = call.getBool("isUrl") ?? false
|
276
272
|
} else {
|
277
273
|
channels = 0
|
278
274
|
volume = 0
|
279
275
|
delay = 0
|
280
|
-
|
276
|
+
isLocalUrl = false
|
281
277
|
}
|
282
278
|
|
283
279
|
if audioList.isEmpty {
|
@@ -289,7 +285,12 @@ public class NativeAudio: CAPPlugin {
|
|
289
285
|
queue.async {
|
290
286
|
if asset == nil {
|
291
287
|
var basePath: String?
|
292
|
-
if
|
288
|
+
if let url = URL(string: assetPath), url.scheme != nil {
|
289
|
+
// Handle remote URL
|
290
|
+
let remoteAudioAsset = RemoteAudioAsset(owner: self, withAssetId: audioId, withPath: assetPath, withChannels: channels, withVolume: volume, withFadeDelay: delay)
|
291
|
+
self.audioList[audioId] = remoteAudioAsset
|
292
|
+
call.resolve()
|
293
|
+
} else if isLocalUrl == false {
|
293
294
|
// if assetPath dont start with public/ add it
|
294
295
|
assetPath = assetPath.starts(with: "public/") ? assetPath : "public/" + assetPath
|
295
296
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import AVFoundation
|
2
|
+
|
3
|
+
public class RemoteAudioAsset: AudioAsset {
|
4
|
+
var playerItem: AVPlayerItem?
|
5
|
+
var player: AVPlayer?
|
6
|
+
|
7
|
+
override init(owner: NativeAudio, withAssetId assetId: String, withPath path: String!, withChannels channels: Int!, withVolume volume: Float!, withFadeDelay delay: Float!) {
|
8
|
+
super.init(owner: owner, withAssetId: assetId, withPath: path, withChannels: channels, withVolume: volume, withFadeDelay: delay)
|
9
|
+
|
10
|
+
if let url = URL(string: path) {
|
11
|
+
self.playerItem = AVPlayerItem(url: url)
|
12
|
+
self.player = AVPlayer(playerItem: self.playerItem)
|
13
|
+
self.player?.volume = volume
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
override func play(time: TimeInterval, delay: TimeInterval) {
|
18
|
+
guard let player = self.player else { return }
|
19
|
+
if delay > 0 {
|
20
|
+
// Calculate the future time to play
|
21
|
+
let timeToPlay = CMTimeAdd(CMTimeMakeWithSeconds(player.currentTime().seconds, preferredTimescale: 1), CMTimeMakeWithSeconds(delay, preferredTimescale: 1))
|
22
|
+
player.seek(to: timeToPlay)
|
23
|
+
} else {
|
24
|
+
player.seek(to: CMTimeMakeWithSeconds(time, preferredTimescale: 1))
|
25
|
+
}
|
26
|
+
player.play()
|
27
|
+
}
|
28
|
+
|
29
|
+
override func pause() {
|
30
|
+
player?.pause()
|
31
|
+
}
|
32
|
+
|
33
|
+
override func resume() {
|
34
|
+
player?.play()
|
35
|
+
}
|
36
|
+
|
37
|
+
override func stop() {
|
38
|
+
player?.pause()
|
39
|
+
player?.seek(to: CMTime.zero)
|
40
|
+
}
|
41
|
+
|
42
|
+
override func loop() {
|
43
|
+
player?.actionAtItemEnd = .none
|
44
|
+
NotificationCenter.default.addObserver(self,
|
45
|
+
selector: #selector(playerItemDidReachEnd(notification:)),
|
46
|
+
name: .AVPlayerItemDidPlayToEndTime,
|
47
|
+
object: player?.currentItem)
|
48
|
+
player?.play()
|
49
|
+
}
|
50
|
+
|
51
|
+
@objc func playerItemDidReachEnd(notification: Notification) {
|
52
|
+
player?.seek(to: CMTime.zero)
|
53
|
+
player?.play()
|
54
|
+
}
|
55
|
+
|
56
|
+
override func unload() {
|
57
|
+
stop()
|
58
|
+
NotificationCenter.default.removeObserver(self)
|
59
|
+
player = nil
|
60
|
+
playerItem = nil
|
61
|
+
}
|
62
|
+
|
63
|
+
override func setVolume(volume: NSNumber!) {
|
64
|
+
player?.volume = volume.floatValue
|
65
|
+
}
|
66
|
+
|
67
|
+
override func setRate(rate: NSNumber!) {
|
68
|
+
player?.rate = rate.floatValue
|
69
|
+
}
|
70
|
+
|
71
|
+
override func isPlaying() -> Bool {
|
72
|
+
return player?.timeControlStatus == .playing
|
73
|
+
}
|
74
|
+
}
|