peasy-audio-js 0.1.1
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/LICENSE +21 -0
- package/README.md +263 -0
- package/dist/index.d.ts +238 -0
- package/dist/index.js +150 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peasy Tools
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# peasy-audio-js
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/peasy-audio-js)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Audio processing library for Node.js -- convert between 6 formats (MP3, WAV, OGG, FLAC, AAC, M4A), trim segments, merge files, normalize volume, apply fade effects, adjust speed, and generate silence. FFmpeg-powered, TypeScript-first with full type safety.
|
|
8
|
+
|
|
9
|
+
Built from [PeasyAudio](https://peasyaudio.com), a free online audio toolkit with 10 browser-based tools for converting, trimming, merging, normalizing, and analyzing audio files.
|
|
10
|
+
|
|
11
|
+
> **Try the interactive tools at [peasyaudio.com](https://peasyaudio.com)** -- audio conversion, trimming, merging, normalization, and analysis
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img src="demo.gif" alt="peasy-audio-js demo — audio info, format conversion, and volume operations in Node.js" width="800">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
- [Prerequisites](#prerequisites)
|
|
20
|
+
- [Install](#install)
|
|
21
|
+
- [Quick Start](#quick-start)
|
|
22
|
+
- [What You Can Do](#what-you-can-do)
|
|
23
|
+
- [Audio Info & Metadata](#audio-info--metadata)
|
|
24
|
+
- [Format Conversion](#format-conversion)
|
|
25
|
+
- [Trimming & Merging](#trimming--merging)
|
|
26
|
+
- [Volume & Normalization](#volume--normalization)
|
|
27
|
+
- [Audio Effects](#audio-effects)
|
|
28
|
+
- [TypeScript Types](#typescript-types)
|
|
29
|
+
- [API Reference](#api-reference)
|
|
30
|
+
- [Also Available for Python](#also-available-for-python)
|
|
31
|
+
- [Peasy Developer Tools](#peasy-developer-tools)
|
|
32
|
+
- [License](#license)
|
|
33
|
+
|
|
34
|
+
## Prerequisites
|
|
35
|
+
|
|
36
|
+
peasy-audio-js uses FFmpeg under the hood. Install it before using this library:
|
|
37
|
+
|
|
38
|
+
| Platform | Command |
|
|
39
|
+
|----------|---------|
|
|
40
|
+
| **macOS** | `brew install ffmpeg` |
|
|
41
|
+
| **Ubuntu/Debian** | `sudo apt install ffmpeg` |
|
|
42
|
+
| **Fedora/RHEL** | `sudo dnf install ffmpeg-free` |
|
|
43
|
+
| **Windows** | `choco install ffmpeg` |
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install peasy-audio-js
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { info, convert, trim, merge, normalize } from "peasy-audio-js";
|
|
55
|
+
|
|
56
|
+
// Get audio file metadata
|
|
57
|
+
const metadata = await info("song.mp3");
|
|
58
|
+
console.log(metadata.duration, metadata.sampleRate); // 240.5 44100
|
|
59
|
+
|
|
60
|
+
// Convert MP3 to WAV
|
|
61
|
+
const wavPath = await convert("song.mp3", { format: "wav" });
|
|
62
|
+
|
|
63
|
+
// Trim to 30-second clip
|
|
64
|
+
const clip = await trim("song.mp3", { start: 10, duration: 30 });
|
|
65
|
+
|
|
66
|
+
// Merge multiple audio files
|
|
67
|
+
const combined = await merge(["intro.mp3", "main.mp3", "outro.mp3"]);
|
|
68
|
+
|
|
69
|
+
// Normalize volume to consistent levels
|
|
70
|
+
const normalized = await normalize("quiet-recording.mp3");
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## What You Can Do
|
|
74
|
+
|
|
75
|
+
### Audio Info & Metadata
|
|
76
|
+
|
|
77
|
+
Every audio file has metadata -- duration, sample rate, channels, bitrate, and codec information. The `info()` function uses FFprobe to extract this data without decoding the entire file, making it fast even for large files.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { info } from "peasy-audio-js";
|
|
81
|
+
|
|
82
|
+
// Extract audio metadata using FFprobe
|
|
83
|
+
const meta = await info("podcast.mp3");
|
|
84
|
+
console.log(meta.duration); // 3600.5 (seconds)
|
|
85
|
+
console.log(meta.sampleRate); // 44100 (Hz)
|
|
86
|
+
console.log(meta.channels); // 2 (stereo)
|
|
87
|
+
console.log(meta.bitrate); // 320000 (bits/sec)
|
|
88
|
+
console.log(meta.format); // "mp3"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Learn more: [Peasy Audio Tools](https://peasyaudio.com) · [Glossary](https://peasytools.com/glossary/)
|
|
92
|
+
|
|
93
|
+
### Format Conversion
|
|
94
|
+
|
|
95
|
+
Audio format conversion transforms audio data between container formats and codecs. Common conversions include MP3 to WAV (for editing), WAV to FLAC (lossless archiving), and any format to AAC/M4A (Apple ecosystem compatibility).
|
|
96
|
+
|
|
97
|
+
| Format | Extension | Use Case |
|
|
98
|
+
|--------|-----------|----------|
|
|
99
|
+
| MP3 | `.mp3` | Universal playback, streaming |
|
|
100
|
+
| WAV | `.wav` | Editing, uncompressed quality |
|
|
101
|
+
| OGG | `.ogg` | Open-source, web audio |
|
|
102
|
+
| FLAC | `.flac` | Lossless archiving |
|
|
103
|
+
| AAC | `.aac` | Apple ecosystem, efficient |
|
|
104
|
+
| M4A | `.m4a` | iTunes, Apple Music |
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { convert } from "peasy-audio-js";
|
|
108
|
+
|
|
109
|
+
// Convert MP3 to WAV for editing
|
|
110
|
+
const wav = await convert("music.mp3", { format: "wav" });
|
|
111
|
+
|
|
112
|
+
// Convert to FLAC with custom sample rate
|
|
113
|
+
const flac = await convert("music.mp3", {
|
|
114
|
+
format: "flac",
|
|
115
|
+
sampleRate: 48000,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Downmix stereo to mono
|
|
119
|
+
const mono = await convert("stereo.wav", {
|
|
120
|
+
format: "mp3",
|
|
121
|
+
channels: 1,
|
|
122
|
+
bitrate: "128k",
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Learn more: [Peasy Audio Tools](https://peasyaudio.com) · [Glossary](https://peasytools.com/glossary/)
|
|
127
|
+
|
|
128
|
+
### Trimming & Merging
|
|
129
|
+
|
|
130
|
+
Audio trimming extracts a segment by specifying start time and duration or end time. Merging concatenates multiple audio files sequentially into a single output file.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { trim, merge } from "peasy-audio-js";
|
|
134
|
+
|
|
135
|
+
// Extract a 30-second segment starting at 1 minute
|
|
136
|
+
const clip = await trim("podcast.mp3", { start: 60, duration: 30 });
|
|
137
|
+
|
|
138
|
+
// Trim from start to end time
|
|
139
|
+
const intro = await trim("song.mp3", { start: 0, end: 15 });
|
|
140
|
+
|
|
141
|
+
// Merge multiple files into one
|
|
142
|
+
const combined = await merge([
|
|
143
|
+
"chapter1.mp3",
|
|
144
|
+
"chapter2.mp3",
|
|
145
|
+
"chapter3.mp3",
|
|
146
|
+
]);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Learn more: [Peasy Audio Tools](https://peasyaudio.com) · [Glossary](https://peasytools.com/glossary/)
|
|
150
|
+
|
|
151
|
+
### Volume & Normalization
|
|
152
|
+
|
|
153
|
+
Audio normalization adjusts the overall volume level to a target loudness, measured in dBFS (decibels relative to full scale). The EBU R128 standard defines target loudness at -23 LUFS for broadcast, while streaming platforms typically target -14 LUFS.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { normalize, changeVolume } from "peasy-audio-js";
|
|
157
|
+
|
|
158
|
+
// Normalize volume using FFmpeg loudnorm filter
|
|
159
|
+
const normalized = await normalize("quiet-recording.mp3");
|
|
160
|
+
|
|
161
|
+
// Increase volume by 6dB (roughly doubles perceived loudness)
|
|
162
|
+
const louder = await changeVolume("track.mp3", { change: 6 });
|
|
163
|
+
|
|
164
|
+
// Decrease volume by 3dB
|
|
165
|
+
const softer = await changeVolume("loud-track.mp3", { change: -3 });
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Learn more: [Peasy Audio Tools](https://peasyaudio.com) · [Glossary](https://peasytools.com/glossary/)
|
|
169
|
+
|
|
170
|
+
### Audio Effects
|
|
171
|
+
|
|
172
|
+
Apply fade effects, speed changes, reversal, and generate silence programmatically. These operations use FFmpeg audio filters for sample-accurate processing.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { fade, speed, reverseAudio, silence } from "peasy-audio-js";
|
|
176
|
+
|
|
177
|
+
// Apply 3-second fade in and 5-second fade out
|
|
178
|
+
const faded = await fade("song.mp3", { fadeIn: 3, fadeOut: 5 });
|
|
179
|
+
|
|
180
|
+
// Double the playback speed (pitch shifts)
|
|
181
|
+
const fast = await speed("podcast.mp3", { factor: 2.0 });
|
|
182
|
+
|
|
183
|
+
// Slow down to 75% speed
|
|
184
|
+
const slow = await speed("audiobook.mp3", { factor: 0.75 });
|
|
185
|
+
|
|
186
|
+
// Reverse audio
|
|
187
|
+
const reversed = await reverseAudio("sample.mp3");
|
|
188
|
+
|
|
189
|
+
// Generate 5 seconds of silence as WAV
|
|
190
|
+
const gap = await silence({ duration: 5, format: "wav", sampleRate: 44100 });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Learn more: [Peasy Audio Tools](https://peasyaudio.com) · [Glossary](https://peasytools.com/glossary/)
|
|
194
|
+
|
|
195
|
+
## TypeScript Types
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import type {
|
|
199
|
+
AudioFormat,
|
|
200
|
+
AudioInfo,
|
|
201
|
+
ConvertOptions,
|
|
202
|
+
TrimOptions,
|
|
203
|
+
FadeOptions,
|
|
204
|
+
SpeedOptions,
|
|
205
|
+
VolumeOptions,
|
|
206
|
+
SilenceOptions,
|
|
207
|
+
} from "peasy-audio-js";
|
|
208
|
+
|
|
209
|
+
// AudioFormat — "mp3" | "wav" | "ogg" | "flac" | "aac" | "m4a"
|
|
210
|
+
const format: AudioFormat = "mp3";
|
|
211
|
+
|
|
212
|
+
// AudioInfo — metadata from info()
|
|
213
|
+
const meta: AudioInfo = {
|
|
214
|
+
duration: 240.5,
|
|
215
|
+
format: "mp3",
|
|
216
|
+
sampleRate: 44100,
|
|
217
|
+
channels: 2,
|
|
218
|
+
bitrate: 320000,
|
|
219
|
+
size: 9_600_000,
|
|
220
|
+
};
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## API Reference
|
|
224
|
+
|
|
225
|
+
| Function | Description |
|
|
226
|
+
|----------|-------------|
|
|
227
|
+
| `info(input)` | Get audio metadata (duration, format, sample rate, channels, bitrate) |
|
|
228
|
+
| `convert(input, options)` | Convert between audio formats (MP3, WAV, OGG, FLAC, AAC, M4A) |
|
|
229
|
+
| `trim(input, options)` | Extract a segment by start/end/duration |
|
|
230
|
+
| `merge(inputs)` | Concatenate multiple audio files sequentially |
|
|
231
|
+
| `normalize(input)` | Normalize volume using EBU R128 loudnorm filter |
|
|
232
|
+
| `changeVolume(input, options)` | Adjust volume by dB amount |
|
|
233
|
+
| `fade(input, options)` | Apply fade in/out effects |
|
|
234
|
+
| `speed(input, options)` | Change playback speed (0.5x to 4.0x) |
|
|
235
|
+
| `reverseAudio(input)` | Reverse audio playback |
|
|
236
|
+
| `silence(options)` | Generate silence of specified duration |
|
|
237
|
+
|
|
238
|
+
## Also Available for Python
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
pip install peasy-audio
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The Python package provides the same 12 audio operations with CLI and pydub engine. See [peasy-audio on PyPI](https://pypi.org/project/peasy-audio/).
|
|
245
|
+
|
|
246
|
+
## Peasy Developer Tools
|
|
247
|
+
|
|
248
|
+
| Package | PyPI | npm | Description |
|
|
249
|
+
|---------|------|-----|-------------|
|
|
250
|
+
| peasy-pdf | [PyPI](https://pypi.org/project/peasy-pdf/) | [npm](https://www.npmjs.com/package/peasy-pdf-js) | PDF merge, split, compress, rotate, watermark |
|
|
251
|
+
| peasy-image | [PyPI](https://pypi.org/project/peasy-image/) | [npm](https://www.npmjs.com/package/peasy-image-js) | Image resize, crop, compress, convert, watermark |
|
|
252
|
+
| peasytext | [PyPI](https://pypi.org/project/peasytext/) | [npm](https://www.npmjs.com/package/peasytext-js) | Text analysis, case conversion, slugs, word count |
|
|
253
|
+
| peasy-css | [PyPI](https://pypi.org/project/peasy-css/) | [npm](https://www.npmjs.com/package/peasy-css-js) | CSS gradients, shadows, flexbox, grid generators |
|
|
254
|
+
| peasy-compress | [PyPI](https://pypi.org/project/peasy-compress/) | [npm](https://www.npmjs.com/package/peasy-compress-js) | ZIP, gzip, brotli, deflate compression |
|
|
255
|
+
| peasy-document | [PyPI](https://pypi.org/project/peasy-document/) | [npm](https://www.npmjs.com/package/peasy-document-js) | Markdown, HTML, CSV, JSON, YAML conversion |
|
|
256
|
+
| **peasy-audio** | [PyPI](https://pypi.org/project/peasy-audio/) | **[npm](https://www.npmjs.com/package/peasy-audio-js)** | **Audio convert, trim, merge, normalize, effects** |
|
|
257
|
+
| peasy-video | [PyPI](https://pypi.org/project/peasy-video/) | [npm](https://www.npmjs.com/package/peasy-video-js) | Video trim, resize, thumbnails, GIF conversion |
|
|
258
|
+
|
|
259
|
+
Part of the [Peasy](https://peasytools.com) developer tools ecosystem.
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* peasy-audio-js — Audio processing types.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/** Supported audio formats. */
|
|
7
|
+
type AudioFormat = "mp3" | "wav" | "ogg" | "flac" | "aac" | "m4a";
|
|
8
|
+
/** Audio file metadata. */
|
|
9
|
+
interface AudioInfo {
|
|
10
|
+
/** Duration in seconds. */
|
|
11
|
+
duration: number;
|
|
12
|
+
/** Container format name (e.g. "mp3", "wav"). */
|
|
13
|
+
format: string;
|
|
14
|
+
/** Sample rate in Hz (e.g. 44100, 48000). */
|
|
15
|
+
sampleRate: number;
|
|
16
|
+
/** Number of audio channels (1 = mono, 2 = stereo). */
|
|
17
|
+
channels: number;
|
|
18
|
+
/** Bitrate in bits per second. */
|
|
19
|
+
bitrate: number;
|
|
20
|
+
/** File size in bytes. */
|
|
21
|
+
size: number;
|
|
22
|
+
}
|
|
23
|
+
/** Options for audio conversion. */
|
|
24
|
+
interface ConvertOptions {
|
|
25
|
+
/** Target audio format. */
|
|
26
|
+
format: AudioFormat;
|
|
27
|
+
/** Target bitrate (e.g. "128k", "320k"). */
|
|
28
|
+
bitrate?: string;
|
|
29
|
+
/** Target sample rate in Hz (e.g. 44100, 48000). */
|
|
30
|
+
sampleRate?: number;
|
|
31
|
+
/** Number of output channels (1 = mono, 2 = stereo). */
|
|
32
|
+
channels?: number;
|
|
33
|
+
}
|
|
34
|
+
/** Options for audio trimming. */
|
|
35
|
+
interface TrimOptions {
|
|
36
|
+
/** Start time in seconds. */
|
|
37
|
+
start?: number;
|
|
38
|
+
/** End time in seconds. */
|
|
39
|
+
end?: number;
|
|
40
|
+
/** Duration in seconds (alternative to end). */
|
|
41
|
+
duration?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Options for fade effects. */
|
|
44
|
+
interface FadeOptions {
|
|
45
|
+
/** Fade-in duration in seconds. */
|
|
46
|
+
fadeIn?: number;
|
|
47
|
+
/** Fade-out duration in seconds. */
|
|
48
|
+
fadeOut?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Options for speed adjustment. */
|
|
51
|
+
interface SpeedOptions {
|
|
52
|
+
/** Speed factor (e.g. 0.5 = half speed, 2.0 = double speed). */
|
|
53
|
+
factor: number;
|
|
54
|
+
}
|
|
55
|
+
/** Options for volume adjustment. */
|
|
56
|
+
interface VolumeOptions {
|
|
57
|
+
/** Volume change in dB (e.g., 3 for +3dB, -5 for -5dB). */
|
|
58
|
+
change: number;
|
|
59
|
+
}
|
|
60
|
+
/** Options for silence generation. */
|
|
61
|
+
interface SilenceOptions {
|
|
62
|
+
/** Duration of silence in seconds. */
|
|
63
|
+
duration: number;
|
|
64
|
+
/** Output format (default: "wav"). */
|
|
65
|
+
format?: AudioFormat;
|
|
66
|
+
/** Sample rate in Hz (default: 44100). */
|
|
67
|
+
sampleRate?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* peasy-audio-js — Audio processing engine.
|
|
72
|
+
*
|
|
73
|
+
* All operations use fluent-ffmpeg to invoke FFmpeg for audio processing.
|
|
74
|
+
* Functions take file paths and return Promises that resolve to output paths.
|
|
75
|
+
*
|
|
76
|
+
* @packageDocumentation
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get metadata for an audio file using ffprobe.
|
|
81
|
+
*
|
|
82
|
+
* @param input - Path to the audio file
|
|
83
|
+
* @returns Audio metadata including duration, format, sample rate, channels
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const metadata = await info("song.mp3");
|
|
88
|
+
* console.log(metadata.duration); // 180.5
|
|
89
|
+
* console.log(metadata.sampleRate); // 44100
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
declare function info(input: string): Promise<AudioInfo>;
|
|
93
|
+
/**
|
|
94
|
+
* Convert an audio file to a different format.
|
|
95
|
+
*
|
|
96
|
+
* @param input - Path to the source audio file
|
|
97
|
+
* @param options - Conversion options (format, bitrate, sampleRate, channels)
|
|
98
|
+
* @returns Path to the converted output file
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Convert MP3 to WAV
|
|
103
|
+
* const wav = await convert("song.mp3", { format: "wav" });
|
|
104
|
+
*
|
|
105
|
+
* // Convert with specific bitrate and sample rate
|
|
106
|
+
* const mp3 = await convert("song.wav", {
|
|
107
|
+
* format: "mp3",
|
|
108
|
+
* bitrate: "320k",
|
|
109
|
+
* sampleRate: 48000,
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare function convert(input: string, options: ConvertOptions): Promise<string>;
|
|
114
|
+
/**
|
|
115
|
+
* Trim an audio file to a specific time range.
|
|
116
|
+
*
|
|
117
|
+
* @param input - Path to the source audio file
|
|
118
|
+
* @param options - Trim options (start, end, duration)
|
|
119
|
+
* @returns Path to the trimmed output file
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* // Trim from 10s to 30s
|
|
124
|
+
* const trimmed = await trim("song.mp3", { start: 10, end: 30 });
|
|
125
|
+
*
|
|
126
|
+
* // Trim first 5 seconds
|
|
127
|
+
* const intro = await trim("song.mp3", { start: 0, duration: 5 });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
declare function trim(input: string, options: TrimOptions): Promise<string>;
|
|
131
|
+
/**
|
|
132
|
+
* Merge multiple audio files into a single file.
|
|
133
|
+
*
|
|
134
|
+
* Files are concatenated in the order provided. All files should have
|
|
135
|
+
* compatible formats (same sample rate and channels for best results).
|
|
136
|
+
*
|
|
137
|
+
* @param inputs - Array of paths to audio files to merge
|
|
138
|
+
* @returns Path to the merged output file
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* const merged = await merge(["intro.mp3", "main.mp3", "outro.mp3"]);
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
declare function merge(inputs: string[]): Promise<string>;
|
|
146
|
+
/**
|
|
147
|
+
* Normalize audio loudness using the EBU R128 loudnorm filter.
|
|
148
|
+
*
|
|
149
|
+
* @param input - Path to the source audio file
|
|
150
|
+
* @returns Path to the normalized output file
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const normalized = await normalize("quiet-recording.mp3");
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare function normalize(input: string): Promise<string>;
|
|
158
|
+
/**
|
|
159
|
+
* Adjust the volume of an audio file.
|
|
160
|
+
*
|
|
161
|
+
* @param input - Path to the source audio file
|
|
162
|
+
* @param options - Volume options with change in dB
|
|
163
|
+
* @returns Path to the volume-adjusted output file
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* // Increase volume by 3 dB
|
|
168
|
+
* const louder = await changeVolume("song.mp3", { change: 3 });
|
|
169
|
+
*
|
|
170
|
+
* // Decrease volume by 5 dB
|
|
171
|
+
* const quieter = await changeVolume("song.mp3", { change: -5 });
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare function changeVolume(input: string, options: VolumeOptions): Promise<string>;
|
|
175
|
+
/**
|
|
176
|
+
* Apply fade-in and/or fade-out effects to an audio file.
|
|
177
|
+
*
|
|
178
|
+
* @param input - Path to the source audio file
|
|
179
|
+
* @param options - Fade options with fadeIn and/or fadeOut durations in seconds
|
|
180
|
+
* @returns Path to the output file with fade effects
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* // Fade in over 2 seconds and fade out over 3 seconds
|
|
185
|
+
* const faded = await fade("song.mp3", { fadeIn: 2, fadeOut: 3 });
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
declare function fade(input: string, options: FadeOptions): Promise<string>;
|
|
189
|
+
/**
|
|
190
|
+
* Change the playback speed of an audio file.
|
|
191
|
+
*
|
|
192
|
+
* Uses the atempo filter which supports factors between 0.5 and 100.0.
|
|
193
|
+
* For factors outside the 0.5-2.0 range, multiple atempo filters are chained.
|
|
194
|
+
*
|
|
195
|
+
* @param input - Path to the source audio file
|
|
196
|
+
* @param options - Speed options with factor (e.g. 0.5 = half, 2.0 = double)
|
|
197
|
+
* @returns Path to the speed-adjusted output file
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* // Double speed
|
|
202
|
+
* const fast = await speed("podcast.mp3", { factor: 2.0 });
|
|
203
|
+
*
|
|
204
|
+
* // Half speed
|
|
205
|
+
* const slow = await speed("podcast.mp3", { factor: 0.5 });
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare function speed(input: string, options: SpeedOptions): Promise<string>;
|
|
209
|
+
/**
|
|
210
|
+
* Reverse an audio file.
|
|
211
|
+
*
|
|
212
|
+
* @param input - Path to the source audio file
|
|
213
|
+
* @returns Path to the reversed output file
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* const reversed = await reverseAudio("message.mp3");
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
declare function reverseAudio(input: string): Promise<string>;
|
|
221
|
+
/**
|
|
222
|
+
* Generate a silent audio file.
|
|
223
|
+
*
|
|
224
|
+
* @param options - Silence options including duration, format, and sample rate
|
|
225
|
+
* @returns Path to the generated silence file
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* // Generate 5 seconds of silence as WAV
|
|
230
|
+
* const gap = await silence({ duration: 5 });
|
|
231
|
+
*
|
|
232
|
+
* // Generate 2 seconds of silence as MP3 at 48kHz
|
|
233
|
+
* const gap48 = await silence({ duration: 2, format: "mp3", sampleRate: 48000 });
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
declare function silence(options: SilenceOptions): Promise<string>;
|
|
237
|
+
|
|
238
|
+
export { type AudioFormat, type AudioInfo, type ConvertOptions, type FadeOptions, type SilenceOptions, type SpeedOptions, type TrimOptions, type VolumeOptions, changeVolume, convert, fade, info, merge, normalize, reverseAudio, silence, speed, trim };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// src/engine.ts
|
|
2
|
+
import ffmpeg from "fluent-ffmpeg";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
import { extname } from "path";
|
|
7
|
+
function tmpOutput(ext) {
|
|
8
|
+
return join(tmpdir(), `peasy-audio-${randomUUID()}.${ext}`);
|
|
9
|
+
}
|
|
10
|
+
function run(command, output) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
command.output(output).on("end", () => resolve(output)).on("error", (err) => reject(err)).run();
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function getExt(filePath) {
|
|
16
|
+
return extname(filePath).slice(1) || "wav";
|
|
17
|
+
}
|
|
18
|
+
function info(input) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
ffmpeg.ffprobe(input, (err, metadata) => {
|
|
21
|
+
if (err) return reject(err);
|
|
22
|
+
const audio = metadata.streams.find((s) => s.codec_type === "audio");
|
|
23
|
+
resolve({
|
|
24
|
+
duration: metadata.format.duration ?? 0,
|
|
25
|
+
format: metadata.format.format_name ?? "unknown",
|
|
26
|
+
sampleRate: audio?.sample_rate ? Number(audio.sample_rate) : 0,
|
|
27
|
+
channels: audio?.channels ?? 0,
|
|
28
|
+
bitrate: metadata.format.bit_rate ? Number(metadata.format.bit_rate) : 0,
|
|
29
|
+
size: metadata.format.size ?? 0
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function convert(input, options) {
|
|
35
|
+
const output = tmpOutput(options.format);
|
|
36
|
+
const cmd = ffmpeg(input);
|
|
37
|
+
if (options.bitrate) {
|
|
38
|
+
cmd.audioBitrate(options.bitrate);
|
|
39
|
+
}
|
|
40
|
+
if (options.sampleRate) {
|
|
41
|
+
cmd.audioFrequency(options.sampleRate);
|
|
42
|
+
}
|
|
43
|
+
if (options.channels) {
|
|
44
|
+
cmd.audioChannels(options.channels);
|
|
45
|
+
}
|
|
46
|
+
return run(cmd, output);
|
|
47
|
+
}
|
|
48
|
+
function trim(input, options) {
|
|
49
|
+
const ext = getExt(input);
|
|
50
|
+
const output = tmpOutput(ext);
|
|
51
|
+
const cmd = ffmpeg(input);
|
|
52
|
+
if (options.start !== void 0) {
|
|
53
|
+
cmd.setStartTime(options.start);
|
|
54
|
+
}
|
|
55
|
+
if (options.duration !== void 0) {
|
|
56
|
+
cmd.setDuration(options.duration);
|
|
57
|
+
} else if (options.end !== void 0 && options.start !== void 0) {
|
|
58
|
+
cmd.setDuration(options.end - options.start);
|
|
59
|
+
} else if (options.end !== void 0) {
|
|
60
|
+
cmd.setDuration(options.end);
|
|
61
|
+
}
|
|
62
|
+
return run(cmd, output);
|
|
63
|
+
}
|
|
64
|
+
function merge(inputs) {
|
|
65
|
+
if (inputs.length === 0) {
|
|
66
|
+
return Promise.reject(new Error("At least one input file is required"));
|
|
67
|
+
}
|
|
68
|
+
const ext = getExt(inputs[0]);
|
|
69
|
+
const output = tmpOutput(ext);
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const cmd = ffmpeg();
|
|
72
|
+
for (const input of inputs) {
|
|
73
|
+
cmd.input(input);
|
|
74
|
+
}
|
|
75
|
+
cmd.on("end", () => resolve(output)).on("error", (err) => reject(err)).mergeToFile(output, tmpdir());
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function normalize(input) {
|
|
79
|
+
const ext = getExt(input);
|
|
80
|
+
const output = tmpOutput(ext);
|
|
81
|
+
const cmd = ffmpeg(input).audioFilters("loudnorm");
|
|
82
|
+
return run(cmd, output);
|
|
83
|
+
}
|
|
84
|
+
function changeVolume(input, options) {
|
|
85
|
+
const ext = getExt(input);
|
|
86
|
+
const output = tmpOutput(ext);
|
|
87
|
+
const cmd = ffmpeg(input).audioFilters(`volume=${options.change}dB`);
|
|
88
|
+
return run(cmd, output);
|
|
89
|
+
}
|
|
90
|
+
async function fade(input, options) {
|
|
91
|
+
const ext = getExt(input);
|
|
92
|
+
const output = tmpOutput(ext);
|
|
93
|
+
const filters = [];
|
|
94
|
+
if (options.fadeIn !== void 0 && options.fadeIn > 0) {
|
|
95
|
+
filters.push(`afade=t=in:st=0:d=${options.fadeIn}`);
|
|
96
|
+
}
|
|
97
|
+
if (options.fadeOut !== void 0 && options.fadeOut > 0) {
|
|
98
|
+
const metadata = await info(input);
|
|
99
|
+
const fadeStart = metadata.duration - options.fadeOut;
|
|
100
|
+
filters.push(`afade=t=out:st=${fadeStart}:d=${options.fadeOut}`);
|
|
101
|
+
}
|
|
102
|
+
const cmd = ffmpeg(input);
|
|
103
|
+
if (filters.length > 0) {
|
|
104
|
+
cmd.audioFilters(filters);
|
|
105
|
+
}
|
|
106
|
+
return run(cmd, output);
|
|
107
|
+
}
|
|
108
|
+
function speed(input, options) {
|
|
109
|
+
const ext = getExt(input);
|
|
110
|
+
const output = tmpOutput(ext);
|
|
111
|
+
const filters = [];
|
|
112
|
+
let remaining = options.factor;
|
|
113
|
+
while (remaining > 2) {
|
|
114
|
+
filters.push("atempo=2.0");
|
|
115
|
+
remaining /= 2;
|
|
116
|
+
}
|
|
117
|
+
while (remaining < 0.5) {
|
|
118
|
+
filters.push("atempo=0.5");
|
|
119
|
+
remaining /= 0.5;
|
|
120
|
+
}
|
|
121
|
+
filters.push(`atempo=${remaining}`);
|
|
122
|
+
const cmd = ffmpeg(input).audioFilters(filters);
|
|
123
|
+
return run(cmd, output);
|
|
124
|
+
}
|
|
125
|
+
function reverseAudio(input) {
|
|
126
|
+
const ext = getExt(input);
|
|
127
|
+
const output = tmpOutput(ext);
|
|
128
|
+
const cmd = ffmpeg(input).audioFilters("areverse");
|
|
129
|
+
return run(cmd, output);
|
|
130
|
+
}
|
|
131
|
+
function silence(options) {
|
|
132
|
+
const format = options.format ?? "wav";
|
|
133
|
+
const sampleRate = options.sampleRate ?? 44100;
|
|
134
|
+
const output = tmpOutput(format);
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
ffmpeg().input("anullsrc").inputFormat("lavfi").inputOptions([`-channel_layout=stereo`, `-sample_rate=${sampleRate}`]).duration(options.duration).output(output).on("end", () => resolve(output)).on("error", (err) => reject(err)).run();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
changeVolume,
|
|
141
|
+
convert,
|
|
142
|
+
fade,
|
|
143
|
+
info,
|
|
144
|
+
merge,
|
|
145
|
+
normalize,
|
|
146
|
+
reverseAudio,
|
|
147
|
+
silence,
|
|
148
|
+
speed,
|
|
149
|
+
trim
|
|
150
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "peasy-audio-js",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Audio processing library for Node.js — convert, trim, merge, normalize, fade, speed. FFmpeg-powered, TypeScript-first.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"audio",
|
|
24
|
+
"mp3",
|
|
25
|
+
"wav",
|
|
26
|
+
"ffmpeg",
|
|
27
|
+
"convert",
|
|
28
|
+
"trim",
|
|
29
|
+
"merge",
|
|
30
|
+
"peasy"
|
|
31
|
+
],
|
|
32
|
+
"author": "Peasy Tools",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"url": "https://github.com/peasytools/peasy-audio-js.git"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://peasyaudio.com",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"fluent-ffmpeg": "^2.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/fluent-ffmpeg": "^2.1",
|
|
43
|
+
"tsup": "^8.0",
|
|
44
|
+
"typescript": "^5.7",
|
|
45
|
+
"vitest": "^3.0"
|
|
46
|
+
}
|
|
47
|
+
}
|