sfxmix 1.0.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/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
package/README.md ADDED
@@ -0,0 +1,391 @@
1
+ # 🎧 SfxMix
2
+
3
+ **SfxMix** is a powerful and easy-to-use module for processing audio files using FFmpeg. It provides a fluent interface to concatenate, mix, insert silence, apply filters, and more! ✨
4
+
5
+ ---
6
+
7
+ ## 🚀 Features
8
+
9
+ - **Concatenate** multiple audio files seamlessly.
10
+ - **Mix** audio tracks with adjustable durations.
11
+ - **Insert silence** at any point in your audio sequence.
12
+ - **Apply filters** like echo, reverb, normalize, and more.
13
+ - **Parameterizable filters** for fine-grained control.
14
+ - **Fluent interface** for chaining multiple operations.
15
+
16
+ ---
17
+
18
+ ## 📦 Installation
19
+
20
+ Before installing **SfxMix**, ensure that **FFmpeg** is installed and accessible in your system's PATH.
21
+
22
+ ### Install FFmpeg
23
+
24
+ - **macOS:** Install via Homebrew
25
+ ```bash
26
+ brew install ffmpeg
27
+ ```
28
+ - **Windows:** Download from [FFmpeg official website](https://ffmpeg.org/download.html).
29
+ - **Linux:** Install via package manager
30
+ ```bash
31
+ sudo apt-get install ffmpeg
32
+ ```
33
+
34
+ ### Install SfxMix
35
+
36
+ ```bash
37
+ npm install sfxmix
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 📝 Usage
43
+
44
+ ```javascript
45
+ const SfxMix = require('sfxmix');
46
+
47
+ const processor = new SfxMix();
48
+
49
+ processor
50
+ .add('intro.mp3')
51
+ .silence(2000) // 2 seconds of silence
52
+ .add('main.mp3')
53
+ .filter('normalize', { i: -14, tp: -2.0, lra: 7.0 })
54
+ .mix('background.mp3', { duration: 'first' })
55
+ .save('final_output.mp3')
56
+ .then(() => {
57
+ console.log('Audio processing completed successfully! 🎉');
58
+ })
59
+ .catch((err) => {
60
+ console.error('Error during audio processing:', err);
61
+ });
62
+ ```
63
+ ---
64
+
65
+ ## 🔧 Examples
66
+
67
+ ### 1. Concatenate and Mix with Background Music
68
+
69
+ ```javascript
70
+ processor
71
+ .add('intro.mp3')
72
+ .add('chapter1.mp3')
73
+ .add('chapter2.mp3')
74
+ .mix('background_music.mp3', { duration: 'first' })
75
+ .save('audiobook_with_music.mp3');
76
+ ```
77
+
78
+ ### 2. Apply Multiple Filters
79
+
80
+ ```javascript
81
+ processor
82
+ .add('voiceover.mp3')
83
+ .filter('normalize', { i: -14 })
84
+ .filter('equalizer', { frequency: 3000, width: 1000, gain: 5 })
85
+ .save('processed_voiceover.mp3');
86
+ ```
87
+
88
+ ### 3. Insert Silence Between Tracks
89
+
90
+ ```javascript
91
+ processor
92
+ .add('track1.mp3')
93
+ .silence(2000)
94
+ .add('track2.mp3')
95
+ .silence(2000)
96
+ .add('track3.mp3')
97
+ .save('album_with_silence.mp3');
98
+ ```
99
+
100
+ ### 4. Apply Telephone Effect
101
+
102
+ ```javascript
103
+ processor
104
+ .add('dialogue.mp3')
105
+ .filter('telephone')
106
+ .save('telephone_effect.mp3');
107
+ ```
108
+
109
+ ### 5. Adjust Volume and Add Echo
110
+
111
+ ```javascript
112
+ processor
113
+ .add('announcement.mp3')
114
+ .filter('volume', { volume: 1.5 })
115
+ .filter('echo', { delay: 750, decay: 0.7 })
116
+ .save('enhanced_announcement.mp3');
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 📖 API Documentation
122
+
123
+ ### Class: `SfxMix`
124
+
125
+ #### Methods
126
+
127
+ - [`add(input)`](#addinput)
128
+ - [`mix(input, options)`](#mixinput-options)
129
+ - [`silence(duration)`](#silenceduration)
130
+ - [`filter(filterName, options)`](#filterfiltername-options)
131
+ - [`save(output)`](#saveoutput)
132
+
133
+ ---
134
+
135
+ ### `add(input)`
136
+
137
+ Adds an audio file to the processor for concatenation.
138
+
139
+ - **Parameters:**
140
+ - `input` (string): Path to the audio file.
141
+ - **Returns:** `SfxMix` (for chaining)
142
+
143
+ **Example:**
144
+
145
+ ```javascript
146
+ processor.add('part1.mp3').add('part2.mp3');
147
+ ```
148
+
149
+ ---
150
+
151
+ ### `mix(input, options)`
152
+
153
+ Mixes an audio file with the current audio.
154
+
155
+ - **Parameters:**
156
+ - `input` (string): Path to the audio file to mix.
157
+ - `options` (object): (Optional) Mixing options.
158
+ - `duration` (string): Determines the duration of the output. Can be `'longest'`, `'shortest'`, or `'first'`. Default is `'longest'`.
159
+ - **Returns:** `SfxMix` (for chaining)
160
+
161
+ **Example:**
162
+
163
+ ```javascript
164
+ processor.mix('sound_effect.wav', { duration: 'first' });
165
+ ```
166
+
167
+ ---
168
+
169
+ ### `silence(duration)`
170
+
171
+ Inserts silence into the audio sequence.
172
+
173
+ - **Parameters:**
174
+ - `duration` (number): Duration of silence in milliseconds.
175
+ - **Returns:** `SfxMix` (for chaining)
176
+
177
+ **Example:**
178
+
179
+ ```javascript
180
+ processor.silence(3000); // Inserts 3 seconds of silence
181
+ ```
182
+
183
+ ---
184
+
185
+ ### `filter(filterName, options)`
186
+
187
+ Applies an audio filter to the current audio.
188
+
189
+ - **Parameters:**
190
+ - `filterName` (string): Name of the filter to apply.
191
+ - `options` (object): (Optional) Filter-specific options.
192
+ - **Returns:** `SfxMix` (for chaining)
193
+
194
+ **Supported Filters:**
195
+
196
+ - [`normalize`](#filternormalize)
197
+ - [`telephone`](#filtertelephone)
198
+ - [`echo`](#filterecho)
199
+ - [`reverb`](#filterreverb)
200
+ - [`highpass`](#filterhighpass)
201
+ - [`lowpass`](#filterlowpass)
202
+ - [`volume`](#filtervolume)
203
+ - [`equalizer`](#filterequalizer)
204
+
205
+ ---
206
+
207
+ ### `save(output)`
208
+
209
+ Processes the audio according to the specified actions and saves the result.
210
+
211
+ - **Parameters:**
212
+ - `output` (string): Path to the output audio file.
213
+ - **Returns:** `Promise` (resolves when processing is complete)
214
+
215
+ **Example:**
216
+
217
+ ```javascript
218
+ processor.save('output.mp3');
219
+ ```
220
+
221
+ ---
222
+
223
+ ## 🎛️ Filters
224
+
225
+ ### Filter: `normalize`
226
+
227
+ Normalizes audio loudness to a specified target using the EBU R128 standard.
228
+
229
+ - **Options:**
230
+ - `tp` (number): Maximum true peak level in dBTP (default: `-1.5`).
231
+ - `i` (number): Target integrated loudness in LUFS (default: `-16`).
232
+ - `lra` (number): Loudness range in LU (default: `11`).
233
+
234
+ **Example:**
235
+
236
+ ```javascript
237
+ processor.filter('normalize', { i: -14, tp: -2.0, lra: 7.0 });
238
+ ```
239
+
240
+ ---
241
+
242
+ ### Filter: `telephone`
243
+
244
+ Applies a telephone effect by applying high-pass and low-pass filters.
245
+
246
+ - **Options:**
247
+ - `lowFreq` (number): High-pass filter cutoff frequency in Hz (default: `300`).
248
+ - `highFreq` (number): Low-pass filter cutoff frequency in Hz (default: `3400`).
249
+
250
+ **Example:**
251
+
252
+ ```javascript
253
+ processor.filter('telephone', { lowFreq: 400, highFreq: 3000 });
254
+ ```
255
+
256
+ ---
257
+
258
+ ### Filter: `echo`
259
+
260
+ Adds an echo effect to the audio.
261
+
262
+ - **Options:**
263
+ - `delay` (number): Echo delay in milliseconds (default: `500`).
264
+ - `decay` (number): Echo decay factor between `0` and `1` (default: `0.5`).
265
+
266
+ **Example:**
267
+
268
+ ```javascript
269
+ processor.filter('echo', { delay: 1000, decay: 0.6 });
270
+ ```
271
+
272
+ ---
273
+
274
+ ### Filter: `reverb`
275
+
276
+ Applies a reverb effect to the audio.
277
+
278
+ - **Options:** None
279
+
280
+ **Example:**
281
+
282
+ ```javascript
283
+ processor.filter('reverb');
284
+ ```
285
+
286
+ ---
287
+
288
+ ### Filter: `highpass`
289
+
290
+ Applies a high-pass filter to remove frequencies below the cutoff.
291
+
292
+ - **Options:**
293
+ - `frequency` (number): Cutoff frequency in Hz.
294
+
295
+ **Example:**
296
+
297
+ ```javascript
298
+ processor.filter('highpass', { frequency: 1000 });
299
+ ```
300
+
301
+ ---
302
+
303
+ ### Filter: `lowpass`
304
+
305
+ Applies a low-pass filter to remove frequencies above the cutoff.
306
+
307
+ - **Options:**
308
+ - `frequency` (number): Cutoff frequency in Hz.
309
+
310
+ **Example:**
311
+
312
+ ```javascript
313
+ processor.filter('lowpass', { frequency: 2000 });
314
+ ```
315
+
316
+ ---
317
+
318
+ ### Filter: `volume`
319
+
320
+ Adjusts the audio volume.
321
+
322
+ - **Options:**
323
+ - `volume` (number): Volume multiplier (e.g., `0.5` for 50%).
324
+
325
+ **Example:**
326
+
327
+ ```javascript
328
+ processor.filter('volume', { volume: 0.8 });
329
+ ```
330
+
331
+ ---
332
+
333
+ ### Filter: `equalizer`
334
+
335
+ Applies an equalizer effect to adjust specific frequencies.
336
+
337
+ - **Options:**
338
+ - `frequency` (number): Center frequency in Hz.
339
+ - `width` (number): Bandwidth in Hz.
340
+ - `gain` (number): Gain in dB (positive to boost, negative to reduce).
341
+
342
+ **Example:**
343
+
344
+ ```javascript
345
+ processor.filter('equalizer', { frequency: 1000, width: 200, gain: -10 });
346
+ ```
347
+
348
+ ---
349
+
350
+ ## ⚠️ Important Notes
351
+
352
+ - **FFmpeg Installation:** Ensure FFmpeg is installed and accessible in your system's PATH.
353
+ - **File Permissions:** The module creates and deletes temporary files during processing. Ensure the application has the necessary permissions.
354
+ - **Audio Formats:** The module assumes input files are in MP3 format. For other formats, adjust codec and format settings accordingly.
355
+ - **Error Handling:** Always handle rejections from the `save()` method to catch any processing errors.
356
+
357
+ ---
358
+
359
+ ## 📄 License
360
+
361
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
362
+
363
+ ---
364
+
365
+ ## 🙌 Contributing
366
+
367
+ Contributions are welcome! Feel free to submit a pull request or open an issue on GitHub.
368
+
369
+ ---
370
+
371
+ ## 💬 Support
372
+
373
+ If you encounter any issues or have questions, please open an issue on the [GitHub repository](https://github.com/clasen/SfxMix/issues).
374
+
375
+ ---
376
+
377
+ ## 📚 References
378
+
379
+ - [FFmpeg Documentation](https://ffmpeg.org/documentation.html)
380
+ - [fluent-ffmpeg GitHub](https://github.com/fluent-ffmpeg/node-fluent-ffmpeg)
381
+ - [EBU R128 Loudness Recommendation](https://tech.ebu.ch/docs/r/r128.pdf)
382
+
383
+ ---
384
+
385
+ ## 🌟 Acknowledgments
386
+
387
+ - Special thanks to the developers of FFmpeg and fluent-ffmpeg for their invaluable tools.
388
+
389
+ ---
390
+
391
+ Enjoy processing your audio with **SfxMix**! 🎶✨
@@ -0,0 +1,16 @@
1
+ import SfxMix from '../index.js';
2
+ const sfx = new SfxMix();
3
+
4
+ // Concatenate part1.mp3 and part2.mp3,
5
+ // then mix with glitches.wav for the duration of the concatenated audio
6
+ await sfx
7
+ .add('part1.mp3')
8
+ .add('part2.mp3')
9
+ .mix('glitches.mp3', { duration: 'first' })
10
+ .save('demo1_add_add_mix.mp3')
11
+ .then(() => {
12
+ console.log('Successfully exported: demo1_add_add_mix.mp3');
13
+ })
14
+ .catch((error) => {
15
+ console.error('Error during audio processing:', error);
16
+ });
@@ -0,0 +1,14 @@
1
+ import SfxMix from '../index.js';
2
+ const sfx = new SfxMix();
3
+
4
+ // Mix part1.mp3 with part2.mp3 and save the result
5
+ try {
6
+ await sfx
7
+ .add('part1.mp3')
8
+ .mix('part2.mp3')
9
+ .save('demo2_add_mix.mp3');
10
+
11
+ console.log('Successfully exported: demo2_add_mix.mp3');
12
+ } catch (error) {
13
+ console.error('Error exporting demo2_add_mix.mp3:', error);
14
+ }
@@ -0,0 +1,19 @@
1
+ import SfxMix from '../index.js';
2
+ const sfx = new SfxMix();
3
+
4
+ // Complex audio processing example
5
+ // This demonstrates adding files, inserting silence, mixing, and applying filters
6
+
7
+ try {
8
+ await sfx.add('part1.mp3')
9
+ .silence(2000) // Add 2 seconds of silence
10
+ .add('part2.mp3')
11
+ .mix('glitches.mp3', { duration: 'first' }) // Mix with glitches.mp3 for the duration of the first audio
12
+ .filter('telephone') // Apply telephone effect
13
+ .filter('normalize') // Normalize audio levels
14
+ .save('demo3_complex.mp3');
15
+
16
+ console.log('Successfully exported: demo3_complex.mp3');
17
+ } catch (error) {
18
+ console.error('Error exporting demo3_complex.mp3:', error);
19
+ }
@@ -0,0 +1,19 @@
1
+ import SfxMix from '../index.js';
2
+ const sfx = new SfxMix();
3
+
4
+ // Complex audio processing example
5
+ // This demonstrates mixing multiple files, adding another file, and normalizing the audio
6
+
7
+ try {
8
+ await sfx
9
+ .mix('part1.mp3')
10
+ .mix('part2.mp3')
11
+ .add('part2.mp3')
12
+ // Normalize the audio to -3 dB
13
+ .filter('normalize', { tp: -3 })
14
+ .save('demo4_mix_mix_add_normalized.mp3');
15
+
16
+ console.log('Successfully exported: demo4_mix_mix_add_normalized.mp3');
17
+ } catch (error) {
18
+ console.error('Error exporting demo4_mix_mix_add_normalized.mp3:', error);
19
+ }
Binary file
package/demo/part1.mp3 ADDED
Binary file
package/demo/part2.mp3 ADDED
Binary file
package/index.js ADDED
@@ -0,0 +1,253 @@
1
+ const ffmpeg = require('fluent-ffmpeg');
2
+ const fs = require('fs');
3
+
4
+ class SfxMix {
5
+ constructor() {
6
+ this.actions = [];
7
+ this.currentFile = null; // Keep track of the current audio file
8
+ }
9
+
10
+ add(input) {
11
+ this.actions.push({ type: 'add', input });
12
+ return this;
13
+ }
14
+
15
+ mix(input, options = {}) {
16
+ if (this.actions.length === 0) {
17
+ this.actions.push({ type: 'add', input });
18
+ } else {
19
+ this.actions.push({ type: 'mix', input, options });
20
+ }
21
+ return this;
22
+ }
23
+
24
+ silence(milliseconds) {
25
+ this.actions.push({ type: 'silence', duration: milliseconds });
26
+ return this;
27
+ }
28
+
29
+ filter(filterName, options = {}) {
30
+ this.actions.push({ type: 'filter', filterName, options });
31
+ return this;
32
+ }
33
+
34
+ save(output) {
35
+ return new Promise(async (resolve, reject) => {
36
+ try {
37
+ for (let action of this.actions) {
38
+ if (action.type === 'add') {
39
+ // Existing add logic
40
+ if (this.currentFile == null) {
41
+ this.currentFile = action.input;
42
+ } else {
43
+ const tempFile = `temp_concat_${Date.now()}.mp3`;
44
+ await concatenateAudioFiles([this.currentFile, action.input], tempFile);
45
+ if (isTempFile(this.currentFile)) {
46
+ fs.unlinkSync(this.currentFile);
47
+ }
48
+ this.currentFile = tempFile;
49
+ }
50
+ } else if (action.type === 'mix') {
51
+ // Existing mix logic
52
+ if (this.currentFile == null) {
53
+ throw new Error('No audio to mix with. Add or concatenate audio before mixing.');
54
+ }
55
+ const tempFile = `temp_mix_${Date.now()}.mp3`;
56
+ await mixAudioFiles(this.currentFile, action.input, tempFile, action.options);
57
+ if (isTempFile(this.currentFile)) {
58
+ fs.unlinkSync(this.currentFile);
59
+ }
60
+ this.currentFile = tempFile;
61
+ } else if (action.type === 'silence') {
62
+ // Existing silence logic
63
+ const tempSilenceFile = `temp_silence_${Date.now()}.mp3`;
64
+ await generateSilence(action.duration, tempSilenceFile);
65
+ if (this.currentFile == null) {
66
+ this.currentFile = tempSilenceFile;
67
+ } else {
68
+ const tempFile = `temp_concat_${Date.now()}.mp3`;
69
+ await concatenateAudioFiles([this.currentFile, tempSilenceFile], tempFile);
70
+ if (isTempFile(this.currentFile)) {
71
+ fs.unlinkSync(this.currentFile);
72
+ }
73
+ fs.unlinkSync(tempSilenceFile); // Remove the silence file
74
+ this.currentFile = tempFile;
75
+ }
76
+ } else if (action.type === 'filter') {
77
+ // New filter logic
78
+ if (this.currentFile == null) {
79
+ throw new Error('No audio to apply filter to. Add audio before applying filters.');
80
+ }
81
+ const tempFile = `temp_filter_${Date.now()}.mp3`;
82
+ await applyFilter(this.currentFile, action.filterName, action.options, tempFile);
83
+ if (isTempFile(this.currentFile)) {
84
+ fs.unlinkSync(this.currentFile);
85
+ }
86
+ this.currentFile = tempFile;
87
+ }
88
+ }
89
+ // Finalize output
90
+ fs.renameSync(this.currentFile, output);
91
+ resolve(output);
92
+ } catch (err) {
93
+ reject(err);
94
+ }
95
+ });
96
+ }
97
+ }
98
+
99
+ // Helper functions
100
+
101
+ function isTempFile(filename) {
102
+ return (
103
+ filename.startsWith('temp_concat_') ||
104
+ filename.startsWith('temp_mix_') ||
105
+ filename.startsWith('temp_silence_') ||
106
+ filename.startsWith('temp_filter_')
107
+ );
108
+ }
109
+
110
+ function concatenateAudioFiles(inputFiles, outputFile) {
111
+ return new Promise((resolve, reject) => {
112
+ const concatList = inputFiles.map(file => `file '${file}'`).join('\n');
113
+ const concatFile = `concat_${Date.now()}.txt`;
114
+ fs.writeFileSync(concatFile, concatList);
115
+
116
+ ffmpeg()
117
+ .input(concatFile)
118
+ .inputOptions(['-f', 'concat', '-safe', '0'])
119
+ .outputOptions(['-c', 'copy'])
120
+ .output(outputFile)
121
+ .on('end', () => {
122
+ fs.unlinkSync(concatFile);
123
+ resolve();
124
+ })
125
+ .on('error', (err) => {
126
+ fs.unlinkSync(concatFile);
127
+ reject(err);
128
+ })
129
+ .run();
130
+ });
131
+ }
132
+
133
+ function mixAudioFiles(inputFile1, inputFile2, outputFile, options = {}) {
134
+ return new Promise((resolve, reject) => {
135
+ const durationOption = options.duration || 'longest'; // Default to 'longest'
136
+ ffmpeg()
137
+ .input(inputFile1)
138
+ .input(inputFile2)
139
+ .complexFilter([
140
+ {
141
+ filter: 'amix',
142
+ options: {
143
+ inputs: 2,
144
+ duration: durationOption,
145
+ },
146
+ },
147
+ ])
148
+ .audioCodec('libmp3lame') // Ensure the codec is MP3
149
+ .format('mp3') // Ensure the format is MP3
150
+ .output(outputFile)
151
+ .on('end', () => resolve())
152
+ .on('error', (err) => reject(err))
153
+ .run();
154
+ });
155
+ }
156
+
157
+ function generateSilence(durationMs, outputFile) {
158
+ return new Promise((resolve, reject) => {
159
+ const durationSec = durationMs / 1000;
160
+ ffmpeg()
161
+ .input('anullsrc=channel_layout=stereo:sample_rate=44100')
162
+ .inputOptions(['-f', 'lavfi', '-t', `${durationSec}`])
163
+ .audioCodec('libmp3lame')
164
+ .format('mp3')
165
+ .output(outputFile)
166
+ .on('end', () => {
167
+ if (fs.existsSync(outputFile)) {
168
+ resolve();
169
+ } else {
170
+ reject(new Error(`Failed to generate silence file: ${outputFile}`));
171
+ }
172
+ })
173
+ .on('error', (err) => reject(err))
174
+ .run();
175
+ });
176
+ }
177
+
178
+ function applyFilter(inputFile, filterName, options, outputFile) {
179
+ return new Promise((resolve, reject) => {
180
+ const filterChain = getFilterChain(filterName, options);
181
+ if (!filterChain) {
182
+ return reject(new Error(`Unknown filter: ${filterName}`));
183
+ }
184
+
185
+ ffmpeg()
186
+ .input(inputFile)
187
+ .audioFilters(filterChain)
188
+ .audioCodec('libmp3lame')
189
+ .format('mp3')
190
+ .output(outputFile)
191
+ .on('end', () => resolve())
192
+ .on('error', (err) => reject(err))
193
+ .run();
194
+ });
195
+ }
196
+
197
+ function getFilterChain(filterName, options) {
198
+ switch (filterName) {
199
+ case 'normalize':
200
+ // Normalize audio using loudnorm filter with parameters
201
+ // Options: i (target integrated loudness), tp (true peak), lra (loudness range)
202
+ const i = options.i || -16;
203
+ const tp = options.tp || -1.5;
204
+ const lra = options.lra || 11;
205
+ return `loudnorm=I=${i}:TP=${tp}:LRA=${lra}:print_format=none`;
206
+ case 'telephone':
207
+ // Telephone effect with parameters
208
+ // Options: lowFreq (default 300), highFreq (default 3400)
209
+ const lowFreq = options.lowFreq || 300;
210
+ const highFreq = options.highFreq || 3400;
211
+ return `highpass=f=${lowFreq}, lowpass=f=${highFreq}`;
212
+ case 'echo':
213
+ // Default echo parameters: delay=500ms, decay=0.5
214
+ const echoDelay = options.delay || 500;
215
+ const echoDecay = options.decay || 0.5;
216
+ return `aecho=0.8:0.88:${echoDelay}:${echoDecay}`;
217
+ case 'reverb':
218
+ // Simple reverb effect
219
+ return 'areverb';
220
+ case 'highpass':
221
+ // High-pass filter: cutoff frequency in Hz
222
+ if (options.frequency) {
223
+ return `highpass=f=${options.frequency}`;
224
+ } else {
225
+ throw new Error('High-pass filter requires "frequency" option.');
226
+ }
227
+ case 'lowpass':
228
+ // Low-pass filter: cutoff frequency in Hz
229
+ if (options.frequency) {
230
+ return `lowpass=f=${options.frequency}`;
231
+ } else {
232
+ throw new Error('Low-pass filter requires "frequency" option.');
233
+ }
234
+ case 'volume':
235
+ // Volume adjustment: volume multiplier (e.g., 0.5 for 50%)
236
+ if (options.volume !== undefined) {
237
+ return `volume=${options.volume}`;
238
+ } else {
239
+ throw new Error('Volume filter requires "volume" option.');
240
+ }
241
+ case 'equalizer':
242
+ // Equalizer filter: frequency, width, gain
243
+ if (options.frequency && options.width && options.gain !== undefined) {
244
+ return `equalizer=f=${options.frequency}:width_type=h:width=${options.width}:g=${options.gain}`;
245
+ } else {
246
+ throw new Error('Equalizer filter requires "frequency", "width", and "gain" options.');
247
+ }
248
+ default:
249
+ return null;
250
+ }
251
+ }
252
+
253
+ module.exports = SfxMix;
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "sfxmix",
3
+ "description": "🎧 SfxMix - powerful and easy-to-use module for processing audio",
4
+ "version": "1.0.0",
5
+ "main": "index.js",
6
+ "dependencies": {
7
+ "fluent-ffmpeg": "^2.1.3"
8
+ },
9
+ "devDependencies": {},
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/clasen/SfxMix.git"
13
+ },
14
+ "keywords": [
15
+ "audio",
16
+ "concat",
17
+ "mix",
18
+ "silence",
19
+ "effects",
20
+ "filters",
21
+ "mp3",
22
+ "wav",
23
+ "echo",
24
+ "delay",
25
+ "telephone",
26
+ "equalizer",
27
+ "volume",
28
+ "normalize",
29
+ "ffmpeg",
30
+ "fluent-ffmpeg"
31
+ ],
32
+ "author": "Martin Clasen",
33
+ "license": "MIT",
34
+ "bugs": {
35
+ "url": "https://github.com/clasen/SfxMix/issues"
36
+ }
37
+ }