react-native-audio-concat 0.2.3 → 0.3.0

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.
Files changed (62) hide show
  1. package/AudioConcat.podspec +2 -21
  2. package/README.md +3 -7
  3. package/android/build.gradle +2 -54
  4. package/android/src/main/java/com/{margelo/nitro/audioconcat/AudioConcat.kt → audioconcat/AudioConcatModule.kt} +73 -31
  5. package/android/src/main/java/com/audioconcat/AudioConcatPackage.kt +33 -0
  6. package/ios/AudioConcat.h +5 -0
  7. package/ios/AudioConcat.mm +104 -0
  8. package/lib/module/NativeAudioConcat.js +5 -0
  9. package/lib/module/NativeAudioConcat.js.map +1 -0
  10. package/lib/module/index.js +2 -28
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/typescript/src/NativeAudioConcat.d.ts +12 -0
  13. package/lib/typescript/src/NativeAudioConcat.d.ts.map +1 -0
  14. package/lib/typescript/src/index.d.ts +6 -27
  15. package/lib/typescript/src/index.d.ts.map +1 -1
  16. package/package.json +14 -18
  17. package/src/NativeAudioConcat.ts +12 -0
  18. package/src/index.tsx +4 -32
  19. package/android/CMakeLists.txt +0 -24
  20. package/android/src/main/cpp/cpp-adapter.cpp +0 -6
  21. package/android/src/main/java/com/margelo/nitro/audioconcat/AudioConcatPackage.kt +0 -22
  22. package/ios/AudioConcat.swift +0 -75
  23. package/lib/module/AudioConcat.nitro.js +0 -4
  24. package/lib/module/AudioConcat.nitro.js.map +0 -1
  25. package/lib/typescript/src/AudioConcat.nitro.d.ts +0 -16
  26. package/lib/typescript/src/AudioConcat.nitro.d.ts.map +0 -1
  27. package/nitro.json +0 -17
  28. package/nitrogen/generated/android/audioconcat+autolinking.cmake +0 -82
  29. package/nitrogen/generated/android/audioconcat+autolinking.gradle +0 -27
  30. package/nitrogen/generated/android/audioconcatOnLoad.cpp +0 -44
  31. package/nitrogen/generated/android/audioconcatOnLoad.hpp +0 -25
  32. package/nitrogen/generated/android/c++/JAudioData.hpp +0 -53
  33. package/nitrogen/generated/android/c++/JAudioDataOrSilence.cpp +0 -26
  34. package/nitrogen/generated/android/c++/JAudioDataOrSilence.hpp +0 -72
  35. package/nitrogen/generated/android/c++/JHybridAudioConcatSpec.cpp +0 -77
  36. package/nitrogen/generated/android/c++/JHybridAudioConcatSpec.hpp +0 -64
  37. package/nitrogen/generated/android/c++/JSilentData.hpp +0 -53
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audioconcat/AudioData.kt +0 -29
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audioconcat/AudioDataOrSilence.kt +0 -42
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audioconcat/HybridAudioConcatSpec.kt +0 -52
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audioconcat/SilentData.kt +0 -29
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audioconcat/audioconcatOnLoad.kt +0 -35
  43. package/nitrogen/generated/ios/AudioConcat+autolinking.rb +0 -60
  44. package/nitrogen/generated/ios/AudioConcat-Swift-Cxx-Bridge.cpp +0 -48
  45. package/nitrogen/generated/ios/AudioConcat-Swift-Cxx-Bridge.hpp +0 -160
  46. package/nitrogen/generated/ios/AudioConcat-Swift-Cxx-Umbrella.hpp +0 -53
  47. package/nitrogen/generated/ios/AudioConcatAutolinking.mm +0 -33
  48. package/nitrogen/generated/ios/AudioConcatAutolinking.swift +0 -25
  49. package/nitrogen/generated/ios/c++/HybridAudioConcatSpecSwift.cpp +0 -11
  50. package/nitrogen/generated/ios/c++/HybridAudioConcatSpecSwift.hpp +0 -81
  51. package/nitrogen/generated/ios/swift/AudioData.swift +0 -35
  52. package/nitrogen/generated/ios/swift/AudioDataOrSilence.swift +0 -18
  53. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +0 -47
  54. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +0 -47
  55. package/nitrogen/generated/ios/swift/HybridAudioConcatSpec.swift +0 -49
  56. package/nitrogen/generated/ios/swift/HybridAudioConcatSpec_cxx.swift +0 -142
  57. package/nitrogen/generated/ios/swift/SilentData.swift +0 -35
  58. package/nitrogen/generated/shared/c++/AudioData.hpp +0 -67
  59. package/nitrogen/generated/shared/c++/HybridAudioConcatSpec.cpp +0 -21
  60. package/nitrogen/generated/shared/c++/HybridAudioConcatSpec.hpp +0 -70
  61. package/nitrogen/generated/shared/c++/SilentData.hpp +0 -67
  62. package/src/AudioConcat.nitro.ts +0 -19
@@ -1,7 +1,6 @@
1
1
  require "json"
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
- folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1'
5
4
 
6
5
  Pod::Spec.new do |s|
7
6
  s.name = "AudioConcat"
@@ -14,27 +13,9 @@ Pod::Spec.new do |s|
14
13
  s.platforms = { :ios => min_ios_version_supported }
15
14
  s.source = { :git => "https://github.com/felixchen-dev/react-native-audio-concat.git", :tag => "#{s.version}" }
16
15
 
16
+ s.source_files = "ios/**/*.{h,m,mm,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
17
18
 
18
- s.source_files = [
19
- "ios/**/*.{swift}",
20
- "ios/**/*.{m,mm}",
21
- "cpp/**/*.{hpp,cpp}",
22
- ]
23
-
24
- s.pod_target_xcconfig = {
25
- "HEADER_SEARCH_PATHS" => [
26
- "${PODS_ROOT}/RCT-Folly",
27
- ],
28
- "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES",
29
- "OTHER_CPLUSPLUSFLAGS" => folly_compiler_flags,
30
- }
31
-
32
-
33
- s.dependency 'React-jsi'
34
- s.dependency 'React-callinvoker'
35
-
36
- load 'nitrogen/generated/ios/AudioConcat+autolinking.rb'
37
- add_nitrogen_files(s)
38
19
 
39
20
  install_modules_dependencies(s)
40
21
  end
package/README.md CHANGED
@@ -6,17 +6,14 @@ Concatenate audio files and silence periods into a single audio file for React N
6
6
 
7
7
  - ✅ Concat multiple audio files with silence periods
8
8
  - ✅ Support for iOS and Android
9
- - ✅ High performance using [Nitro Modules](https://nitro.margelo.com/)
10
9
  - ✅ Output in M4A format
11
10
 
12
11
  ## Installation
13
12
 
14
13
  ```sh
15
- npm install react-native-audio-concat react-native-nitro-modules
14
+ npm install react-native-audio-concat
16
15
  ```
17
16
 
18
- > **Note:** `react-native-nitro-modules` is required as this library relies on [Nitro Modules](https://nitro.margelo.com/).
19
-
20
17
  ### iOS
21
18
 
22
19
  ```sh
@@ -35,10 +32,10 @@ import { concatAudioFiles } from 'react-native-audio-concat';
35
32
  // Concatenate audio files with silence periods
36
33
  const data = [
37
34
  { filePath: '/path/to/audio1.m4a' },
38
- { durationMs: 500 }, // 500ms silence
35
+ { durationMs: 500 }, // 500ms silence
39
36
  { filePath: '/path/to/audio2.m4a' },
40
37
  { durationMs: 1000 }, // 1 second silence
41
- { filePath: '/path/to/audio3.m4a' }
38
+ { filePath: '/path/to/audio3.m4a' },
42
39
  ];
43
40
 
44
41
  const outputPath = '/path/to/merged.m4a';
@@ -72,7 +69,6 @@ Concatenates audio files and silence periods into a single output file.
72
69
 
73
70
  Check out the [example app](example/) for a complete working example.
74
71
 
75
-
76
72
  ## Contributing
77
73
 
78
74
  - [Development workflow](CONTRIBUTING.md#development-workflow)
@@ -15,79 +15,28 @@ buildscript {
15
15
  }
16
16
  }
17
17
 
18
- def reactNativeArchitectures() {
19
- def value = rootProject.getProperties().get("reactNativeArchitectures")
20
- return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
21
- }
22
18
 
23
19
  apply plugin: "com.android.library"
24
20
  apply plugin: "kotlin-android"
25
- apply from: '../nitrogen/generated/android/audioconcat+autolinking.gradle'
26
21
 
27
- // Note: Don't apply "com.facebook.react" plugin in library modules
28
- // as it triggers autolinking. Only the app module should autolink dependencies.
22
+ apply plugin: "com.facebook.react"
29
23
 
30
24
  def getExtOrIntegerDefault(name) {
31
25
  return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["AudioConcat_" + name]).toInteger()
32
26
  }
33
27
 
34
28
  android {
35
- namespace "com.margelo.nitro.audioconcat"
29
+ namespace "com.audioconcat"
36
30
 
37
31
  compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
38
32
 
39
33
  defaultConfig {
40
34
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
41
35
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
42
-
43
- externalNativeBuild {
44
- cmake {
45
- cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
46
- arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
47
- abiFilters (*reactNativeArchitectures())
48
-
49
- buildTypes {
50
- debug {
51
- cppFlags "-O1 -g"
52
- }
53
- release {
54
- cppFlags "-O2"
55
- }
56
- }
57
- }
58
- }
59
- }
60
-
61
- externalNativeBuild {
62
- cmake {
63
- path "CMakeLists.txt"
64
- }
65
- }
66
-
67
- packagingOptions {
68
- excludes = [
69
- "META-INF",
70
- "META-INF/**",
71
- "**/libc++_shared.so",
72
- "**/libfbjni.so",
73
- "**/libjsi.so",
74
- "**/libfolly_json.so",
75
- "**/libfolly_runtime.so",
76
- "**/libglog.so",
77
- "**/libhermes.so",
78
- "**/libhermes-executor-debug.so",
79
- "**/libhermes_executor.so",
80
- "**/libreactnative.so",
81
- "**/libreactnativejni.so",
82
- "**/libturbomodulejsijni.so",
83
- "**/libreact_nativemodule_core.so",
84
- "**/libjscexecutor.so"
85
- ]
86
36
  }
87
37
 
88
38
  buildFeatures {
89
39
  buildConfig true
90
- prefab true
91
40
  }
92
41
 
93
42
  buildTypes {
@@ -125,5 +74,4 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
125
74
  dependencies {
126
75
  implementation "com.facebook.react:react-android"
127
76
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
128
- implementation project(":react-native-nitro-modules")
129
77
  }
@@ -1,7 +1,10 @@
1
- package com.margelo.nitro.audioconcat
2
-
3
- import com.facebook.proguard.annotations.DoNotStrip
4
- import com.margelo.nitro.core.Promise
1
+ package com.audioconcat
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReadableArray
6
+ import com.facebook.react.bridge.ReadableMap
7
+ import com.facebook.react.module.annotations.ReactModule
5
8
  import android.media.MediaCodec
6
9
  import android.media.MediaCodecInfo
7
10
  import android.media.MediaExtractor
@@ -11,14 +14,21 @@ import java.io.File
11
14
  import java.nio.ByteBuffer
12
15
  import android.util.Log
13
16
 
14
- @DoNotStrip
15
- class AudioConcat : HybridAudioConcatSpec() {
16
- private data class AudioConfig(
17
+ @ReactModule(name = AudioConcatModule.NAME)
18
+ class AudioConcatModule(reactContext: ReactApplicationContext) :
19
+ NativeAudioConcatSpec(reactContext) {
20
+
21
+ private data class AudioConfig(
17
22
  val sampleRate: Int,
18
23
  val channelCount: Int,
19
24
  val bitRate: Int
20
25
  )
21
26
 
27
+ private sealed class AudioDataOrSilence {
28
+ data class AudioFile(val filePath: String) : AudioDataOrSilence()
29
+ data class Silence(val durationMs: Double) : AudioDataOrSilence()
30
+ }
31
+
22
32
  private fun extractAudioConfig(filePath: String): AudioConfig {
23
33
  val extractor = MediaExtractor()
24
34
  try {
@@ -112,7 +122,7 @@ private data class AudioConfig(
112
122
  audioTrackIndex = muxer.addTrack(newFormat)
113
123
  muxer.start()
114
124
  muxerStarted = true
115
- Log.d("AwesomeLibrary", "Encoder started, format: $newFormat")
125
+ Log.d("AudioConcat", "Encoder started, format: $newFormat")
116
126
  }
117
127
  MediaCodec.INFO_TRY_AGAIN_LATER -> {
118
128
  if (!endOfStream) {
@@ -270,29 +280,54 @@ private data class AudioConfig(
270
280
  }
271
281
  }
272
282
 
273
- override fun concatAudioFiles(data: Array<AudioDataOrSilence>, outputPath: String): Promise<String> {
274
- return Promise.async {
275
- if (data.isEmpty()) {
276
- throw Exception("Data array is empty")
283
+ private fun parseAudioData(data: ReadableArray): List<AudioDataOrSilence> {
284
+ val result = mutableListOf<AudioDataOrSilence>()
285
+ for (i in 0 until data.size()) {
286
+ val item = data.getMap(i)
287
+ if (item != null) {
288
+ if (item.hasKey("filePath")) {
289
+ val filePath = item.getString("filePath")
290
+ if (filePath != null) {
291
+ result.add(AudioDataOrSilence.AudioFile(filePath))
292
+ }
293
+ } else if (item.hasKey("durationMs")) {
294
+ result.add(AudioDataOrSilence.Silence(item.getDouble("durationMs")))
295
+ }
277
296
  }
297
+ }
298
+ return result
299
+ }
278
300
 
279
- Log.d("AwesomeLibrary", "Streaming merge of ${data.size} items")
280
- Log.d("AwesomeLibrary", "Output: $outputPath")
301
+ override fun getName(): String {
302
+ return NAME
303
+ }
304
+
305
+ override fun concatAudioFiles(data: ReadableArray, outputPath: String, promise: Promise) {
306
+ try {
307
+ if (data.size() == 0) {
308
+ promise.reject("EMPTY_DATA", "Data array is empty")
309
+ return
310
+ }
311
+
312
+ val parsedData = parseAudioData(data)
313
+ Log.d("AudioConcat", "Streaming merge of ${parsedData.size} items")
314
+ Log.d("AudioConcat", "Output: $outputPath")
281
315
 
282
316
  // Get audio config from first audio file
283
317
  var audioConfig: AudioConfig? = null
284
- for (item in data) {
285
- if (item is AudioDataOrSilence.First) {
286
- audioConfig = extractAudioConfig(item.value.filePath)
318
+ for (item in parsedData) {
319
+ if (item is AudioDataOrSilence.AudioFile) {
320
+ audioConfig = extractAudioConfig(item.filePath)
287
321
  break
288
322
  }
289
323
  }
290
324
 
291
325
  if (audioConfig == null) {
292
- throw Exception("No audio files found in data array")
326
+ promise.reject("NO_AUDIO_FILES", "No audio files found in data array")
327
+ return
293
328
  }
294
329
 
295
- Log.d("AwesomeLibrary", "Audio config: ${audioConfig.sampleRate}Hz, ${audioConfig.channelCount}ch, ${audioConfig.bitRate}bps")
330
+ Log.d("AudioConcat", "Audio config: ${audioConfig.sampleRate}Hz, ${audioConfig.channelCount}ch, ${audioConfig.bitRate}bps")
296
331
 
297
332
  // Delete existing output file
298
333
  val outputFile = File(outputPath)
@@ -310,19 +345,19 @@ private data class AudioConfig(
310
345
 
311
346
  try {
312
347
  // Process each item
313
- for ((index, item) in data.withIndex()) {
348
+ for ((index, item) in parsedData.withIndex()) {
314
349
  when (item) {
315
- is AudioDataOrSilence.First -> {
316
- val filePath = item.value.filePath
317
- Log.d("AwesomeLibrary", "Item $index: Streaming decode $filePath")
350
+ is AudioDataOrSilence.AudioFile -> {
351
+ val filePath = item.filePath
352
+ Log.d("AudioConcat", "Item $index: Streaming decode $filePath")
318
353
 
319
- val isLastFile = (index == data.size - 1)
354
+ val isLastFile = (index == parsedData.size - 1)
320
355
  streamDecodeAudioFile(filePath, encoder, isLastFile)
321
356
  }
322
357
 
323
- is AudioDataOrSilence.Second -> {
324
- val durationMs = item.value.durationMs
325
- Log.d("AwesomeLibrary", "Item $index: Streaming silence ${durationMs}ms")
358
+ is AudioDataOrSilence.Silence -> {
359
+ val durationMs = item.durationMs
360
+ Log.d("AudioConcat", "Item $index: Streaming silence ${durationMs}ms")
326
361
 
327
362
  streamEncodeSilence(
328
363
  durationMs,
@@ -336,14 +371,21 @@ private data class AudioConfig(
336
371
 
337
372
  // Finish encoding
338
373
  encoder.finish()
339
- Log.d("AwesomeLibrary", "Successfully merged audio to $outputPath")
374
+ Log.d("AudioConcat", "Successfully merged audio to $outputPath")
375
+ promise.resolve(outputPath)
340
376
 
341
377
  } catch (e: Exception) {
342
- Log.e("AwesomeLibrary", "Error during streaming merge: ${e.message}", e)
343
- throw e
378
+ Log.e("AudioConcat", "Error during streaming merge: ${e.message}", e)
379
+ promise.reject("MERGE_ERROR", e.message, e)
344
380
  }
345
381
 
346
- outputPath
382
+ } catch (e: Exception) {
383
+ Log.e("AudioConcat", "Error parsing data: ${e.message}", e)
384
+ promise.reject("PARSE_ERROR", e.message, e)
347
385
  }
348
386
  }
387
+
388
+ companion object {
389
+ const val NAME = "AudioConcat"
390
+ }
349
391
  }
@@ -0,0 +1,33 @@
1
+ package com.audioconcat
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class AudioConcatPackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == AudioConcatModule.NAME) {
13
+ AudioConcatModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
20
+ return ReactModuleInfoProvider {
21
+ val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
22
+ moduleInfos[AudioConcatModule.NAME] = ReactModuleInfo(
23
+ AudioConcatModule.NAME,
24
+ AudioConcatModule.NAME,
25
+ false, // canOverrideExistingModule
26
+ false, // needsEagerInit
27
+ false, // isCxxModule
28
+ true // isTurboModule
29
+ )
30
+ moduleInfos
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,5 @@
1
+ #import <AudioConcatSpec/AudioConcatSpec.h>
2
+
3
+ @interface AudioConcat : NSObject <NativeAudioConcatSpec>
4
+
5
+ @end
@@ -0,0 +1,104 @@
1
+ #import "AudioConcat.h"
2
+ #import <AVFoundation/AVFoundation.h>
3
+
4
+ @implementation AudioConcat
5
+
6
+ - (void)concatAudioFiles:(NSArray *)data
7
+ outputPath:(NSString *)outputPath
8
+ resolve:(RCTPromiseResolveBlock)resolve
9
+ reject:(RCTPromiseRejectBlock)reject
10
+ {
11
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
12
+ @try {
13
+ AVMutableComposition *composition = [AVMutableComposition composition];
14
+ AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
15
+ preferredTrackID:kCMPersistentTrackID_Invalid];
16
+
17
+ CMTime currentTime = kCMTimeZero;
18
+
19
+ for (NSDictionary *item in data) {
20
+ if (item[@"filePath"]) {
21
+ // Add audio file
22
+ NSString *filePath = item[@"filePath"];
23
+ NSURL *fileURL = [NSURL fileURLWithPath:filePath];
24
+
25
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
26
+ NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeAudio];
27
+
28
+ if (tracks.count == 0) {
29
+ reject(@"ERR_NO_AUDIO_TRACK", [NSString stringWithFormat:@"No audio track found in file: %@", filePath], nil);
30
+ return;
31
+ }
32
+
33
+ AVAssetTrack *track = tracks[0];
34
+ CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
35
+
36
+ NSError *error = nil;
37
+ [audioTrack insertTimeRange:timeRange
38
+ ofTrack:track
39
+ atTime:currentTime
40
+ error:&error];
41
+
42
+ if (error) {
43
+ reject(@"ERR_INSERT_TRACK", error.localizedDescription, error);
44
+ return;
45
+ }
46
+
47
+ currentTime = CMTimeAdd(currentTime, asset.duration);
48
+
49
+ } else if (item[@"durationMs"]) {
50
+ // Add silence period
51
+ NSNumber *durationMs = item[@"durationMs"];
52
+ double durationSeconds = [durationMs doubleValue] / 1000.0;
53
+ CMTime silenceDuration = CMTimeMakeWithSeconds(durationSeconds, 600);
54
+
55
+ currentTime = CMTimeAdd(currentTime, silenceDuration);
56
+ }
57
+ }
58
+
59
+ // Export the composition
60
+ NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
61
+
62
+ // Remove existing file if it exists
63
+ [[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
64
+
65
+ AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition
66
+ presetName:AVAssetExportPresetAppleM4A];
67
+ exportSession.outputURL = outputURL;
68
+ exportSession.outputFileType = AVFileTypeAppleM4A;
69
+
70
+ [exportSession exportAsynchronouslyWithCompletionHandler:^{
71
+ switch (exportSession.status) {
72
+ case AVAssetExportSessionStatusCompleted:
73
+ resolve(outputPath);
74
+ break;
75
+ case AVAssetExportSessionStatusFailed:
76
+ reject(@"ERR_EXPORT_FAILED", exportSession.error.localizedDescription, exportSession.error);
77
+ break;
78
+ case AVAssetExportSessionStatusCancelled:
79
+ reject(@"ERR_EXPORT_CANCELLED", @"Export was cancelled", nil);
80
+ break;
81
+ default:
82
+ reject(@"ERR_EXPORT_UNKNOWN", @"Unknown export error", nil);
83
+ break;
84
+ }
85
+ }];
86
+
87
+ } @catch (NSException *exception) {
88
+ reject(@"ERR_EXCEPTION", exception.reason, nil);
89
+ }
90
+ });
91
+ }
92
+
93
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
94
+ (const facebook::react::ObjCTurboModule::InitParams &)params
95
+ {
96
+ return std::make_shared<facebook::react::NativeAudioConcatSpecJSI>(params);
97
+ }
98
+
99
+ + (NSString *)moduleName
100
+ {
101
+ return @"AudioConcat";
102
+ }
103
+
104
+ @end
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('AudioConcat');
5
+ //# sourceMappingURL=NativeAudioConcat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeAudioConcat.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAWpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,aAAa,CAAC","ignoreList":[]}
@@ -1,33 +1,7 @@
1
1
  "use strict";
2
2
 
3
- import { NitroModules } from 'react-native-nitro-modules';
4
- const AudioConcatHybridObject = NitroModules.createHybridObject('AudioConcat');
5
-
6
- /**
7
- * Concat audio files and silence periods into a single output file.
8
- *
9
- * @param data - Array of audio files and silence periods to merge.
10
- * Each item can be either:
11
- * - `{ filePath: string }` for an audio file
12
- * - `{ durationMs: number }` for a silence period
13
- * @param outputPath - Absolute path where the merged audio file will be saved (M4A format)
14
- * @returns Promise that resolves with the output file path
15
- *
16
- * @example
17
- * ```typescript
18
- * const data = [
19
- * { filePath: '/path/to/audio1.m4a' },
20
- * { durationMs: 500 }, // 500ms silence
21
- * { filePath: '/path/to/audio2.m4a' },
22
- * { durationMs: 1000 }, // 1 second silence
23
- * { filePath: '/path/to/audio3.m4a' }
24
- * ];
25
- * const output = '/path/to/merged.m4a';
26
- * const result = await concatAudioFiles(data, output);
27
- * console.log('concat file:', result);
28
- * ```
29
- */
3
+ import AudioConcat from "./NativeAudioConcat.js";
30
4
  export function concatAudioFiles(data, outputPath) {
31
- return AudioConcatHybridObject.concatAudioFiles(data, outputPath);
5
+ return AudioConcat.concatAudioFiles(data, outputPath);
32
6
  }
33
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NitroModules","AudioConcatHybridObject","createHybridObject","concatAudioFiles","data","outputPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAGzD,MAAMC,uBAAuB,GAC3BD,YAAY,CAACE,kBAAkB,CAAc,aAAa,CAAC;;AAE7D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAC9BC,IAA0B,EAC1BC,UAAkB,EACD;EACjB,OAAOJ,uBAAuB,CAACE,gBAAgB,CAACC,IAAI,EAAEC,UAAU,CAAC;AACnE","ignoreList":[]}
1
+ {"version":3,"names":["AudioConcat","concatAudioFiles","data","outputPath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,WAAW,MAAM,wBAAqB;AAI7C,OAAO,SAASC,gBAAgBA,CAC9BC,IAA0D,EAC1DC,UAAkB,EACD;EACjB,OAAOH,WAAW,CAACC,gBAAgB,CAACC,IAAI,EAAEC,UAAU,CAAC;AACvD","ignoreList":[]}
@@ -0,0 +1,12 @@
1
+ import { type TurboModule } from 'react-native';
2
+ export type AudioDataOrSilence = {
3
+ filePath: string;
4
+ } | {
5
+ durationMs: number;
6
+ };
7
+ export interface Spec extends TurboModule {
8
+ concatAudioFiles(data: AudioDataOrSilence[], outputPath: string): Promise<string>;
9
+ }
10
+ declare const _default: Spec;
11
+ export default _default;
12
+ //# sourceMappingURL=NativeAudioConcat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeAudioConcat.d.ts","sourceRoot":"","sources":["../../../src/NativeAudioConcat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,MAAM,kBAAkB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,gBAAgB,CACd,IAAI,EAAE,kBAAkB,EAAE,EAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAAC;CACpB;;AAED,wBAAqE"}
@@ -1,28 +1,7 @@
1
- import type { AudioDataOrSilence } from './AudioConcat.nitro';
2
- /**
3
- * Concat audio files and silence periods into a single output file.
4
- *
5
- * @param data - Array of audio files and silence periods to merge.
6
- * Each item can be either:
7
- * - `{ filePath: string }` for an audio file
8
- * - `{ durationMs: number }` for a silence period
9
- * @param outputPath - Absolute path where the merged audio file will be saved (M4A format)
10
- * @returns Promise that resolves with the output file path
11
- *
12
- * @example
13
- * ```typescript
14
- * const data = [
15
- * { filePath: '/path/to/audio1.m4a' },
16
- * { durationMs: 500 }, // 500ms silence
17
- * { filePath: '/path/to/audio2.m4a' },
18
- * { durationMs: 1000 }, // 1 second silence
19
- * { filePath: '/path/to/audio3.m4a' }
20
- * ];
21
- * const output = '/path/to/merged.m4a';
22
- * const result = await concatAudioFiles(data, output);
23
- * console.log('concat file:', result);
24
- * ```
25
- */
26
- export declare function concatAudioFiles(data: AudioDataOrSilence[], outputPath: string): Promise<string>;
27
- export type { AudioDataOrSilence } from './AudioConcat.nitro';
1
+ export type { AudioDataOrSilence } from './NativeAudioConcat';
2
+ export declare function concatAudioFiles(data: Array<{
3
+ filePath: string;
4
+ } | {
5
+ durationMs: number;
6
+ }>, outputPath: string): Promise<string>;
28
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAK3E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,kBAAkB,EAAE,EAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1D,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB"}