sfxmix 1.2.2 → 1.2.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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(node:*)"
5
+ ],
6
+ "deny": [],
7
+ "ask": []
8
+ }
9
+ }
@@ -0,0 +1,15 @@
1
+ import SfxMix from '../index.js';
2
+ const sfx = new SfxMix();
3
+
4
+
5
+ // Trim silence from beginning and end of to_trim.mp3
6
+ await sfx
7
+ .add('to_trim.mp3')
8
+ .trim()
9
+ .save('trimmed_audio.ogg')
10
+ .then(() => {
11
+ console.log('Successfully exported: trimmed_audio.ogg');
12
+ })
13
+ .catch((error) => {
14
+ console.error('Error during audio processing:', error);
15
+ });
package/index.js CHANGED
@@ -2,6 +2,7 @@ const ffmpeg = require('fluent-ffmpeg');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { v4: uuidv4 } = require('uuid');
5
+ const { Readable } = require('stream');
5
6
 
6
7
  class SfxMix {
7
8
  constructor(config = {}) {
@@ -47,7 +48,103 @@ class SfxMix {
47
48
  return this;
48
49
  }
49
50
 
51
+ exportOgg(output) {
52
+ return new Promise(async (resolve, reject) => {
53
+ try {
54
+ for (let action of this.actions) {
55
+ if (action.type === 'add') {
56
+ if (this.currentFile == null) {
57
+ this.currentFile = path.isAbsolute(action.input) ? action.input : path.resolve(process.cwd(), action.input);
58
+ } else {
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)) {
62
+ fs.unlinkSync(this.currentFile);
63
+ }
64
+ this.currentFile = tempFile;
65
+ }
66
+ } else if (action.type === 'mix') {
67
+ if (this.currentFile == null) {
68
+ throw new Error('No audio to mix with. Add or concatenate audio before mixing.');
69
+ }
70
+ const tempFile = path.join(this.TMP_DIR, `temp_mix_${uuidv4()}.mp3`);
71
+ await this.mixAudioFiles(this.currentFile, action.input, tempFile, action.options);
72
+ if (this.isTempFile(this.currentFile)) {
73
+ fs.unlinkSync(this.currentFile);
74
+ }
75
+ this.currentFile = tempFile;
76
+ } else if (action.type === 'silence') {
77
+ const tempSilenceFile = path.join(this.TMP_DIR, `temp_silence_${uuidv4()}.mp3`);
78
+ await this.generateSilence(action.duration, tempSilenceFile);
79
+ if (this.currentFile == null) {
80
+ this.currentFile = tempSilenceFile;
81
+ } else {
82
+ const tempFile = path.join(this.TMP_DIR, `temp_concat_${uuidv4()}.mp3`);
83
+ await this.concatenateAudioFiles([this.currentFile, tempSilenceFile], tempFile);
84
+ if (this.isTempFile(this.currentFile)) {
85
+ fs.unlinkSync(this.currentFile);
86
+ }
87
+ fs.unlinkSync(tempSilenceFile);
88
+ this.currentFile = tempFile;
89
+ }
90
+ } else if (action.type === 'filter') {
91
+ if (this.currentFile == null) {
92
+ throw new Error('No audio to apply filter to. Add audio before applying filters.');
93
+ }
94
+ const tempFile = path.join(this.TMP_DIR, `temp_filter_${uuidv4()}.mp3`);
95
+ await this.applyFilter(this.currentFile, action.filterName, action.options, tempFile);
96
+ if (this.isTempFile(this.currentFile)) {
97
+ fs.unlinkSync(this.currentFile);
98
+ }
99
+ this.currentFile = tempFile;
100
+ }
101
+ }
102
+
103
+ const absoluteOutput = path.resolve(process.cwd(), output);
104
+ const outputDir = path.dirname(absoluteOutput);
105
+
106
+ if (!fs.existsSync(outputDir)) {
107
+ fs.mkdirSync(outputDir, { recursive: true });
108
+ }
109
+
110
+ if (!fs.existsSync(this.currentFile)) {
111
+ throw new Error(`Source file does not exist: ${this.currentFile}`);
112
+ }
113
+
114
+ await this.convertToOgg(this.currentFile, absoluteOutput);
115
+
116
+ if (this.isTempFile(this.currentFile)) {
117
+ fs.unlinkSync(this.currentFile);
118
+ }
119
+
120
+ this.reset();
121
+ resolve(absoluteOutput);
122
+ } catch (err) {
123
+ console.error('Error during OGG export:', err);
124
+ reject(err);
125
+ }
126
+ });
127
+ }
128
+
129
+ convertToOgg(inputFile, outputFile) {
130
+ return new Promise((resolve, reject) => {
131
+ ffmpeg()
132
+ .input(inputFile)
133
+ .audioCodec('libopus')
134
+ .format('ogg')
135
+ .output(outputFile)
136
+ .on('end', () => resolve())
137
+ .on('error', (err) => reject(err))
138
+ .run();
139
+ });
140
+ }
141
+
50
142
  save(output) {
143
+ // Check if output file has .ogg extension
144
+ if (output.toLowerCase().endsWith('.ogg')) {
145
+ return this.exportOgg(output);
146
+ }
147
+
51
148
  return new Promise(async (resolve, reject) => {
52
149
  try {
53
150
  for (let action of this.actions) {
@@ -201,8 +298,8 @@ class SfxMix {
201
298
  },
202
299
  },
203
300
  ])
204
- .audioCodec('libmp3lame') // Ensure the codec is MP3
205
- .format('mp3') // Ensure the format is MP3
301
+ .audioCodec('libmp3lame')
302
+ .format('mp3')
206
303
  .output(outputFile)
207
304
  .on('end', () => resolve())
208
305
  .on('error', (err) => reject(err))
@@ -213,9 +310,29 @@ class SfxMix {
213
310
  generateSilence(durationMs, outputFile) {
214
311
  return new Promise((resolve, reject) => {
215
312
  const durationSec = durationMs / 1000;
313
+ const sampleRate = 44100;
314
+ const numChannels = 2; // Stereo
315
+ const bytesPerSample = 2; // 16-bit audio
316
+ const bytesPerSecond = sampleRate * numChannels * bytesPerSample;
317
+ let totalBytes = Math.floor(durationSec * bytesPerSecond);
318
+
319
+ const silenceStream = new Readable({
320
+ read(size) {
321
+ const chunkSize = Math.min(size, totalBytes);
322
+ if (chunkSize <= 0) {
323
+ this.push(null);
324
+ return;
325
+ }
326
+ this.push(Buffer.alloc(chunkSize, 0));
327
+ totalBytes -= chunkSize;
328
+ }
329
+ });
330
+
216
331
  ffmpeg()
217
- .input('anullsrc=channel_layout=stereo:sample_rate=44100')
218
- .inputOptions(['-f', 'lavfi', '-t', `${durationSec}`])
332
+ .input(silenceStream)
333
+ .inputFormat('s16le')
334
+ .audioChannels(numChannels)
335
+ .audioFrequency(sampleRate)
219
336
  .audioCodec('libmp3lame')
220
337
  .format('mp3')
221
338
  .output(outputFile)
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.2.2",
4
+ "version": "1.2.6",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
7
  "fluent-ffmpeg": "^2.1.3",
@@ -20,6 +20,8 @@
20
20
  "filters",
21
21
  "mp3",
22
22
  "wav",
23
+ "ogg",
24
+ "opus",
23
25
  "echo",
24
26
  "delay",
25
27
  "tremolo",