I am constructing complexFilters to procedurally generate audio with ffmpeg via fluent-ffmpeg. The interesting part of my fluent-ffmpeg complex filters look like:
{
"filter": "volume",
"options": {
"volume": "min(1, max(0, ((cos(PI * t * 1 / 13) * 1 + cos(PI * t * 1 / 7) * 0.5 + cos(PI * t * 1 / 3) * 0.25) +
-0.5 ) * 0.75 * -1 + 0.5))",
"eval": "frame"
},
"inputs": "track_1_output",
"outputs": "track_1_adjusted"
},
Note that I am using an expression to determine the volume dynamically. To simplify my tests, I’m constructing complexFilters on the command line to understand the mysteries of some of the lightly-documented ffmpeg filters. Here’s a working CLI test of using the volume filter to fade the sources in and out according to a time-based expresssion:
# working fade in/out
ffmpeg -i knox.mp3 -i static.mp3 -filter_complex
"[0:a]
volume = 'min(1, max(0, 0.5 + 0.5 * cos(PI * t / 5)))': eval=frame
[a0];
[1:a]
volume = 'min(1, max(0, 0.5 - 0.5 * cos(PI * t / 5)))': eval=frame
[a1];
[a0][a1]
amix = inputs=2: duration=shortest"
-c:a libmp3lame -q:a 2 output.mp3
Now I want to apply lowpass, highpass, aphaser, and/or aecho to the transitions using a time-based expression. For instance, looking at lowpasss, according to ffmpeg -filters
it says that it has time-based support:
T.. = Timeline support
TSC lowpass A->A Apply a low-pass filter with 3dB point frequency.
How can I similarly apply a time-based expression to lowpass, highpass, aphaser, or aecho? I thought the mix
option might be the key, but I couldn’t construct a working example and couldn’t find any examples.
# applying a lowpass filter
ffmpeg -i knox.mp3 -filter_complex
"[0:a]
lowpass=f=3000:mix='0.5 + 0.5 * cos(PI * t / 5)';"
-c:a libmp3lame -q:a 2 output.mp3
Or if you prefer the fluent-ffmpeg filter:
const ffmpeg = require('fluent-ffmpeg');
ffmpeg('knox.mp3')
.complexFilter([
{
filter: 'lowpass',
options: {
f: 3000,
mix: '0.5 + 0.5 * cos(PI * t / 5)'
},
inputs: '[0:a]',
}
])
.audioCodec('libmp3lame')
.audioQuality(2)
.save('output.mp3')
.on('end', () => {
console.log('Processing finished successfully');
})
.on('error', (err) => {
console.error('Error: ' + err.message);
});
With the resultant error:
[Parsed_lowpass_1 @ 0x7f9a72005c00] [Eval @ 0x7ff7bec423d8] Undefined constant or missing '(' in 't/5)'
[Parsed_lowpass_1 @ 0x7f9a72005c00] Unable to parse option value "0.5 + 0.5 * cos(PI * t / 5)"
Error applying option 'mix' to filter 'lowpass': Invalid argument
What complicated fffmpeg faerie magic is needed to make some of the filters other than volume controlled by a time-based expr?