aevalsrc

Watch on youtube.com
doc

https://ffmpeg.org/ffmpeg-filters.html#aevalsrc

see also

sine, (ffmpeg-utils)3. Expression Evaluation

This filter that produces sound is a bit more complex than sine, but it is flexible using expressions. If you just want to create a simple “sine wave”, of course sine is easier:

#! /bin/sh
# 440Hz sine wave ("A4" in MIDI scale) for 4 secs.
ffplay -f lavfi 'sine=440:d=4'
#! /bin/sh
# same as above using with "aevalsrc"
ffplay -f lavfi 'aevalsrc=0.125 * sin(2 * PI * 440 * t):d=4'
#! /bin/sh
# C4 + E4 + G4
ffplay -f lavfi aevalsrc='
0.1 * (
    sin(2 * PI * 261.6255653005986 * t) +
    sin(2 * PI * 329.6275569128699 * t) +
    sin(2 * PI * 391.9954359817493 * t)
):d=4'
#! /bin/sh
ffmpeg -y -filter_complex "
aevalsrc='0.1*(
sin(2*PI*t*261.62556530059862)
 + sin(2*PI*t*329.62755691286992)
 + sin(2*PI*t*391.99543598174927)
)':d=0.99[a00];
aevalsrc=0:d=0.0125[a01];
aevalsrc='0.1*(
sin(2*PI*t*261.62556530059862)
)':d=0.49[a02];
aevalsrc=0:d=0.0125[a03];
aevalsrc='0.1*(
sin(2*PI*t*329.62755691286992)
)':d=0.49[a04];
aevalsrc=0:d=0.0125[a05];
aevalsrc='0.1*(
sin(2*PI*t*391.99543598174927)
)':d=0.49[a06];
aevalsrc=0:d=0.0125[a07];
aevalsrc='0.1*(
sin(2*PI*t*329.62755691286992)
)':d=0.49[a08];
aevalsrc=0:d=0.0125[a09];
aevalsrc='0.1*(
sin(2*PI*t*261.62556530059862)
)':d=0.99[a10];
aevalsrc=0:d=0.0125[a11];
aevalsrc='0.1*(
sin(2*PI*t*277.18263097687208)
 + sin(2*PI*t*349.22823143300388)
 + sin(2*PI*t*415.30469757994513)
)':d=0.99[a12];
aevalsrc=0:d=0.0125[a13];
aevalsrc='0.1*(
sin(2*PI*t*277.18263097687208)
)':d=0.49[a14];
aevalsrc=0:d=0.0125[a15];
aevalsrc='0.1*(
sin(2*PI*t*349.22823143300388)
)':d=0.49[a16];
aevalsrc=0:d=0.0125[a17];
aevalsrc='0.1*(
sin(2*PI*t*415.30469757994513)
)':d=0.49[a18];
aevalsrc=0:d=0.0125[a19];
aevalsrc='0.1*(
sin(2*PI*t*349.22823143300388)
)':d=0.49[a20];
aevalsrc=0:d=0.0125[a21];
aevalsrc='0.1*(
sin(2*PI*t*277.18263097687208)
)':d=0.99[a22];
aevalsrc=0:d=0.0125[a23];
aevalsrc='0.1*(
sin(2*PI*t*293.66476791740757)
 + sin(2*PI*t*369.99442271163440)
 + sin(2*PI*t*440.00000000000000)
)':d=0.99[a24];
aevalsrc=0:d=0.0125[a25];
aevalsrc='0.1*(
sin(2*PI*t*293.66476791740757)
)':d=0.49[a26];
aevalsrc=0:d=0.0125[a27];
aevalsrc='0.1*(
sin(2*PI*t*369.99442271163440)
)':d=0.49[a28];
aevalsrc=0:d=0.0125[a29];
aevalsrc='0.1*(
sin(2*PI*t*440.00000000000000)
)':d=0.49[a30];
aevalsrc=0:d=0.0125[a31];
aevalsrc='0.1*(
sin(2*PI*t*369.99442271163440)
)':d=0.49[a32];
aevalsrc=0:d=0.0125[a33];
aevalsrc='0.1*(
sin(2*PI*t*293.66476791740757)
)':d=0.99[a34];
aevalsrc=0:d=0.0125[a35];
aevalsrc='0.1*(
sin(2*PI*t*311.12698372208092)
 + sin(2*PI*t*391.99543598174927)
 + sin(2*PI*t*466.16376151808993)
)':d=0.99[a36];
aevalsrc=0:d=0.0125[a37];
aevalsrc='0.1*(
sin(2*PI*t*311.12698372208092)
)':d=0.49[a38];
aevalsrc=0:d=0.0125[a39];
aevalsrc='0.1*(
sin(2*PI*t*391.99543598174927)
)':d=0.49[a40];
aevalsrc=0:d=0.0125[a41];
aevalsrc='0.1*(
sin(2*PI*t*466.16376151808993)
)':d=0.49[a42];
aevalsrc=0:d=0.0125[a43];
aevalsrc='0.1*(
sin(2*PI*t*391.99543598174927)
)':d=0.49[a44];
aevalsrc=0:d=0.0125[a45];
aevalsrc='0.1*(
sin(2*PI*t*311.12698372208092)
)':d=0.99[a46];
aevalsrc=0:d=0.0125[a47];
aevalsrc='0.1*(
sin(2*PI*t*329.62755691286992)
 + sin(2*PI*t*415.30469757994513)
 + sin(2*PI*t*493.88330125612413)
)':d=0.99[a48];
aevalsrc=0:d=0.0125[a49];
aevalsrc='0.1*(
sin(2*PI*t*329.62755691286992)
)':d=0.49[a50];
aevalsrc=0:d=0.0125[a51];
aevalsrc='0.1*(
sin(2*PI*t*415.30469757994513)
)':d=0.49[a52];
aevalsrc=0:d=0.0125[a53];
aevalsrc='0.1*(
sin(2*PI*t*493.88330125612413)
)':d=0.49[a54];
aevalsrc=0:d=0.0125[a55];
aevalsrc='0.1*(
sin(2*PI*t*415.30469757994513)
)':d=0.49[a56];
aevalsrc=0:d=0.0125[a57];
aevalsrc='0.1*(
sin(2*PI*t*329.62755691286992)
)':d=0.99[a58];
aevalsrc=0:d=0.0125[a59];

[a00][a01][a02][a03][a04][a05][a06][a07][a08][a09][a10][a11][a12][a13][a14]
[a15][a16][a17][a18][a19][a20][a21][a22][a23][a24][a25][a26][a27][a28][a29]
[a30][a31][a32][a33][a34][a35][a36][a37][a38][a39][a40][a41][a42][a43][a44]
[a45][a46][a47][a48][a49][a50][a51][a52][a53][a54][a55][a56][a57][a58][a59]

concat=n=60:v=0:a=1
" cegec.wav

Note the quotes. If you encounter an error like the following, you probably need quotes:

[me@host: ~]$ ffplay -f lavfi aevalsrc='0.1 * sin(2 * PI * t * (
>     between(t, 0.000, 0.875) * 261.62556530 +
>     between(t, 1.000, 1.875) * 261.62556530
> ))'
    nan    :  0.000 fd=   0 aq=    0KB vq=    0KB sq=    0B f=0/0
[Parsed_aevalsrc_0 @ 0000000001cde140] [Eval @ 00000000039df360] Missing ')' or too many args in 'between(t'
[lavfi @ 0000000001cdb0e0] Error initializing filter 'aevalsrc' with args '0.1 * sin(2 * PI * t * (
    between(t'
aevalsrc=0.1 * sin(2 * PI * t * (
    between(t, 0.000, 0.875) * 261.62556530 +
    between(t, 1.000, 1.875) * 261.62556530
)): Invalid argument
[me@host: ~]$ # you need quotes
[me@host: ~]$ ffplay -f lavfi aevalsrc="'0.1 * sin(2 * PI * t * (
>     between(t, 0.000, 0.875) * 261.62556530 +
>     between(t, 1.000, 1.875) * 261.62556530 +
>     between(t, 2.000, 2.875) * 391.99543598 +
>     between(t, 3.000, 3.875) * 391.99543598 +
>     between(t, 4.000, 4.875) * 440.00000000 +
>     between(t, 5.000, 5.875) * 440.00000000 +
>     between(t, 6.000, 7.875) * 391.99543598 +
>     between(t, 8.000, 8.875) * 349.22823143 +
>     between(t, 9.000, 9.875) * 349.22823143 +
>     between(t, 10.000, 10.875) * 329.62755691 +
>     between(t, 11.000, 11.875) * 329.62755691 +
>     between(t, 12.000, 12.875) * 293.66476792 +
>     between(t, 13.000, 13.875) * 293.66476792 +
>     between(t, 14.000, 15.875) * 261.62556530
> ))'"
[me@host: ~]$ ffplay -f lavfi "aevalsrc='0.1 * sin(2 * PI * 440 * pow(t, 3))'"
[me@host: ~]$ ffplay -f lavfi "
> aevalsrc='0.2 * abs(1 - mod(t, 2)) * sin(2 * PI * 440 * t)'"
[me@host: ~]$ # visualize
[me@host: ~]$ ffplay -f lavfi "
> aevalsrc='0.2 * abs(1 - mod(t, 2)) * sin(2 * PI * 440 * t)'
> , asplit[out1], showvolume[out0]"
play_morse.py
# -*- coding: utf-8 -*-
import subprocess


_lut = {'!': '-.-.--',
        "'": '.----.',
        '"': '.-..-.',
        '$': '...-..-',
        '&': '.-...',
        '(': '-.--.',
        ')': '-.--.-',
        '+': '.-.-.',
        ',': '--..--',
        '-': '-....-',
        '.': '.-.-.-',
        '/': '-..-.',
        '0': '-----',
        '1': '.----',
        '2': '..---',
        '3': '...--',
        '4': '....-',
        '5': '.....',
        '6': '-....',
        '7': '--...',
        '8': '---..',
        '9': '----.',
        ':': '---...',
        ';': '-.-.-.',
        '=': '-...-',
        '?': '..--..',
        '@': '.--.-.',
        'A': '.-',
        'B': '-...',
        'C': '-.-.',
        'D': '-..',
        'E': '.',
        'F': '..-.',
        'G': '--.',
        'H': '....',
        'I': '..',
        'J': '.---',
        'K': '-.-',
        'L': '.-..',
        'M': '--',
        'N': '-.',
        'O': '---',
        'P': '.--.',
        'Q': '--.-',
        'R': '.-.',
        'S': '...',
        'T': '-',
        'U': '..-',
        'V': '...-',
        'W': '.--',
        'X': '-..-',
        'Y': '-.--',
        'Z': '--..',
        '_': '..--.-',
}


if hasattr("", "decode"):
    _encode = lambda s: s.encode(sys.getfilesystemencoding())
else:
    _encode = lambda s: s


def _play(message):
    _onoff = []
    _BREAK_CD = 0.075
    _BREAK_CH = 0.500
    _BREAK_P = 2.000
    t = 0
    for ch in message:
        cd = _lut.get(ch.upper(), " ")
        for c in cd:
            if c == " ":
                t += _BREAK_P
            elif c == ".":
                _LN = 0.25
                _onoff.append("between(t, {}, {})".format(t, t + _LN))
                t += _LN + _BREAK_CD
            else:
                _LN = 0.5
                _onoff.append("between(t, {}, {})".format(t, t + _LN))
                t += _LN + _BREAK_CD
        t += _BREAK_CH
    fc = "aevalsrc='0.1 * sin(2 * PI * t * 440 * ({}))':d={}".format(" + ".join(_onoff), t)
    cmdl = [
        "ffplay",
        "-f", "lavfi",
        fc,
        "-autoexit"
    ]
    subprocess.check_call(list(map(_encode, filter(None, cmdl))))



if __name__ == '__main__':
    import sys
    import argparse
    ap = argparse.ArgumentParser()
    ap.add_argument("message")
    args = ap.parse_args()
    _play(args.message)

This filter can create sounds for multiple channels at once:

#! /bin/sh
# left: A3 / right: A3 + 7.83Hz using with "sine"
ffplay -f lavfi "
sine=220.00:d=500[a1];
sine=227.83:d=500[a2];
[a1][a2]amerge=inputs=2"
#! /bin/sh
# same as above using with "aevalsrc"
ffplay -f lavfi "aevalsrc=
0.125 * sin(2 * PI * 220.00 * t) |
0.125 * sin(2 * PI * 227.83 * t):d=500"
#! /bin/sh
# visualize (showcqt)
ffplay -f lavfi "aevalsrc=
0.1 * sin(2 * PI * 220.00 * t) |
0.1 * sin(2 * PI * 227.83 * t):d=50,
asplit[out1],channelsplit[a1][a2];
[a1]showcqt=s=960x270,setsar=1[v1];
[a2]showcqt=s=960x270,setsar=1,vflip[v2];
[v1][v2]vstack[out0]"
#! /bin/sh
# visualize (showvolume), specifying the channel layout
ffplay -f lavfi "aevalsrc=
0.1 * sin(2 * PI * 220.00 * t) |
0.1 * sin(2 * PI * 227.83 * t):d=50:c=FL|FR,
asplit[out1],showvolume[out0]"

If the formula gets complicated, it is better to use another filter instead of trying to do it only with aevalsrc.

#! /bin/sh
ffplay -f lavfi aevalsrc="'
0.3 * abs(1 - mod(t, 2)) * sin(2 * PI * 220.00 * t) |
0.3 * abs(1 - mod(t + 1, 2)) * sin(2 * PI * 227.83 * t)':d=500,
asplit[out1], showvolume[out0]"
#! /bin/sh
# almost same as above (using "volume" filter)
ffplay -f lavfi aevalsrc="'
abs(1 - mod(t, 2)) * sin(2 * PI * 220.00 * t) |
abs(1 - mod(t + 1, 2)) * sin(2 * PI * 227.83 * t)':d=500,
volume=0.3,
asplit[out1], showvolume[out0]"
Watch on youtube.com
#! /bin/sh
#
cat << __END__ > hearingc.ass
[Script Info]
PlayResX: 1920
PlayResY: 1080
WrapStyle: 1

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, OutlineColour, BackColour, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: s1, mp,58,&H00F0F0F0,&H00FFF0F0,&H00D05050, 1,2,2, 7 ,30,30,30,0

[Fonts]
mp: meiryo.ttf

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,00:00:00.00,00:00:08.00,s1,_,0,0,0,,{\pos(100,860)}  8000 Hz
Dialogue: 0,00:00:08.00,00:00:16.00,s1,_,0,0,0,,{\pos(100,860)} 10000 Hz
Dialogue: 0,00:00:16.00,00:00:24.00,s1,_,0,0,0,,{\pos(100,860)} 12000 Hz
Dialogue: 0,00:00:24.00,00:00:32.00,s1,_,0,0,0,,{\pos(100,860)} 13000 Hz
Dialogue: 0,00:00:32.00,00:00:40.00,s1,_,0,0,0,,{\pos(100,860)} 14000 Hz
Dialogue: 0,00:00:40.00,00:00:48.00,s1,_,0,0,0,,{\pos(100,860)} 15000 Hz
Dialogue: 0,00:00:48.00,00:00:56.00,s1,_,0,0,0,,{\pos(100,860)} 17000 Hz
Dialogue: 0,00:00:56.00,00:00:64.00,s1,_,0,0,0,,{\pos(100,860)} 19000 Hz
__END__
#
ffmpeg -y -filter_complex "
aevalsrc='
sin(2 * PI * (
    between(t, 16 *  0, 16 *  1) *  8000
  + between(t, 16 *  2, 16 *  3) * 10000
  + between(t, 16 *  4, 16 *  5) * 12000
  + between(t, 16 *  6, 16 *  7) * 13000
  + between(t, 16 *  8, 16 *  9) * 14000
  + between(t, 16 * 10, 16 * 11) * 15000
  + between(t, 16 * 12, 16 * 13) * 17000
  + between(t, 16 * 14, 16 * 15) * 19000
) * t) |
sin(2 * PI * (
    between(t, 16 *  1, 16 *  2) *  8000
  + between(t, 16 *  3, 16 *  4) * 10000
  + between(t, 16 *  5, 16 *  6) * 12000
  + between(t, 16 *  7, 16 *  8) * 13000
  + between(t, 16 *  9, 16 * 10) * 14000
  + between(t, 16 * 11, 16 * 12) * 15000
  + between(t, 16 * 13, 16 * 14) * 17000
  + between(t, 16 * 15, 16 * 16) * 19000
) * t):s=192000:c=FL|FR
'
,volume='if(lt(st(0, mod(t, 4)), 1.5), ld(0) / 1.5, if(lt(ld(0), 2.5), 1, (4 - ld(0)) / 1.5))':eval=frame
,atempo=2,atempo=2
,asplit=5[a][a1][a2][a3][a4];
[a1]showcqt=s=1920x1080,setsar=1[vcqt];
[a2]showvolume=w=1760,scale=1760:40,setsar=1,colorkey=black[vv];
[a3]showwaves=split_channels=1:mode=line:colors=red@0.8|green@0.9:s=1760x480,setsar=1[vs];
[a4]ahistogram=s=1760x500:dmode=separate:slide=scroll,setsar=1,colorkey=black[vh];
[vs][vv]overlay=x=0:y=(H-h)/2[v1];
[vcqt][v1]overlay=x=(W-w)/2:y=50[v2];
[v2][vh]overlay=x=(W-w)/2:y=560,subtitles=hearingc.ass[v]
" -map '[v]' -map '[a]' -t 70 hearingc.mkv

To use shell variables or functions is also good idea:

#! /bin/sh
function _ampl() {
    echo "abs(1 - mod($1, 2))";
}
ffplay -f lavfi "aevalsrc='
    0.3 * `_ampl t` * sin(2 * PI * 220.00 * t) |
    0.3 * `_ampl '(t + 1)'` * sin(2 * PI * 227.83 * t)':d=500,
asplit[out1], showvolume[out0]"
see also

About `shell’ script

making silence audio stream

Naturally, you can make silence by giving zero as the frequency. In video combining or audio mixing, it is sometimes necessary to produce a silent audio stream. In these cases, you can use aevalsrc, sine, apad, or anullsrc. You may feel anullsrc is useful, but because you can’t give it a duration, so you probably find aevalsrc easier to use:

silence by ‘sine’
#! /bin/sh
ifn1=bb20.mp4  # 20 secs / 48000Hz
ifn2=bb30.mp4  # 30 secs / 48000Hz
#
ffmpeg -y -i ${ifn1} -i ${ifn2} -filter_complex "
sine=0:d=10:r=48000[L];
sine=0:d=10:r=48000[R];
[L][R]amerge[pad_a];
[0:a][pad_a]concat=n=2:a=1:v=0[a0];
[a0][1:a]amix=inputs=2,asplit[a][av];
[av]channelsplit[av1][av2];
[av1]showcqt=s=960x270,setsar=1[v1];
[av2]showcqt=s=960x270,setsar=1,vflip[v2];
[v1][v2]vstack[v]
" -map '[v]' -map '[a]' amixed0.mp4
silence by ‘anullsrc’
#! /bin/sh
ifn1=bb20.mp4  # 20 secs / 48000Hz
ifn2=bb30.mp4  # 30 secs / 48000Hz
#
ffmpeg -y -i ${ifn1} -i ${ifn2} -filter_complex "
anullsrc=r=48000:cl=stereo,atrim=0:10[pad_a];
[0:a][pad_a]concat=n=2:a=1:v=0[a0];
[a0][1:a]amix=inputs=2,asplit[a][av];
[av]channelsplit[av1][av2];
[av1]showcqt=s=960x270,setsar=1[v1];
[av2]showcqt=s=960x270,setsar=1,vflip[v2];
[v1][v2]vstack[v]
" -map '[v]' -map '[a]' amixed1.mp4
silence by ‘aevalsrc’
#! /bin/sh
ifn1=bb20.mp4  # 20 secs / 48000Hz
ifn2=bb30.mp4  # 30 secs / 48000Hz
#
ffmpeg -y -i ${ifn1} -i ${ifn2} -filter_complex "
aevalsrc='0|0:s=48000:d=10'[pad_a];
[0:a][pad_a]concat=n=2:a=1:v=0[a0];
[a0][1:a]amix=inputs=2,asplit[a][av];
[av]channelsplit[av1][av2];
[av1]showcqt=s=960x270,setsar=1[v1];
[av2]showcqt=s=960x270,setsar=1,vflip[v2];
[v1][v2]vstack[v]
" -map '[v]' -map '[a]' amixed2.mp4
see also

anullsrc