sezo-audio-engine 0.0.13 → 0.0.15

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
@@ -38,7 +38,7 @@ allprojects {
38
38
  Optionally pin the engine version in `android/gradle.properties`:
39
39
 
40
40
  ```properties
41
- sezoAudioEngineVersion=android-engine-v0.1.6
41
+ sezoAudioEngineVersion=android-engine-v0.1.7
42
42
  ```
43
43
 
44
44
  If you want to build from source instead, include the local engine module in
@@ -46,7 +46,7 @@ dependencies {
46
46
  if (androidEngineProject != null) {
47
47
  implementation androidEngineProject
48
48
  } else {
49
- def sezoAudioEngineVersion = project.findProperty("sezoAudioEngineVersion") ?: "android-engine-v0.1.6"
49
+ def sezoAudioEngineVersion = project.findProperty("sezoAudioEngineVersion") ?: "android-engine-v0.1.7"
50
50
  implementation "com.github.Sepzie:SezoAudioEngine:${sezoAudioEngineVersion}"
51
51
  }
52
52
  }
@@ -5,6 +5,7 @@ import android.os.Bundle
5
5
  import android.util.Log
6
6
  import com.sezo.audioengine.AudioEngine
7
7
  import expo.modules.kotlin.Promise
8
+ import expo.modules.kotlin.exception.CodedException
8
9
  import expo.modules.kotlin.modules.Module
9
10
  import expo.modules.kotlin.modules.ModuleDefinition
10
11
  import java.util.Collections
@@ -60,6 +61,10 @@ class ExpoAudioEngineModule : Module() {
60
61
  val operation: String
61
62
  )
62
63
 
64
+ private fun requireEngine(): AudioEngine {
65
+ return audioEngine ?: throw CodedException("ENGINE_NOT_INITIALIZED", "Engine not initialized")
66
+ }
67
+
63
68
  override fun definition() = ModuleDefinition {
64
69
  Name("ExpoAudioEngineModule")
65
70
 
@@ -151,7 +156,7 @@ class ExpoAudioEngineModule : Module() {
151
156
  val success = audioEngine?.initialize(sampleRate, maxTracks) ?: false
152
157
 
153
158
  if (!success) {
154
- throw Exception("Failed to initialize audio engine")
159
+ throw CodedException("ENGINE_INIT_FAILED", "Failed to initialize audio engine", null)
155
160
  }
156
161
 
157
162
  ensureAudioFocusManager()
@@ -194,18 +199,24 @@ class ExpoAudioEngineModule : Module() {
194
199
  }
195
200
 
196
201
  AsyncFunction("loadTracks") { tracks: List<Map<String, Any?>> ->
197
- val engine = audioEngine ?: throw Exception("Engine not initialized")
202
+ val engine = requireEngine()
198
203
 
199
204
  tracks.forEach { track ->
200
- val id = track["id"] as? String ?: throw Exception("Missing track id")
201
- val uri = track["uri"] as? String ?: throw Exception("Missing track uri")
205
+ val id = track["id"] as? String
206
+ ?: throw CodedException("TRACK_LOAD_FAILED", "Missing track id", null)
207
+ val uri = track["uri"] as? String
208
+ ?: throw CodedException("TRACK_LOAD_FAILED", "Missing track uri", null)
202
209
  val startTimeMs = (track["startTimeMs"] as? Number)?.toDouble() ?: 0.0
203
210
 
204
211
  Log.d(TAG, "Loading track: id=$id, uri=$uri")
205
212
  val filePath = convertUriToPath(uri)
206
213
 
207
214
  if (!engine.loadTrack(id, filePath, startTimeMs)) {
208
- throw Exception("Failed to load track: $id from $filePath")
215
+ throw CodedException(
216
+ "TRACK_LOAD_FAILED",
217
+ "Failed to load track: $id from $filePath",
218
+ null
219
+ )
209
220
  }
210
221
 
211
222
  loadedTrackIds.add(id)
@@ -214,10 +225,10 @@ class ExpoAudioEngineModule : Module() {
214
225
  }
215
226
 
216
227
  AsyncFunction("unloadTrack") { trackId: String ->
217
- val engine = audioEngine ?: throw Exception("Engine not initialized")
228
+ val engine = requireEngine()
218
229
 
219
230
  if (!engine.unloadTrack(trackId)) {
220
- throw Exception("Failed to unload track: $trackId")
231
+ throw CodedException("TRACK_UNLOAD_FAILED", "Failed to unload track: $trackId", null)
221
232
  }
222
233
 
223
234
  loadedTrackIds.remove(trackId)
@@ -225,7 +236,7 @@ class ExpoAudioEngineModule : Module() {
225
236
  }
226
237
 
227
238
  AsyncFunction("unloadAllTracks") {
228
- val engine = audioEngine ?: throw Exception("Engine not initialized")
239
+ val engine = requireEngine()
229
240
  engine.unloadAllTracks()
230
241
  loadedTrackIds.clear()
231
242
  Log.d(TAG, "All tracks unloaded")
@@ -236,27 +247,27 @@ class ExpoAudioEngineModule : Module() {
236
247
  }
237
248
 
238
249
  AsyncFunction("play") {
239
- val engine = audioEngine ?: throw Exception("Engine not initialized")
250
+ val engine = requireEngine()
240
251
  if (!startPlaybackInternal(engine, fromSystem = false)) {
241
- throw Exception("Failed to start playback")
252
+ throw CodedException("PLAYBACK_START_FAILED", "Failed to start playback", null)
242
253
  }
243
254
  Log.d(TAG, "Playback started")
244
255
  }
245
256
 
246
257
  Function("pause") {
247
- val engine = audioEngine ?: throw Exception("Engine not initialized")
258
+ val engine = requireEngine()
248
259
  pausePlaybackInternal(engine, fromSystem = false)
249
260
  Log.d(TAG, "Playback paused")
250
261
  }
251
262
 
252
263
  Function("stop") {
253
- val engine = audioEngine ?: throw Exception("Engine not initialized")
264
+ val engine = requireEngine()
254
265
  stopPlaybackInternal(engine, fromSystem = false)
255
266
  Log.d(TAG, "Playback stopped")
256
267
  }
257
268
 
258
269
  Function("seek") { positionMs: Double ->
259
- val engine = audioEngine ?: throw Exception("Engine not initialized")
270
+ val engine = requireEngine()
260
271
  engine.seek(positionMs)
261
272
  Log.d(TAG, "Seeked to position: $positionMs ms")
262
273
  }
@@ -274,27 +285,27 @@ class ExpoAudioEngineModule : Module() {
274
285
  }
275
286
 
276
287
  Function("setTrackVolume") { trackId: String, volume: Double ->
277
- val engine = audioEngine ?: throw Exception("Engine not initialized")
288
+ val engine = requireEngine()
278
289
  engine.setTrackVolume(trackId, volume.toFloat())
279
290
  }
280
291
 
281
292
  Function("setTrackMuted") { trackId: String, muted: Boolean ->
282
- val engine = audioEngine ?: throw Exception("Engine not initialized")
293
+ val engine = requireEngine()
283
294
  engine.setTrackMuted(trackId, muted)
284
295
  }
285
296
 
286
297
  Function("setTrackSolo") { trackId: String, solo: Boolean ->
287
- val engine = audioEngine ?: throw Exception("Engine not initialized")
298
+ val engine = requireEngine()
288
299
  engine.setTrackSolo(trackId, solo)
289
300
  }
290
301
 
291
302
  Function("setTrackPan") { trackId: String, pan: Double ->
292
- val engine = audioEngine ?: throw Exception("Engine not initialized")
303
+ val engine = requireEngine()
293
304
  engine.setTrackPan(trackId, pan.toFloat())
294
305
  }
295
306
 
296
307
  Function("setMasterVolume") { volume: Double ->
297
- val engine = audioEngine ?: throw Exception("Engine not initialized")
308
+ val engine = requireEngine()
298
309
  engine.setMasterVolume(volume.toFloat())
299
310
  }
300
311
 
@@ -303,7 +314,7 @@ class ExpoAudioEngineModule : Module() {
303
314
  }
304
315
 
305
316
  Function("setPitch") { semitones: Double ->
306
- val engine = audioEngine ?: throw Exception("Engine not initialized")
317
+ val engine = requireEngine()
307
318
  engine.setPitch(semitones.toFloat())
308
319
  }
309
320
 
@@ -312,7 +323,7 @@ class ExpoAudioEngineModule : Module() {
312
323
  }
313
324
 
314
325
  Function("setSpeed") { rate: Double ->
315
- val engine = audioEngine ?: throw Exception("Engine not initialized")
326
+ val engine = requireEngine()
316
327
  engine.setSpeed(rate.toFloat())
317
328
  }
318
329
 
@@ -321,13 +332,13 @@ class ExpoAudioEngineModule : Module() {
321
332
  }
322
333
 
323
334
  Function("setTempoAndPitch") { tempo: Double, pitch: Double ->
324
- val engine = audioEngine ?: throw Exception("Engine not initialized")
335
+ val engine = requireEngine()
325
336
  engine.setSpeed(tempo.toFloat())
326
337
  engine.setPitch(pitch.toFloat())
327
338
  }
328
339
 
329
340
  Function("setTrackPitch") { trackId: String, semitones: Double ->
330
- val engine = audioEngine ?: throw Exception("Engine not initialized")
341
+ val engine = requireEngine()
331
342
  engine.setTrackPitch(trackId, semitones.toFloat())
332
343
  }
333
344
 
@@ -336,7 +347,7 @@ class ExpoAudioEngineModule : Module() {
336
347
  }
337
348
 
338
349
  Function("setTrackSpeed") { trackId: String, rate: Double ->
339
- val engine = audioEngine ?: throw Exception("Engine not initialized")
350
+ val engine = requireEngine()
340
351
  engine.setTrackSpeed(trackId, rate.toFloat())
341
352
  }
342
353
 
@@ -345,7 +356,7 @@ class ExpoAudioEngineModule : Module() {
345
356
  }
346
357
 
347
358
  AsyncFunction("startRecording") { config: Map<String, Any?>? ->
348
- val engine = audioEngine ?: throw Exception("Engine not initialized")
359
+ val engine = requireEngine()
349
360
 
350
361
  val sampleRate = (config?.get("sampleRate") as? Number)?.toInt() ?: 44100
351
362
  val channels = (config?.get("channels") as? Number)?.toInt() ?: 1
@@ -376,7 +387,7 @@ class ExpoAudioEngineModule : Module() {
376
387
  )
377
388
 
378
389
  if (!success) {
379
- throw Exception("Failed to start recording")
390
+ throw CodedException("RECORDING_START_FAILED", "Failed to start recording", null)
380
391
  }
381
392
 
382
393
  activeRecordingFormat = format
@@ -384,13 +395,17 @@ class ExpoAudioEngineModule : Module() {
384
395
  }
385
396
 
386
397
  AsyncFunction("stopRecording") {
387
- val engine = audioEngine ?: throw Exception("Engine not initialized")
398
+ val engine = requireEngine()
388
399
 
389
400
  Log.d(TAG, "Stopping recording")
390
401
  val result = engine.stopRecording()
391
402
 
392
403
  if (!result.success) {
393
- throw Exception("Failed to stop recording: ${result.errorMessage}")
404
+ throw CodedException(
405
+ "RECORDING_STOP_FAILED",
406
+ "Failed to stop recording: ${result.errorMessage}",
407
+ null
408
+ )
394
409
  }
395
410
 
396
411
  Log.d(TAG, "Recording stopped: ${result.fileSize} bytes, ${result.durationSamples} samples")
@@ -426,12 +441,12 @@ class ExpoAudioEngineModule : Module() {
426
441
  }
427
442
 
428
443
  Function("setRecordingVolume") { volume: Double ->
429
- val engine = audioEngine ?: throw Exception("Engine not initialized")
444
+ val engine = requireEngine()
430
445
  engine.setRecordingVolume(volume.toFloat())
431
446
  }
432
447
 
433
448
  AsyncFunction("extractTrack") { trackId: String, config: Map<String, Any?>?, promise: Promise ->
434
- val engine = audioEngine ?: throw Exception("Engine not initialized")
449
+ val engine = requireEngine()
435
450
 
436
451
  val format = (config?.get("format") as? String) ?: "wav"
437
452
  val bitrate = (config?.get("bitrate") as? Number)?.toInt() ?: 128000
@@ -471,7 +486,7 @@ class ExpoAudioEngineModule : Module() {
471
486
  }
472
487
 
473
488
  AsyncFunction("extractAllTracks") { config: Map<String, Any?>?, promise: Promise ->
474
- val engine = audioEngine ?: throw Exception("Engine not initialized")
489
+ val engine = requireEngine()
475
490
 
476
491
  val format = (config?.get("format") as? String) ?: "wav"
477
492
  val bitrate = (config?.get("bitrate") as? Number)?.toInt() ?: 128000
@@ -510,7 +525,7 @@ class ExpoAudioEngineModule : Module() {
510
525
  }
511
526
 
512
527
  Function("cancelExtraction") { jobId: Double? ->
513
- val engine = audioEngine ?: throw Exception("Engine not initialized")
528
+ val engine = requireEngine()
514
529
  val resolvedJobId = jobId?.toLong() ?: lastExtractionJobId
515
530
  if (resolvedJobId == null) {
516
531
  false
@@ -527,7 +542,7 @@ class ExpoAudioEngineModule : Module() {
527
542
  Function("getTrackLevel") { _trackId: String -> 0.0 }
528
543
 
529
544
  AsyncFunction("enableBackgroundPlayback") { metadata: Map<String, Any?> ->
530
- val engine = audioEngine ?: throw Exception("Engine not initialized")
545
+ val engine = requireEngine()
531
546
  backgroundPlaybackEnabled = true
532
547
  mergeNowPlayingMetadata(metadata)
533
548
  ensureAudioFocusManager()
@@ -940,15 +955,27 @@ class ExpoAudioEngineModule : Module() {
940
955
  uri.startsWith("content://") -> {
941
956
  // TODO: Handle content:// URIs with ContentResolver
942
957
  // For now, throw an error - this will be implemented when needed
943
- throw Exception("content:// URIs not yet supported. Use file:// or absolute paths.")
958
+ throw CodedException(
959
+ "UNSUPPORTED_URI",
960
+ "content:// URIs not yet supported. Use file:// or absolute paths.",
961
+ null
962
+ )
944
963
  }
945
964
  uri.startsWith("asset://") -> {
946
965
  // TODO: Handle asset:// URIs by copying to temp file
947
966
  // For now, throw an error - this will be implemented when needed
948
- throw Exception("asset:// URIs not yet supported. Use file:// or absolute paths.")
967
+ throw CodedException(
968
+ "UNSUPPORTED_URI",
969
+ "asset:// URIs not yet supported. Use file:// or absolute paths.",
970
+ null
971
+ )
949
972
  }
950
973
  else -> {
951
- throw Exception("Unsupported URI scheme: $uri. Use file:// or absolute paths.")
974
+ throw CodedException(
975
+ "UNSUPPORTED_URI",
976
+ "Unsupported URI scheme: $uri. Use file:// or absolute paths.",
977
+ null
978
+ )
952
979
  }
953
980
  }
954
981
  }
@@ -0,0 +1,32 @@
1
+ import ExpoModulesCore
2
+
3
+ struct AudioEngineError: CodedError, CustomNSError {
4
+ let code: String
5
+ let description: String
6
+ let details: [String: Any]?
7
+
8
+ init(_ code: String, _ description: String, details: [String: Any]? = nil) {
9
+ self.code = code
10
+ self.description = description
11
+ self.details = details
12
+ }
13
+
14
+ static var errorDomain: String {
15
+ return "ExpoAudioEngine"
16
+ }
17
+
18
+ var errorCode: Int {
19
+ return 0
20
+ }
21
+
22
+ var errorUserInfo: [String: Any] {
23
+ var info: [String: Any] = [
24
+ NSLocalizedDescriptionKey: description,
25
+ "code": code
26
+ ]
27
+ if let details = details {
28
+ info["details"] = details
29
+ }
30
+ return info
31
+ }
32
+ }