sfxmix 1.1.4 → 1.1.6

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.
@@ -4,10 +4,10 @@ const sfx = new SfxMix();
4
4
  // Slow down part1.mp3 by 25%
5
5
  await sfx
6
6
  .add('part1.mp3')
7
- .filter('echo', { delays: [1000, 2000, 3000, 4000], decays: [0.5, 0.5, 0.5, 0.5] })
8
- .save('demo7_part1_echo.mp3')
7
+ .filter('tempo', { x: 0.75 })
8
+ .save('demo5_part1_slow.mp3')
9
9
  .then(() => {
10
- console.log('Successfully exported: demo7_part1_echo.mp3');
10
+ console.log('Successfully exported: demo5_part1_slow.mp3');
11
11
  })
12
12
  .catch((error) => {
13
13
  console.error('Error during audio processing:', error);
package/demo/demo_6.mjs CHANGED
@@ -4,10 +4,10 @@ const sfx = new SfxMix();
4
4
  // Slow down part1.mp3 by 25%
5
5
  await sfx
6
6
  .add('part1.mp3')
7
- .filter('tempo', { x: 0.75 })
8
- .save('demo6_part1_slow.mp3')
7
+ .filter('echo', { delays: [1000, 2000, 3000, 4000], decays: [0.5, 0.5, 0.5, 0.5] })
8
+ .save('demo6_part1_echo.mp3')
9
9
  .then(() => {
10
- console.log('Successfully exported: demo6_part1_slow.mp3');
10
+ console.log('Successfully exported: demo6_part1_echo.mp3');
11
11
  })
12
12
  .catch((error) => {
13
13
  console.error('Error during audio processing:', error);
package/index.js CHANGED
@@ -1,11 +1,26 @@
1
1
  const ffmpeg = require('fluent-ffmpeg');
2
2
  const fs = require('fs');
3
+ const path = require('path');
3
4
  const { v4: uuidv4 } = require('uuid');
4
5
 
5
6
  class SfxMix {
6
- constructor() {
7
+ constructor(config = {}) {
7
8
  this.actions = [];
8
- this.currentFile = null; // Keep track of the current audio file
9
+ this.currentFile = null;
10
+ this.TMP_DIR = path.resolve(config.tmpDir || path.join(__dirname, 'tmp'));
11
+
12
+ // Ensure the temporary directory exists and is writable
13
+ try {
14
+ if (!fs.existsSync(this.TMP_DIR)) {
15
+ fs.mkdirSync(this.TMP_DIR, { recursive: true });
16
+ }
17
+ fs.accessSync(this.TMP_DIR, fs.constants.W_OK);
18
+ // console.log(`Temporary directory set to: ${this.TMP_DIR}`);
19
+ } catch (err) {
20
+ console.error(`Error accessing temporary directory: ${this.TMP_DIR}`);
21
+ console.error(err);
22
+ throw new Error('Unable to access or create temporary directory');
23
+ }
9
24
  }
10
25
 
11
26
  add(input) {
@@ -41,9 +56,9 @@ class SfxMix {
41
56
  if (this.currentFile == null) {
42
57
  this.currentFile = action.input;
43
58
  } else {
44
- const tempFile = `temp_concat_${uuidv4()}.mp3`;
45
- await concatenateAudioFiles([this.currentFile, action.input], tempFile);
46
- if (isTempFile(this.currentFile)) {
59
+ const tempFile = path.join(this.TMP_DIR, `temp_concat_${uuidv4()}.mp3`);
60
+ await this.concatenateAudioFiles([this.currentFile, action.input], tempFile);
61
+ if (this.isTempFile(this.currentFile)) {
47
62
  fs.unlinkSync(this.currentFile);
48
63
  }
49
64
  this.currentFile = tempFile;
@@ -53,22 +68,22 @@ class SfxMix {
53
68
  if (this.currentFile == null) {
54
69
  throw new Error('No audio to mix with. Add or concatenate audio before mixing.');
55
70
  }
56
- const tempFile = `temp_mix_${uuidv4()}.mp3`;
57
- await mixAudioFiles(this.currentFile, action.input, tempFile, action.options);
58
- if (isTempFile(this.currentFile)) {
71
+ const tempFile = path.join(this.TMP_DIR, `temp_mix_${uuidv4()}.mp3`);
72
+ await this.mixAudioFiles(this.currentFile, action.input, tempFile, action.options);
73
+ if (this.isTempFile(this.currentFile)) {
59
74
  fs.unlinkSync(this.currentFile);
60
75
  }
61
76
  this.currentFile = tempFile;
62
77
  } else if (action.type === 'silence') {
63
78
  // Existing silence logic
64
- const tempSilenceFile = `temp_silence_${uuidv4()}.mp3`;
65
- await generateSilence(action.duration, tempSilenceFile);
79
+ const tempSilenceFile = path.join(this.TMP_DIR, `temp_silence_${uuidv4()}.mp3`);
80
+ await this.generateSilence(action.duration, tempSilenceFile);
66
81
  if (this.currentFile == null) {
67
82
  this.currentFile = tempSilenceFile;
68
83
  } else {
69
- const tempFile = `temp_concat_${uuidv4()}.mp3`;
70
- await concatenateAudioFiles([this.currentFile, tempSilenceFile], tempFile);
71
- if (isTempFile(this.currentFile)) {
84
+ const tempFile = path.join(this.TMP_DIR, `temp_concat_${uuidv4()}.mp3`);
85
+ await this.concatenateAudioFiles([this.currentFile, tempSilenceFile], tempFile);
86
+ if (this.isTempFile(this.currentFile)) {
72
87
  fs.unlinkSync(this.currentFile);
73
88
  }
74
89
  fs.unlinkSync(tempSilenceFile); // Remove the silence file
@@ -79,9 +94,9 @@ class SfxMix {
79
94
  if (this.currentFile == null) {
80
95
  throw new Error('No audio to apply filter to. Add audio before applying filters.');
81
96
  }
82
- const tempFile = `temp_filter_${uuidv4()}.mp3`;
83
- await applyFilter(this.currentFile, action.filterName, action.options, tempFile);
84
- if (isTempFile(this.currentFile)) {
97
+ const tempFile = path.join(this.TMP_DIR, `temp_filter_${uuidv4()}.mp3`);
98
+ await this.applyFilter(this.currentFile, action.filterName, action.options, tempFile);
99
+ if (this.isTempFile(this.currentFile)) {
85
100
  fs.unlinkSync(this.currentFile);
86
101
  }
87
102
  this.currentFile = tempFile;
@@ -106,234 +121,250 @@ class SfxMix {
106
121
  this.currentFile = null;
107
122
  return this;
108
123
  }
109
- }
110
124
 
111
- // Helper functions
125
+ // Move helper functions inside the class and use this.TMP_DIR
126
+ isTempFile(filename) {
127
+ return path.dirname(filename) === this.TMP_DIR && (
128
+ filename.includes('temp_concat_') ||
129
+ filename.includes('temp_mix_') ||
130
+ filename.includes('temp_silence_') ||
131
+ filename.includes('temp_filter_')
132
+ );
133
+ }
112
134
 
113
- function isTempFile(filename) {
114
- return (
115
- filename.startsWith('temp_concat_') ||
116
- filename.startsWith('temp_mix_') ||
117
- filename.startsWith('temp_silence_') ||
118
- filename.startsWith('temp_filter_')
119
- );
120
- }
135
+ concatenateAudioFiles(inputFiles, outputFile) {
136
+ return new Promise((resolve, reject) => {
137
+ // Use absolute paths for input files
138
+ const absoluteInputFiles = inputFiles.map(file => path.resolve(file));
139
+ const concatList = absoluteInputFiles.map(file => `file '${file}'`).join('\n');
140
+ const concatFile = path.join(this.TMP_DIR, `concat_${uuidv4()}.txt`);
121
141
 
122
- function concatenateAudioFiles(inputFiles, outputFile) {
123
- return new Promise((resolve, reject) => {
124
- const concatList = inputFiles.map(file => `file '${file}'`).join('\n');
125
- const concatFile = `concat_${uuidv4()}.txt`;
126
- fs.writeFileSync(concatFile, concatList);
127
-
128
- ffmpeg()
129
- .input(concatFile)
130
- .inputOptions(['-f', 'concat', '-safe', '0'])
131
- .outputOptions(['-c', 'copy'])
132
- .output(outputFile)
133
- .on('end', () => {
134
- fs.unlinkSync(concatFile);
135
- resolve();
136
- })
137
- .on('error', (err) => {
138
- fs.unlinkSync(concatFile);
142
+ try {
143
+ fs.writeFileSync(concatFile, concatList);
144
+ // console.log(`Concat file created at: ${concatFile}`);
145
+ // console.log(`Concat file contents:\n${concatList}`);
146
+
147
+ ffmpeg()
148
+ .input(concatFile)
149
+ .inputOptions(['-f', 'concat', '-safe', '0'])
150
+ .outputOptions(['-c', 'copy'])
151
+ .output(outputFile)
152
+ .on('start', (commandLine) => {
153
+ // console.log('Spawned FFmpeg with command: ' + commandLine);
154
+ })
155
+ .on('end', () => {
156
+ fs.unlinkSync(concatFile);
157
+ // console.log(`Concatenation complete. Output file: ${outputFile}`);
158
+ resolve();
159
+ })
160
+ .on('error', (err) => {
161
+ console.error('FFmpeg error:', err);
162
+ if (fs.existsSync(concatFile)) {
163
+ fs.unlinkSync(concatFile);
164
+ }
165
+ reject(err);
166
+ })
167
+ .run();
168
+ } catch (err) {
169
+ console.error('Error in concatenateAudioFiles:', err);
139
170
  reject(err);
140
- })
141
- .run();
142
- });
143
- }
171
+ }
172
+ });
173
+ }
144
174
 
145
- function mixAudioFiles(inputFile1, inputFile2, outputFile, options = {}) {
146
- return new Promise((resolve, reject) => {
147
- const durationOption = options.duration || 'longest'; // Default to 'longest'
148
- ffmpeg()
149
- .input(inputFile1)
150
- .input(inputFile2)
151
- .complexFilter([
152
- {
153
- filter: 'amix',
154
- options: {
155
- inputs: 2,
156
- duration: durationOption,
175
+ mixAudioFiles(inputFile1, inputFile2, outputFile, options = {}) {
176
+ return new Promise((resolve, reject) => {
177
+ const durationOption = options.duration || 'longest'; // Default to 'longest'
178
+ ffmpeg()
179
+ .input(inputFile1)
180
+ .input(inputFile2)
181
+ .complexFilter([
182
+ {
183
+ filter: 'amix',
184
+ options: {
185
+ inputs: 2,
186
+ duration: durationOption,
187
+ },
157
188
  },
158
- },
159
- ])
160
- .audioCodec('libmp3lame') // Ensure the codec is MP3
161
- .format('mp3') // Ensure the format is MP3
162
- .output(outputFile)
163
- .on('end', () => resolve())
164
- .on('error', (err) => reject(err))
165
- .run();
166
- });
167
- }
189
+ ])
190
+ .audioCodec('libmp3lame') // Ensure the codec is MP3
191
+ .format('mp3') // Ensure the format is MP3
192
+ .output(outputFile)
193
+ .on('end', () => resolve())
194
+ .on('error', (err) => reject(err))
195
+ .run();
196
+ });
197
+ }
168
198
 
169
- function generateSilence(durationMs, outputFile) {
170
- return new Promise((resolve, reject) => {
171
- const durationSec = durationMs / 1000;
172
- ffmpeg()
173
- .input('anullsrc=channel_layout=stereo:sample_rate=44100')
174
- .inputOptions(['-f', 'lavfi', '-t', `${durationSec}`])
175
- .audioCodec('libmp3lame')
176
- .format('mp3')
177
- .output(outputFile)
178
- .on('end', () => {
179
- if (fs.existsSync(outputFile)) {
180
- resolve();
181
- } else {
182
- reject(new Error(`Failed to generate silence file: ${outputFile}`));
183
- }
184
- })
185
- .on('error', (err) => reject(err))
186
- .run();
187
- });
188
- }
199
+ generateSilence(durationMs, outputFile) {
200
+ return new Promise((resolve, reject) => {
201
+ const durationSec = durationMs / 1000;
202
+ ffmpeg()
203
+ .input('anullsrc=channel_layout=stereo:sample_rate=44100')
204
+ .inputOptions(['-f', 'lavfi', '-t', `${durationSec}`])
205
+ .audioCodec('libmp3lame')
206
+ .format('mp3')
207
+ .output(outputFile)
208
+ .on('end', () => {
209
+ if (fs.existsSync(outputFile)) {
210
+ resolve();
211
+ } else {
212
+ reject(new Error(`Failed to generate silence file: ${outputFile}`));
213
+ }
214
+ })
215
+ .on('error', (err) => reject(err))
216
+ .run();
217
+ });
218
+ }
189
219
 
190
- function applyFilter(inputFile, filterName, options, outputFile) {
191
- return new Promise((resolve, reject) => {
192
- const filterChain = getFilterChain(filterName, options);
193
- if (!filterChain) {
194
- return reject(new Error(`Unknown filter: ${filterName}`));
195
- }
220
+ applyFilter(inputFile, filterName, options, outputFile) {
221
+ return new Promise((resolve, reject) => {
222
+ const filterChain = this.getFilterChain(filterName, options);
223
+ if (!filterChain) {
224
+ return reject(new Error(`Unknown filter: ${filterName}`));
225
+ }
196
226
 
197
- ffmpeg()
198
- .input(inputFile)
199
- .audioFilters(filterChain)
200
- .audioCodec('libmp3lame')
201
- .format('mp3')
202
- .output(outputFile)
203
- .on('end', () => resolve())
204
- .on('error', (err) => reject(err))
205
- .run();
206
- });
207
- }
227
+ ffmpeg()
228
+ .input(inputFile)
229
+ .audioFilters(filterChain)
230
+ .audioCodec('libmp3lame')
231
+ .format('mp3')
232
+ .output(outputFile)
233
+ .on('end', () => resolve())
234
+ .on('error', (err) => reject(err))
235
+ .run();
236
+ });
237
+ }
208
238
 
209
- function getFilterChain(filterName, options) {
210
- switch (filterName) {
211
- case 'normalize':
212
- // Normalize audio using loudnorm filter with parameters
213
- const tp = options.tp || -3;
214
- const i = options.i || -20;
215
- const lra = options.lra || 11;
216
- return `loudnorm=I=${i}:TP=${tp}:LRA=${lra}:print_format=none`;
217
-
218
- case 'telephone':
219
- // Telephone effect with parameters
220
- const lowFreq = options.lowFreq || 300;
221
- const highFreq = options.highFreq || 2800;
222
- return `highpass=f=${lowFreq}, lowpass=f=${highFreq}`;
223
-
224
- case 'echo':
225
- // Reverb effect using aecho filter
226
- const inGain = options.inGain !== undefined ? options.inGain : 0.8;
227
- const outGain = options.outGain !== undefined ? options.outGain : 0.9;
228
- const delays = options.delays !== undefined ? options.delays : [1, 200, 300, 400];
229
- const decays = options.decays !== undefined ? options.decays : [0.5, 0.5, 0.5, 0.5];
230
-
231
- const delaysStr = delays.join('|');
232
- const decaysStr = decays.join('|');
233
- return `aecho=${inGain}:${outGain}:${delaysStr}:${decaysStr}`;
234
-
235
- case 'highpass':
236
- // High-pass filter
237
- if (options.frequency) {
238
- return `highpass=f=${options.frequency}`;
239
- } else {
240
- throw new Error('High-pass filter requires "frequency" option.');
241
- }
239
+ getFilterChain(filterName, options) {
240
+ switch (filterName) {
241
+ case 'normalize':
242
+ // Normalize audio using loudnorm filter with parameters
243
+ const tp = options.tp || -3;
244
+ const i = options.i || -20;
245
+ const lra = options.lra || 11;
246
+ return `loudnorm=I=${i}:TP=${tp}:LRA=${lra}:print_format=none`;
247
+
248
+ case 'telephone':
249
+ // Telephone effect with parameters
250
+ const lowFreq = options.lowFreq || 300;
251
+ const highFreq = options.highFreq || 2800;
252
+ return `highpass=f=${lowFreq}, lowpass=f=${highFreq}`;
253
+
254
+ case 'echo':
255
+ // Reverb effect using aecho filter
256
+ const inGain = options.inGain !== undefined ? options.inGain : 0.8;
257
+ const outGain = options.outGain !== undefined ? options.outGain : 0.9;
258
+ const delays = options.delays !== undefined ? options.delays : [1, 200, 300, 400];
259
+ const decays = options.decays !== undefined ? options.decays : [0.5, 0.5, 0.5, 0.5];
260
+
261
+ const delaysStr = delays.join('|');
262
+ const decaysStr = decays.join('|');
263
+ return `aecho=${inGain}:${outGain}:${delaysStr}:${decaysStr}`;
264
+
265
+ case 'highpass':
266
+ // High-pass filter
267
+ if (options.frequency) {
268
+ return `highpass=f=${options.frequency}`;
269
+ } else {
270
+ throw new Error('High-pass filter requires "frequency" option.');
271
+ }
242
272
 
243
- case 'lowpass':
244
- // Low-pass filter
245
- if (options.frequency) {
246
- return `lowpass=f=${options.frequency}`;
247
- } else {
248
- throw new Error('Low-pass filter requires "frequency" option.');
249
- }
273
+ case 'lowpass':
274
+ // Low-pass filter
275
+ if (options.frequency) {
276
+ return `lowpass=f=${options.frequency}`;
277
+ } else {
278
+ throw new Error('Low-pass filter requires "frequency" option.');
279
+ }
250
280
 
251
- case 'volume':
252
- // Volume adjustment
253
- if (options.volume !== undefined) {
254
- return `volume=${options.volume}`;
255
- } else {
256
- throw new Error('Volume filter requires "volume" option.');
257
- }
281
+ case 'volume':
282
+ // Volume adjustment
283
+ if (options.volume !== undefined) {
284
+ return `volume=${options.volume}`;
285
+ } else {
286
+ throw new Error('Volume filter requires "volume" option.');
287
+ }
258
288
 
259
- case 'equalizer':
260
- // Equalizer filter
261
- if (options.frequency && options.width && options.gain !== undefined) {
262
- return `equalizer=f=${options.frequency}:width_type=h:width=${options.width}:g=${options.gain}`;
263
- } else {
264
- throw new Error('Equalizer filter requires "frequency", "width", and "gain" options.');
265
- }
289
+ case 'equalizer':
290
+ // Equalizer filter
291
+ if (options.frequency && options.width && options.gain !== undefined) {
292
+ return `equalizer=f=${options.frequency}:width_type=h:width=${options.width}:g=${options.gain}`;
293
+ } else {
294
+ throw new Error('Equalizer filter requires "frequency", "width", and "gain" options.');
295
+ }
266
296
 
267
- case 'flanger':
268
- // Flanger effect
269
- // Options: delay, depth, regen, width, speed, shape, phase, interp
270
- const flangerDelay = options.delay !== undefined ? options.delay : 0;
271
- const depth = options.depth !== undefined ? options.depth : 2;
272
- const regen = options.regen !== undefined ? options.regen : 0;
273
- const width = options.width !== undefined ? options.width : 71;
274
- const speed = options.speed !== undefined ? options.speed : 0.5;
275
- const shape = options.shape !== undefined ? options.shape : 'sine';
276
- const phase = options.phase !== undefined ? options.phase : 25;
277
- const interp = options.interp !== undefined ? options.interp : 'linear';
278
- return `aflanger=delay=${flangerDelay}:depth=${depth}:regen=${regen}:width=${width}:speed=${speed}:shape=${shape}:phase=${phase}:interp=${interp}`;
279
-
280
- case 'pitch':
281
- // Pitch shift effect
282
- // Options: pitch (in semitones)
283
- if (options.pitch !== undefined) {
284
- const pitch = options.pitch;
285
- return `asetrate=44100*${Math.pow(2, pitch / 12)},aresample=44100`;
286
- } else {
287
- throw new Error('Pitch filter requires "pitch" option.');
288
- }
297
+ case 'flanger':
298
+ // Flanger effect
299
+ // Options: delay, depth, regen, width, speed, shape, phase, interp
300
+ const flangerDelay = options.delay !== undefined ? options.delay : 0;
301
+ const depth = options.depth !== undefined ? options.depth : 2;
302
+ const regen = options.regen !== undefined ? options.regen : 0;
303
+ const width = options.width !== undefined ? options.width : 71;
304
+ const speed = options.speed !== undefined ? options.speed : 0.5;
305
+ const shape = options.shape !== undefined ? options.shape : 'sine';
306
+ const phase = options.phase !== undefined ? options.phase : 25;
307
+ const interp = options.interp !== undefined ? options.interp : 'linear';
308
+ return `aflanger=delay=${flangerDelay}:depth=${depth}:regen=${regen}:width=${width}:speed=${speed}:shape=${shape}:phase=${phase}:interp=${interp}`;
309
+
310
+ case 'pitch':
311
+ // Pitch shift effect
312
+ // Options: pitch (in semitones)
313
+ if (options.pitch !== undefined) {
314
+ const pitch = options.pitch;
315
+ return `asetrate=44100*${Math.pow(2, pitch / 12)},aresample=44100`;
316
+ } else {
317
+ throw new Error('Pitch filter requires "pitch" option.');
318
+ }
289
319
 
290
- case 'tremolo':
291
- // Tremolo effect
292
- // Options: frequency
293
- const frequency = options.frequency !== undefined ? options.frequency : 1;
294
- return `tremolo=f=${frequency}`;
295
-
296
- case 'phaser':
297
- // Phaser effect
298
- // Options: in_gain, out_gain, delay, decay, speed, type
299
- const in_gain = options.in_gain !== undefined ? options.in_gain : 0.4;
300
- const out_gain = options.out_gain !== undefined ? options.out_gain : 0.74;
301
- const delay = options.delay !== undefined ? options.delay : 3;
302
- const decay = options.decay !== undefined ? options.decay : 0.4;
303
- const speed_ph = options.speed !== undefined ? options.speed : 0.5;
304
- const type = options.type !== undefined ? options.type : 0;
305
- return `aphaser=in_gain=${in_gain}:out_gain=${out_gain}:delay=${delay}:decay=${decay}:speed=${speed_ph}:type=${type}`;
306
-
307
- case 'tempo':
308
- // Change the speed without changing the tone
309
- // Options: x (speed factor, between 0.5 and 2.0)
310
- if (options.x !== undefined) {
311
- const tempo = options.x;
312
- if (tempo < 0.5 || tempo > 2.0) {
313
- // If the value is outside the supported range, chain filters
314
- const tempos = [];
315
- let remainingTempo = tempo;
316
- while (remainingTempo < 0.5 || remainingTempo > 2.0) {
317
- if (remainingTempo < 0.5) {
318
- tempos.push(0.5);
319
- remainingTempo /= 0.5;
320
- } else {
321
- tempos.push(2.0);
322
- remainingTempo /= 2.0;
320
+ case 'tremolo':
321
+ // Tremolo effect
322
+ // Options: frequency
323
+ const frequency = options.frequency !== undefined ? options.frequency : 1;
324
+ return `tremolo=f=${frequency}`;
325
+
326
+ case 'phaser':
327
+ // Phaser effect
328
+ // Options: in_gain, out_gain, delay, decay, speed, type
329
+ const in_gain = options.in_gain !== undefined ? options.in_gain : 0.4;
330
+ const out_gain = options.out_gain !== undefined ? options.out_gain : 0.74;
331
+ const delay = options.delay !== undefined ? options.delay : 3;
332
+ const decay = options.decay !== undefined ? options.decay : 0.4;
333
+ const speed_ph = options.speed !== undefined ? options.speed : 0.5;
334
+ const type = options.type !== undefined ? options.type : 0;
335
+ return `aphaser=in_gain=${in_gain}:out_gain=${out_gain}:delay=${delay}:decay=${decay}:speed=${speed_ph}:type=${type}`;
336
+
337
+ case 'tempo':
338
+ // Change the speed without changing the tone
339
+ // Options: x (speed factor, between 0.5 and 2.0)
340
+ if (options.x !== undefined) {
341
+ const tempo = options.x;
342
+ if (tempo < 0.5 || tempo > 2.0) {
343
+ // If the value is outside the supported range, chain filters
344
+ const tempos = [];
345
+ let remainingTempo = tempo;
346
+ while (remainingTempo < 0.5 || remainingTempo > 2.0) {
347
+ if (remainingTempo < 0.5) {
348
+ tempos.push(0.5);
349
+ remainingTempo /= 0.5;
350
+ } else {
351
+ tempos.push(2.0);
352
+ remainingTempo /= 2.0;
353
+ }
323
354
  }
355
+ tempos.push(remainingTempo);
356
+ const atempoFilters = tempos.map(t => `atempo=${t}`).join(',');
357
+ return atempoFilters;
358
+ } else {
359
+ return `atempo=${tempo}`;
324
360
  }
325
- tempos.push(remainingTempo);
326
- const atempoFilters = tempos.map(t => `atempo=${t}`).join(',');
327
- return atempoFilters;
328
361
  } else {
329
- return `atempo=${tempo}`;
362
+ throw new Error('The tempo filter requires the "x" option.');
330
363
  }
331
- } else {
332
- throw new Error('The tempo filter requires the "x" option.');
333
- }
334
364
 
335
- default:
336
- throw new Error(`Unknown filter: ${filterName}`);
365
+ default:
366
+ throw new Error(`Unknown filter: ${filterName}`);
367
+ }
337
368
  }
338
369
  }
339
370
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sfxmix",
3
3
  "description": "🎧 SfxMix - Powerful and easy-to-use module for processing audio.",
4
- "version": "1.1.4",
4
+ "version": "1.1.6",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
7
  "fluent-ffmpeg": "^2.1.3",
@@ -35,7 +35,8 @@
35
35
  "volume",
36
36
  "normalize",
37
37
  "ffmpeg",
38
- "fluent-ffmpeg"
38
+ "fluent-ffmpeg",
39
+ "clasen"
39
40
  ],
40
41
  "author": "Martin Clasen",
41
42
  "license": "MIT",