@capgo/native-audio 6.1.36 → 6.2.1
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/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 +2 -1
- package/ios/Plugin/Plugin.swift +34 -7
- 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
|
+
}
|
|
@@ -32,6 +32,7 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
|
32
32
|
for _ in 0..<channels {
|
|
33
33
|
do {
|
|
34
34
|
let player: AVAudioPlayer! = try AVAudioPlayer(contentsOf: pathUrl)
|
|
35
|
+
player.delegate = owner
|
|
35
36
|
|
|
36
37
|
if player != nil {
|
|
37
38
|
player.enableRate = true
|
|
@@ -75,7 +76,7 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
|
75
76
|
player = channels[playIndex]
|
|
76
77
|
player.currentTime = time
|
|
77
78
|
player.numberOfLoops = 0
|
|
78
|
-
if
|
|
79
|
+
if delay > 0 {
|
|
79
80
|
player.play(atTime: player.deviceCurrentTime + delay)
|
|
80
81
|
} else {
|
|
81
82
|
player.play()
|
package/ios/Plugin/Plugin.swift
CHANGED
|
@@ -10,7 +10,7 @@ enum MyError: Error {
|
|
|
10
10
|
/// Please read the Capacitor iOS Plugin Development Guide
|
|
11
11
|
/// here: https://capacitor.ionicframework.com/docs/plugins/ios
|
|
12
12
|
@objc(NativeAudio)
|
|
13
|
-
public class NativeAudio: CAPPlugin {
|
|
13
|
+
public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate {
|
|
14
14
|
|
|
15
15
|
var audioList: [String: Any] = [:]
|
|
16
16
|
var fadeMusic = false
|
|
@@ -101,6 +101,26 @@ public class NativeAudio: CAPPlugin {
|
|
|
101
101
|
preloadAsset(call, isComplex: true)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
func activateSession() {
|
|
105
|
+
do {
|
|
106
|
+
try self.session.setActive(true)
|
|
107
|
+
} catch {
|
|
108
|
+
print("Failed to set session active")
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func endSession() {
|
|
113
|
+
do {
|
|
114
|
+
try self.session.setActive(false, options: .notifyOthersOnDeactivation)
|
|
115
|
+
} catch {
|
|
116
|
+
print("Failed to deactivate audio session")
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
|
|
121
|
+
self.endSession()
|
|
122
|
+
}
|
|
123
|
+
|
|
104
124
|
@objc func play(_ call: CAPPluginCall) {
|
|
105
125
|
let audioId = call.getString(Constant.AssetIdKey) ?? ""
|
|
106
126
|
let time = call.getDouble("time") ?? 0
|
|
@@ -115,7 +135,7 @@ public class NativeAudio: CAPPlugin {
|
|
|
115
135
|
if asset != nil {
|
|
116
136
|
if asset is AudioAsset {
|
|
117
137
|
let audioAsset = asset as? AudioAsset
|
|
118
|
-
|
|
138
|
+
self.activateSession()
|
|
119
139
|
if self.fadeMusic {
|
|
120
140
|
audioAsset?.playWithFade(time: time)
|
|
121
141
|
} else {
|
|
@@ -124,6 +144,7 @@ public class NativeAudio: CAPPlugin {
|
|
|
124
144
|
call.resolve()
|
|
125
145
|
} else if asset is Int32 {
|
|
126
146
|
let audioAsset = asset as? NSNumber ?? 0
|
|
147
|
+
self.activateSession()
|
|
127
148
|
AudioServicesPlaySystemSound(SystemSoundID(audioAsset.intValue))
|
|
128
149
|
call.resolve()
|
|
129
150
|
} else {
|
|
@@ -175,7 +196,7 @@ public class NativeAudio: CAPPlugin {
|
|
|
175
196
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
176
197
|
return
|
|
177
198
|
}
|
|
178
|
-
|
|
199
|
+
self.activateSession()
|
|
179
200
|
audioAsset.resume()
|
|
180
201
|
call.resolve()
|
|
181
202
|
}
|
|
@@ -186,6 +207,7 @@ public class NativeAudio: CAPPlugin {
|
|
|
186
207
|
}
|
|
187
208
|
|
|
188
209
|
audioAsset.pause()
|
|
210
|
+
self.endSession()
|
|
189
211
|
call.resolve()
|
|
190
212
|
}
|
|
191
213
|
|
|
@@ -194,6 +216,7 @@ public class NativeAudio: CAPPlugin {
|
|
|
194
216
|
|
|
195
217
|
do {
|
|
196
218
|
try stopAudio(audioId: audioId)
|
|
219
|
+
self.endSession()
|
|
197
220
|
} catch {
|
|
198
221
|
call.reject(Constant.ErrorAssetNotFound)
|
|
199
222
|
}
|
|
@@ -260,7 +283,7 @@ public class NativeAudio: CAPPlugin {
|
|
|
260
283
|
let channels: Int?
|
|
261
284
|
let volume: Float?
|
|
262
285
|
let delay: Float?
|
|
263
|
-
|
|
286
|
+
var isLocalUrl: Bool = call.getBool("isUrl") ?? false // Existing flag for local URLs
|
|
264
287
|
|
|
265
288
|
if audioId != "" {
|
|
266
289
|
var assetPath: String = call.getString(Constant.AssetPathKey) ?? ""
|
|
@@ -269,12 +292,11 @@ public class NativeAudio: CAPPlugin {
|
|
|
269
292
|
volume = call.getFloat("volume") ?? 1.0
|
|
270
293
|
channels = call.getInt("channels") ?? 1
|
|
271
294
|
delay = call.getFloat("delay") ?? 1.0
|
|
272
|
-
isUrl = call.getBool("isUrl") ?? false
|
|
273
295
|
} else {
|
|
274
296
|
channels = 0
|
|
275
297
|
volume = 0
|
|
276
298
|
delay = 0
|
|
277
|
-
|
|
299
|
+
isLocalUrl = false
|
|
278
300
|
}
|
|
279
301
|
|
|
280
302
|
if audioList.isEmpty {
|
|
@@ -286,7 +308,12 @@ public class NativeAudio: CAPPlugin {
|
|
|
286
308
|
queue.async {
|
|
287
309
|
if asset == nil {
|
|
288
310
|
var basePath: String?
|
|
289
|
-
if
|
|
311
|
+
if let url = URL(string: assetPath), url.scheme != nil {
|
|
312
|
+
// Handle remote URL
|
|
313
|
+
let remoteAudioAsset = RemoteAudioAsset(owner: self, withAssetId: audioId, withPath: assetPath, withChannels: channels, withVolume: volume, withFadeDelay: delay)
|
|
314
|
+
self.audioList[audioId] = remoteAudioAsset
|
|
315
|
+
call.resolve()
|
|
316
|
+
} else if isLocalUrl == false {
|
|
290
317
|
// if assetPath dont start with public/ add it
|
|
291
318
|
assetPath = assetPath.starts(with: "public/") ? assetPath : "public/" + assetPath
|
|
292
319
|
|
|
@@ -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
|
+
}
|