check duration of audio files on the command-line
Asked Answered
N

20

65

I need to check the duration of a group of audio files. Is there a simple way to do this on the unix command-line?

> duration *

I have the amazing SoX app which has an option called stats that generates a set of audio info including duration. I am looking for a way to get only duration. I am flexible on output format, could be any of sample length, hh:mm:ss, or seconds. The latter would be my preference.

Numidia answered 19/1, 2012 at 20:47 Comment(0)
J
38
mp3info -p "%m:%02s\n" filename

gives you the length of the specified file in mm:ss format (mm can be greater than 59). For just the total number of seconds in the file, you'd use:

mp3info -p "%S\n" filename

To get the total length of all the mp3 files in seconds, AWK can help:

mp3info -p "%S\n" *.mp3 | awk 'BEGIN { s = 0 }; { s = s + $1 }; END { print s }'
Jealousy answered 19/1, 2012 at 20:59 Comment(3)
so wrap that in a for loop and you're all set, for file in *.mp3 ; do mp3info -p "%m:%s\n" $file ; done. Good luck to all.Coffey
mp3info -p " %f %m:%s\n" *.mp3 works as well - will print the file name and the durationBison
Only works for mp3 files, not e.g. wav or ogg containers.Boo
N
66
soxi -D filename
soxi -D *

Soxi queries metadata of audio files; D is the duration option. It supports globbing. Soxi's big brother sox does command-line audio processing.

Numidia answered 21/1, 2012 at 16:41 Comment(4)
soxi is what I use too, but unfortunately it lacks m4a supportBaryton
Is it just me, or doesn't it even support mp3? I'm getting soxi FAIL formats: no handler for file extension 'mp3'. EDIT : It turned out I had to sudo apt-get install libsox-fmt-mp3.Traject
There is also soxi -d for hours, minutes and secondsMccurry
In order to add mp3 support to sox, install libsox-fmt-mp3Suckow
J
38
mp3info -p "%m:%02s\n" filename

gives you the length of the specified file in mm:ss format (mm can be greater than 59). For just the total number of seconds in the file, you'd use:

mp3info -p "%S\n" filename

To get the total length of all the mp3 files in seconds, AWK can help:

mp3info -p "%S\n" *.mp3 | awk 'BEGIN { s = 0 }; { s = s + $1 }; END { print s }'
Jealousy answered 19/1, 2012 at 20:59 Comment(3)
so wrap that in a for loop and you're all set, for file in *.mp3 ; do mp3info -p "%m:%s\n" $file ; done. Good luck to all.Coffey
mp3info -p " %f %m:%s\n" *.mp3 works as well - will print the file name and the durationBison
Only works for mp3 files, not e.g. wav or ogg containers.Boo
L
24
ffmpeg -i <audiofile> 2>&1 | grep Duration
Luciolucita answered 6/2, 2018 at 2:48 Comment(1)
You can also use ffprobe that comes with ffmpeg and is designed for this.Heron
C
19

mediainfo will return to you the milliseconds of an audio file. Assuming the current directory only has audio files, the following

mediainfo --Inform="Audio;%Duration%" "Miley Cyrus - Wrecking Ball.mp3"

To calculate the duration of all audio in the local directory, this gist will help:

shopt -s nullglob
let playlist_duration_ms=0
for song_file in *.{mp3,ogg,m4a,flac,wav}; do
  playlist_duration_ms=$(expr $playlist_duration_ms + $(mediainfo --Inform="Audio;%Duration%" "$song_file"))
done
shopt -u nullglob

let playlist_duration_secs=$(expr $playlist_duration_ms / 1000)
let playlist_duration_mins=$(expr $playlist_duration_ms / 60000)
let playlist_duration_remaining_secs=$(expr $playlist_duration_secs - $(expr $playlist_duration_mins \* 60))

echo $playlist_duration_mins minutes, $playlist_duration_remaining_secs seconds
Corona answered 19/10, 2015 at 18:17 Comment(3)
This tool is great, works for ogg files as well and is available in the debian repositoriesCrossbench
I think this answer requires the least resources from the machine running it compared to other answers.Ablaut
Only prints a blank line for me.Luciolucita
C
18

The raw duration in seconds can be obtained with a high degree of precision with the use of ffprobe of ffmpeg, as follows:

ffprobe -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "filename.mp3" 2>/dev/null

The output, easy to use in further scripting, is formatted like this:

193.656236

Extending upon that, the following will measure the total duration in seconds of all .mp3 files in the current directory:

LENGTH=0; for file in *.mp3; do if [ -f "$file" ]; then LENGTH="$LENGTH+$(ffprobe -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file" 2>/dev/null)"; fi; done; echo "$LENGTH" | bc

And to measure the total length of audio files of several extensions, another wildcard may be appended:

LENGTH=0; for file in *.mp3 *.ogg; do if [ -f "$file" ]; then LENGTH="$LENGTH+$(ffprobe -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file" 2>/dev/null)"; fi; done; echo "$LENGTH" | bc
Cyruscyst answered 2/12, 2019 at 14:10 Comment(0)
G
13

on OSX

Print the length of each audio file in the current dir:

afinfo * | awk '/estimated duration/ { print $3 }'

Include the filepath:

afinfo * | awk '/File:/ { song=$2 } /estimated duration/ { print song, $3 }'
Gilbertgilberta answered 27/2, 2016 at 0:39 Comment(0)
H
12

with ffprobe

ffprobe your_file.mp3 2>&1 | grep "Duration"

The output looks like this:

Duration: 00:44:33.50, start: 0.011995, bitrate: 128 kb/

Heron answered 19/5, 2019 at 17:24 Comment(1)
With xargs find *.mp3 | xargs -I {} ffprobe -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{}" 2>/dev/null | awk 'BEGIN { s = 0 }; { s = s + $1 }; END { print s }'Cinelli
R
10

In addition to cdosborn's answer, to calculate the length of all .mp3 files recursively in subfolders of current directory and show the total sum result in days:hours:minutes:seconds format:

In zsh:

afinfo **/*.mp3 | awk '/estimated duration/ { print $3 }' | paste -sd+ - | bc | awk '{printf("%d:%02d:%02d:%02d\n",($1/60/60/24),($1/60/60%24),($1/60%60),($1%60))}'

In bash or sh:

find . -name "*.mp3" -exec afinfo {} \; | awk '/estimated duration/ { print $3 }' | paste -sd+ - | bc | awk '{printf("%d:%02d:%02d:%02d\n",($1/60/60/24),($1/60/60%24),($1/60%60),($1%60))}'

The result is like this (7 days, 5 hours, 6 minutes, 58 seconds):

$ afinfo **/*.mp3 | awk '/estimated duration/ { print $3 }' | paste -sd+ - | bc | awk '{printf("%d:%02d:%02d:%02d\n",($1/60/60/24),($1/60/60%24),($1/60%60),($1%60))}'
7:05:06:58
$
Rebound answered 21/1, 2017 at 0:36 Comment(0)
A
9
sox --info -D file             --> duration in seconds
sox --info -d file             --> duration in HH:mm:ss.ss
sox --info file                --> metadata 
Alyciaalyda answered 29/9, 2017 at 17:5 Comment(0)
A
9

If you are interested in finding total duration of wav files in a directory using soxi and python you can use this:

soxi -D input_dir/*.wav | python -c "import sys;print(sum(float(l) for l in sys.stdin))"

change input_dir according to your input directory. If you want to find max/min duration between all wav files feel free to change sum to max or min.

Arapaho answered 2/12, 2019 at 14:57 Comment(0)
B
5

A solution based on mplayer from commandlinefu by syssyphus that works with both audio and video files:

sudo apt-get install mplayer
find -type f -name "*.mp3" -print0 | xargs -0 mplayer -vo dummy -ao dummy -identify 2>/dev/null | perl -nle '/ID_LENGTH=([0-9\.]+)/ && ($t +=$1) && printf "%02d:%02d:%02d\n",$t/3600,$t/60%60,$t%60' | tail -n 1

Get the total length of all video / audio in the current dir (and below) in H:m:s

Change the *.mp3 to whatever you want to match (e.g., *.avi, *.wav), you can remove it altogether if you want to check all files.

Example of output: 00:00:37

Beanery answered 8/2, 2018 at 5:23 Comment(0)
K
4

I expanded on Navid Naderi's answer here, and created a bash function that will give you a summary of every file's running time, plus some time totals in various formats (seconds, minutes, or total running time).

function sox_duration_total
{
  if [[ "$#" -lt 1 ]]; then
    echo "find files!"
    echo "  sox_duration_total *.wav"
    echo ""
    return
  fi
  for i in "$@"; do
    val=`soxi -d "$i"`
    echo "$val | $i"
  done
  soxi -D "$@" | python -c "import sys;print(\"\ntotal sec:    \" +str( sum(float(l) for l in sys.stdin)))"
  soxi -D "$@" | python -c "import sys;print(\"total min:    \" +str( sum(float(l) for l in sys.stdin)/60 ))"
  soxi -D "$@" | python -c "import sys;import datetime;print(\"running time: \" +str( datetime.timedelta(seconds=sum(float(l) for l in sys.stdin)) ))"
}

Output will look like:

00:01:40.97 | subatomicglue - inertialdecay - 01 - hard.wav
00:00:32.00 | subatomicglue - inertialdecay - 02 - acidbass.wav
00:04:50.80 | subatomicglue - inertialdecay - 03 - cause.of.a.new.dark.age.wav
00:02:08.00 | subatomicglue - inertialdecay - 04 - daybreak.falls.wav
00:05:20.75 | subatomicglue - inertialdecay - 05 - forestfloor.wav
00:03:56.32 | subatomicglue - inertialdecay - 06 - rabbithole.wav
00:03:50.40 | subatomicglue - inertialdecay - 07 - feedme.wav
00:06:03.97 | subatomicglue - inertialdecay - 08 - grand.wav
00:02:09.45 | subatomicglue - inertialdecay - 09 - strawberryflavoreddeath.wav
00:04:43.29 | subatomicglue - inertialdecay - 10 - subfloor.wav
00:03:36.96 | subatomicglue - inertialdecay - 11 - silicone.wav
00:04:28.47 | subatomicglue - inertialdecay - 12 - inertial decay.wav
00:01:23.81 | subatomicglue - inertialdecay - 13 - the.void.wav
00:01:18.86 | subatomicglue - inertialdecay - 14 - weet.wav

total sec:    2764.0637880000004
total min:    46.06772980000001
running time: 0:46:04.063788
Kea answered 3/3, 2021 at 16:49 Comment(0)
S
2

Another soxi based answer including the file names and duration in hours, minutes and seconds format.

$for f in *amr; do printf "$f "; soxi -d $f; done

DGT20161216.amr 00:22:04.62
DGT20170108.amr 00:28:22.80
DGT20170117.amr 00:20:05.18
Studdard answered 2/5, 2017 at 19:17 Comment(0)
K
1

mediainfo can do this, but mediainfo is one of those useful tools that's so badly documented that you almost need to know how to use it in order to learn how to use it (happens a lot in the linux world).

After hours of trials and reading high and low, I finally got it to generate a recursive comma-separated list of file names and their duration in milliseconds.

cd to the starting directory and issue the following command:

find "$(pwd)" -type f -print0 | xargs -0 -I {} mediainfo --Inform="General;%CompleteName%,%Duration%" {} > list.txt

The results will be output to list.txt.

Kerguelen answered 8/1, 2017 at 1:28 Comment(0)
E
0

(When you don't have afinfo at your disposal) I got it recursively for all my files

# install mp3info if not yet installed with
sudo apt-get install mp3info

with the find command, put the total seconds to a csv file (go to the directory with your e.g. mp3 files first)

find . -name "*.mp3" -exec mp3info {} -p "%S\r\n" >> totalSeconds.csv \;

Then open it in e.g. in LibreOffice and sum it up at the bottom (to get the hours) with

=SUM(A{start}:A{end})/60/60
Eustashe answered 11/5, 2019 at 22:14 Comment(0)
S
0

Maybe a little bit late, but this is my version.

# by dnetto

[[ "$#" -lt 1 ]] && echo -e "imput error\n\$>$0 *.mp3" && exit
echo "geting time of all songs\n"

declare td=0
declare th=0
declare tm=0
declare ts=0
function updateTotal
{
        [[ "$ts" -gt 60 ]] && tm=`echo $tm + $ts / 60 | bc` ; ts=`echo $ts % 60 | bc`
        [[ "$tm" -gt 60 ]] && th=`echo $th + $tm / 60 | bc` ; tm=`echo $tm % 60 | bc`
        [[ "$th" -gt 24 ]] && td=`echo $td + $th / 24 | bc`
}

for i in "$@"; do
        m=`mp3info -p %m $i`
        s=`mp3info -p %s $i`
        t=`mp3info -p %S $i`
        [[ "$color" = "" ]] && color="\033[38;5;199m" || color=""
        printf "$color%.2d:%.2d  -=- %s\033[0m\n" $m $s $i
        ts=`echo $ts + $t | bc`
        updateTotal
done
echo -e "\n=======================================================\n\ttotal time:"
printf "%d days %.2d hours %.2d minutes %.2d seconds" $td $th $tm $ts
echo -e "\n======================================================="                                                                            

if you don't wanna verbose, just comment printf inside to get only total

Silica answered 21/12, 2021 at 11:6 Comment(1)
for complete script look on my githubSilica
M
0

For MP3 there are many options.

When it comes to OGG, TinyTag is the best I found so far.

Shell

$ python -m tinytag --format csv /some/music.mp3
> {"filename": "/some/music.mp3", "filesize": 30212227, "album": "Album", "albumartist": "Artist", "artist": "Artist", "audio_offset": null, "bitrate": 256, "channels": 2, "comment": null, "composer": null, "disc": "1", "disc_total": null, "duration": 10, "genre": null, "samplerate": 44100, "title": "Title", "track": "5", "track_total": null, "year": "2012"}

Or Python

from tinytag import TinyTag
tag = TinyTag.get('/some/music.mp3')
print('This track is by %s.' % tag.artist)
print('It is %f seconds long.' % tag.duration)

Read tags, length and cover images of audio files supported formats

MP3 (ID3 v1, v1.1, v2.2, v2.3+)
Wave/RIFF
OGG
OPUS
FLAC
WMA
MP4/M4A/M4B
AIFF/AIFF-C
Metalinguistic answered 27/12, 2021 at 10:20 Comment(0)
M
0

Very simple single-liner for FLAC on Debian/Ubuntu, useful for almost any audio container files and SoX:

for i in *.flac ; do echo -ne "${i} - " ; soxi -d "${i}" ;  done

Also

soxi -V

could be used instead, but then you will need sed or awk and grep to turn the output in the compact form.

Misdirection answered 7/2, 2022 at 17:5 Comment(0)
M
0

The way to obtain durations for most audio and video files using ffprobe, it is also configurable to make a list (the example here is for BBcode listing):

c=0 ; NAMEPREFIX="[*]" ; DELIMITER="." ; for f in *.flac ; do infstr=`ffprobe -show_entries format=duration -show_entries "format_tags=title" -sexagesimal -of compact=p=0:nk=1 "${f}" 2>/dev/null`; c=$((${c}+1)) ; echo -e "${NAMEPREFIX}${c}${DELIMITER}" $(sed -e 's/.*|//' <<<${infstr}) [$(sed -e 's/\..*//' <<<${infstr})] ; done
Misdirection answered 14/11, 2022 at 17:4 Comment(0)
M
0

Expanding on kevin meinert's answer, I have modified the script to:

  1. Default to listing the duration of files in the current directory when no arguments are provided
  2. Allow the -r flag to search recursively
  3. Allow any number of specific arguments such as snare/*.wav soundtracks/starwars.wav (same as previous behavior)
  4. Added some QoL improvements like trimming duration and removed the running time as I didn't care about that

I also added the script to ./.local/bin rather than having it be a function, so I can call it anywhere by just running wavdur

Example:

$ wavdur -r
Recursive mode enabled, searching for WAV files recursively.
00:00:21.89 | ./240627_0003.wav
00:01:16.88 | ./240627_0005.wav
00:00:22.26 | ./240627_0004.wav
00:02:24.27 | ./240627_0006.wav
00:00:28.21 | ./240627_0007.wav
00:00:14.63 | ./ambient/bacon-sizzling-2.wav
00:00:08.33 | ./ambient/bacon-sizzling.wav

The modified script:

#!/bin/bash

if [[ "$#" -eq 0 ]]; then
  echo "No arguments provided, processing all WAV files in the current directory."
  files=$(find . -maxdepth 1 -type f -name "*.wav")
elif [[ "$1" == "-r" ]]; then
  echo "Recursive mode enabled, searching for WAV files recursively."
  files=$(find . -type f -name "*.wav")
else
  files="$@"
fi

if [[ -z "$files" ]]; then
  echo "No WAV files found."
  exit 1
fi

echo "hh:mm:ss:ms | filepath"
echo "----------------------" 

for i in $files; do
  val=`soxi -d "$i"`
  echo "$val | $i"
done

echo "$files" | xargs soxi -D | python3 -c "import sys;print(\"\ntotal sec:    \" +str( sum(float(l) for l in sys.stdin)))"
echo "$files" | xargs soxi -D | python3 -c "import sys;print(\"total min:    \" +str( sum(float(l) for l in sys.stdin)/60 ))"
#echo "$files" | xargs soxi -D | python3 -c "import sys;import datetime;print(\"running time: \" +str( datetime.timedelta(seconds=sum(float(l) for l in sys.stdin)) ))"

Methuselah answered 28/6 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.