From 9edb1e4322cf1a044bdaaca5d5f3563464d93c5d Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Tue, 8 Dec 2020 13:30:31 +0100 Subject: [PATCH] implement output volume #83 --- README.md | 2 ++ audio.js | 9 +++++---- cli.js | 5 ++++- examples/audio-volume.json5 | 11 +++++++++++ index.js | 3 ++- 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 examples/audio-volume.json5 diff --git a/README.md b/README.md index 19207b71..ef30efac 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ Edit specs are JavaScript / JSON objects describing the whole edit operation wit loopAudio: false, keepSourceAudio: false, clipsAudioVolume: 1, + outputVolume: 1, audio: [ { path, @@ -197,6 +198,7 @@ Edit specs are JavaScript / JSON objects describing the whole edit operation wit | `loopAudio` | `--loop-audio` | Loop the audio track if it is shorter than video? | `false` | | | `keepSourceAudio` | `--keep-source-audio` | Keep source audio from `clips`? | `false` | | | `clipsAudioVolume` | | Volume of audio from `clips` relative to `audioTracks`. See [audio tracks](#arbitrary-audio-tracks). | `1` | | +| `outputVolume` | `--output-volume` | Adjust output [volume](http://ffmpeg.org/ffmpeg-filters.html#volume) (final stage). See [example](https://github.com/mifi/editly/blob/master/examples/audio-volume.json5) | `1` | e.g. `0.5` or `10dB` | | `audioNorm.enable` | | Enable audio normalization? See [audio normalization](#audio-normalization). | `false` | | | `audioNorm.gaussSize` | | Audio normalization gauss size. See [audio normalization](#audio-normalization). | `5` | | | `audioNorm.maxGain` | | Audio normalization max gain. See [audio normalization](#audio-normalization). | `30` | | diff --git a/audio.js b/audio.js index 61acff85..87248dc6 100644 --- a/audio.js +++ b/audio.js @@ -156,7 +156,7 @@ module.exports = ({ ffmpegPath, ffprobePath, enableFfmpegLog, verbose, tmpDir }) return outPath; } - async function mixArbitraryAudio({ streams, audioNorm }) { + async function mixArbitraryAudio({ streams, audioNorm, outputVolume }) { let maxGain = 30; let gaussSize = 5; if (audioNorm) { @@ -173,8 +173,9 @@ module.exports = ({ ffmpegPath, ffprobePath, enableFfmpegLog, verbose, tmpDir }) return `[${i}]atrim=start=${cutFrom || 0}${cutToArg},adelay=delays=${Math.floor((start || 0) * 1000)}:all=1${apadArg}[a${i}]`; }).join(';'); + const volumeArg = outputVolume != null ? `,volume=${outputVolume}` : ''; const audioNormArg = enableAudioNorm ? `,dynaudnorm=g=${gaussSize}:maxgain=${maxGain}` : ''; - filterComplex += `;${streams.map((s, i) => `[a${i}]`).join('')}amix=inputs=${streams.length}:duration=first:dropout_transition=0:weights=${streams.map((s) => (s.mixVolume != null ? s.mixVolume : 1)).join(' ')}${audioNormArg}`; + filterComplex += `;${streams.map((s, i) => `[a${i}]`).join('')}amix=inputs=${streams.length}:duration=first:dropout_transition=0:weights=${streams.map((s) => (s.mixVolume != null ? s.mixVolume : 1)).join(' ')}${audioNormArg}${volumeArg}`; const mixedAudioPath = join(tmpDir, 'audio-mixed.flac'); @@ -198,7 +199,7 @@ module.exports = ({ ffmpegPath, ffprobePath, enableFfmpegLog, verbose, tmpDir }) } - async function editAudio({ keepSourceAudio, clips, arbitraryAudio, clipsAudioVolume, audioNorm }) { + async function editAudio({ keepSourceAudio, clips, arbitraryAudio, clipsAudioVolume, audioNorm, outputVolume }) { // We need clips to process audio, because we need to know duration if (clips.length === 0) return undefined; @@ -228,7 +229,7 @@ module.exports = ({ ffmpegPath, ffprobePath, enableFfmpegLog, verbose, tmpDir }) if (streams.length < 2) return concatedClipAudioPath; - const mixedFile = await mixArbitraryAudio({ streams, audioNorm }); + const mixedFile = await mixArbitraryAudio({ streams, audioNorm, outputVolume }); return mixedFile; } diff --git a/cli.js b/cli.js index cfe89c10..e526ef37 100644 --- a/cli.js +++ b/cli.js @@ -34,6 +34,7 @@ const cli = meow(` --audio-file-path Add an audio track --loop-audio Loop the audio track if it is shorter than video? --keep-source-audio Keep audio from source files + --output-volume Adjust audio output volume --allow-remote-requests --fast, -f Fast mode (low resolution and FPS, useful for getting a quick preview) @@ -57,6 +58,7 @@ const cli = meow(` height: { type: 'number' }, fps: { type: 'number' }, loopAudio: { type: 'boolean' }, + outputVolume: { type: 'string' }, }, }); @@ -98,7 +100,7 @@ const cli = meow(` params.clips = clips.map((clip) => ({ layers: [clip] })); } - const { verbose, transitionName, transitionDuration, clipDuration, width, height, fps, audioFilePath, fontPath, fast, out: outPath, keepSourceAudio, loopAudio, allowRemoteRequests } = cli.flags; + const { verbose, transitionName, transitionDuration, clipDuration, width, height, fps, audioFilePath, fontPath, fast, out: outPath, keepSourceAudio, loopAudio, outputVolume, allowRemoteRequests } = cli.flags; if (transitionName || transitionDuration != null) { params.defaults.transition = {}; @@ -117,6 +119,7 @@ const cli = meow(` if (outPath) params.outPath = outPath; if (audioFilePath) params.audioFilePath = audioFilePath; if (loopAudio) params.loopAudio = loopAudio; + if (outputVolume) params.outputVolume = outputVolume; if (keepSourceAudio) params.keepSourceAudio = true; if (allowRemoteRequests) params.allowRemoteRequests = true; if (width) params.width = width; diff --git a/examples/audio-volume.json5 b/examples/audio-volume.json5 new file mode 100644 index 00000000..032d5267 --- /dev/null +++ b/examples/audio-volume.json5 @@ -0,0 +1,11 @@ +{ + outPath: './audio-volume.mp4', + width: 200, height: 200, + clips: [ + { duration: 2, layers: [{ type: 'title-background', text: 'Audio output volume' }] }, + ], + audioTracks: [ + { path: './assets/High [NCS Release] - JPB (No Copyright Music)-R8ZRCXy5vhA.m4a', cutFrom: 18 }, + ], + outputVolume: '-10dB', +} \ No newline at end of file diff --git a/index.js b/index.js index 21a78d73..50e417f7 100644 --- a/index.js +++ b/index.js @@ -39,6 +39,7 @@ const Editly = async (config = {}) => { keepSourceAudio, allowRemoteRequests, audioNorm, + outputVolume, ffmpegPath = 'ffmpeg', ffprobePath = 'ffprobe', @@ -68,7 +69,7 @@ const Editly = async (config = {}) => { const { editAudio } = Audio({ ffmpegPath, ffprobePath, enableFfmpegLog, verbose, tmpDir }); - const audioFilePath = !isGif ? await editAudio({ keepSourceAudio, arbitraryAudio, clipsAudioVolume, clips, audioNorm }) : undefined; + const audioFilePath = !isGif ? await editAudio({ keepSourceAudio, arbitraryAudio, clipsAudioVolume, clips, audioNorm, outputVolume }) : undefined; // Try to detect parameters from first video let firstVideoWidth;