react-native-audio-concat 0.8.0 → 0.9.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.
package/README.md CHANGED
@@ -5,6 +5,7 @@ Concatenate audio files and silence periods into a single audio file for React N
5
5
  ## Features
6
6
 
7
7
  - ✅ Concat multiple audio files with silence periods
8
+ - ✅ Convert audio files to M4A format with AAC encoding
8
9
  - ✅ Support for iOS and Android
9
10
  - ✅ Output in M4A format
10
11
 
@@ -26,6 +27,8 @@ No additional steps required.
26
27
 
27
28
  ## Usage
28
29
 
30
+ ### Concatenate Audio Files
31
+
29
32
  ```typescript
30
33
  import { concatAudioFiles } from 'react-native-audio-concat';
31
34
 
@@ -48,6 +51,22 @@ try {
48
51
  }
49
52
  ```
50
53
 
54
+ ### Convert Audio to M4A
55
+
56
+ ```typescript
57
+ import { convertToM4a } from 'react-native-audio-concat';
58
+
59
+ const inputPath = '/path/to/audio.mp3';
60
+ const outputPath = '/path/to/output.m4a';
61
+
62
+ try {
63
+ const result = await convertToM4a(inputPath, outputPath);
64
+ console.log('Converted to M4A:', result);
65
+ } catch (error) {
66
+ console.error('Conversion failed:', error);
67
+ }
68
+ ```
69
+
51
70
  ## API
52
71
 
53
72
  ### `concatAudioFiles(data, outputPath)`
@@ -65,6 +84,24 @@ Concatenates audio files and silence periods into a single output file.
65
84
 
66
85
  - `Promise<string>` - Resolves with the output file path
67
86
 
87
+ ### `convertToM4a(inputPath, outputPath)`
88
+
89
+ Converts an audio file to M4A format with AAC encoding.
90
+
91
+ **Parameters:**
92
+
93
+ - `inputPath`: `string` - Absolute path to the input audio file (supports MP3, WAV, FLAC, OGG, M4A, and other common formats)
94
+ - `outputPath`: `string` - Absolute path where the M4A file will be saved
95
+
96
+ **Returns:**
97
+
98
+ - `Promise<string>` - Resolves with the output file path
99
+
100
+ **Notes:**
101
+
102
+ - On Android: Uses FFmpeg with AAC codec at 128kbps bitrate
103
+ - On iOS: Uses AVFoundation's native M4A export preset
104
+
68
105
  ## Example
69
106
 
70
107
  Check out the [example app](example/) for a complete working example.
@@ -115,6 +115,49 @@ class AudioConcatModule(reactContext: ReactApplicationContext) :
115
115
  }
116
116
  }
117
117
 
118
+ override fun convertToM4a(inputPath: String, outputPath: String, promise: Promise) {
119
+ try {
120
+ // Check if input file exists
121
+ val inputFile = File(inputPath)
122
+ if (!inputFile.exists()) {
123
+ promise.reject("INPUT_NOT_FOUND", "Input file does not exist: $inputPath")
124
+ return
125
+ }
126
+
127
+ Log.d("AudioConcat", "Converting to M4A: $inputPath")
128
+ Log.d("AudioConcat", "Output: $outputPath")
129
+
130
+ // Delete existing output file
131
+ val outputFile = File(outputPath)
132
+ if (outputFile.exists()) {
133
+ outputFile.delete()
134
+ }
135
+
136
+ // Build FFmpeg command for M4A conversion with AAC codec
137
+ val command = "-y -i \"$inputPath\" -c:a aac -b:a 128k -f mp4 \"$outputPath\" -loglevel level+error"
138
+ Log.d("AudioConcat", "FFmpeg command: $command")
139
+
140
+ // Execute FFmpeg command
141
+ FFmpegKit.executeAsync(command) { session ->
142
+ val returnCode = session.returnCode
143
+ if (ReturnCode.isSuccess(returnCode)) {
144
+ Log.d("AudioConcat", "Successfully converted to M4A: $outputPath")
145
+ promise.resolve(outputPath)
146
+ } else {
147
+ val output = session.output
148
+ val error = session.failStackTrace
149
+ Log.e("AudioConcat", "FFmpeg failed: $output")
150
+ Log.e("AudioConcat", "Error: $error")
151
+ promise.reject("FFMPEG_ERROR", "FFmpeg conversion failed: $output", Exception(error))
152
+ }
153
+ }
154
+
155
+ } catch (e: Exception) {
156
+ Log.e("AudioConcat", "Error converting to M4A: ${e.message}", e)
157
+ promise.reject("CONVERSION_ERROR", e.message, e)
158
+ }
159
+ }
160
+
118
161
  companion object {
119
162
  const val NAME = "AudioConcat"
120
163
  }
@@ -90,6 +90,66 @@
90
90
  });
91
91
  }
92
92
 
93
+ - (void)convertToM4a:(NSString *)inputPath
94
+ outputPath:(NSString *)outputPath
95
+ resolve:(RCTPromiseResolveBlock)resolve
96
+ reject:(RCTPromiseRejectBlock)reject
97
+ {
98
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
99
+ @try {
100
+ NSURL *inputURL = [NSURL fileURLWithPath:inputPath];
101
+
102
+ // Check if input file exists
103
+ if (![[NSFileManager defaultManager] fileExistsAtPath:inputPath]) {
104
+ reject(@"ERR_INPUT_NOT_FOUND", [NSString stringWithFormat:@"Input file does not exist: %@", inputPath], nil);
105
+ return;
106
+ }
107
+
108
+ // Load the input audio file
109
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
110
+ NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeAudio];
111
+
112
+ if (tracks.count == 0) {
113
+ reject(@"ERR_NO_AUDIO_TRACK", [NSString stringWithFormat:@"No audio track found in file: %@", inputPath], nil);
114
+ return;
115
+ }
116
+
117
+ // Setup output URL
118
+ NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
119
+
120
+ // Remove existing file if it exists
121
+ [[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
122
+
123
+ // Create export session
124
+ AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset
125
+ presetName:AVAssetExportPresetAppleM4A];
126
+ exportSession.outputURL = outputURL;
127
+ exportSession.outputFileType = AVFileTypeAppleM4A;
128
+
129
+ // Start export
130
+ [exportSession exportAsynchronouslyWithCompletionHandler:^{
131
+ switch (exportSession.status) {
132
+ case AVAssetExportSessionStatusCompleted:
133
+ resolve(outputPath);
134
+ break;
135
+ case AVAssetExportSessionStatusFailed:
136
+ reject(@"ERR_CONVERSION_FAILED", exportSession.error.localizedDescription, exportSession.error);
137
+ break;
138
+ case AVAssetExportSessionStatusCancelled:
139
+ reject(@"ERR_CONVERSION_CANCELLED", @"Conversion was cancelled", nil);
140
+ break;
141
+ default:
142
+ reject(@"ERR_CONVERSION_UNKNOWN", @"Unknown conversion error", nil);
143
+ break;
144
+ }
145
+ }];
146
+
147
+ } @catch (NSException *exception) {
148
+ reject(@"ERR_EXCEPTION", exception.reason, nil);
149
+ }
150
+ });
151
+ }
152
+
93
153
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
94
154
  (const facebook::react::ObjCTurboModule::InitParams &)params
95
155
  {
@@ -1 +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
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeAudioConcat.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAYpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,aAAa,CAAC","ignoreList":[]}
@@ -4,4 +4,7 @@ import AudioConcat from "./NativeAudioConcat.js";
4
4
  export function concatAudioFiles(data, outputPath) {
5
5
  return AudioConcat.concatAudioFiles(data, outputPath);
6
6
  }
7
+ export function convertToM4a(inputPath, outputPath) {
8
+ return AudioConcat.convertToM4a(inputPath, outputPath);
9
+ }
7
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
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":[]}
1
+ {"version":3,"names":["AudioConcat","concatAudioFiles","data","outputPath","convertToM4a","inputPath"],"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;AAEA,OAAO,SAASC,YAAYA,CAC1BC,SAAiB,EACjBF,UAAkB,EACD;EACjB,OAAOH,WAAW,CAACI,YAAY,CAACC,SAAS,EAAEF,UAAU,CAAC;AACxD","ignoreList":[]}
@@ -6,6 +6,7 @@ export type AudioDataOrSilence = {
6
6
  };
7
7
  export interface Spec extends TurboModule {
8
8
  concatAudioFiles(data: AudioDataOrSilence[], outputPath: string): Promise<string>;
9
+ convertToM4a(inputPath: string, outputPath: string): Promise<string>;
9
10
  }
10
11
  declare const _default: Spec;
11
12
  export default _default;
@@ -1 +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
+ {"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;IACnB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtE;;AAED,wBAAqE"}
@@ -4,4 +4,5 @@ export declare function concatAudioFiles(data: Array<{
4
4
  } | {
5
5
  durationMs: number;
6
6
  }>, outputPath: string): Promise<string>;
7
+ export declare function convertToM4a(inputPath: string, outputPath: string): Promise<string>;
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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"}
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;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-concat",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "audio-concat for react-native",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -7,6 +7,7 @@ export interface Spec extends TurboModule {
7
7
  data: AudioDataOrSilence[],
8
8
  outputPath: string
9
9
  ): Promise<string>;
10
+ convertToM4a(inputPath: string, outputPath: string): Promise<string>;
10
11
  }
11
12
 
12
13
  export default TurboModuleRegistry.getEnforcing<Spec>('AudioConcat');
package/src/index.tsx CHANGED
@@ -8,3 +8,10 @@ export function concatAudioFiles(
8
8
  ): Promise<string> {
9
9
  return AudioConcat.concatAudioFiles(data, outputPath);
10
10
  }
11
+
12
+ export function convertToM4a(
13
+ inputPath: string,
14
+ outputPath: string
15
+ ): Promise<string> {
16
+ return AudioConcat.convertToM4a(inputPath, outputPath);
17
+ }