Cross fading several audio files using sox - bash

I'm trying to cross-fade several audio files together with a 3 second cross-fade and join them together in to one file with sox.
I can join several files together by the command below but not sure how to cross fade between each one:
sox $(ls /tmp/a*.wav | sort -n) /tmp/out/out.wav
I can cross fade two files with the commands below but not sure how to combine the first line that joins several files together with the second line that splices / cross fades
sox 100hz.wav 440hz.wav out.wav splice $(soxi -D 100hz.wav),3
I found this question but the answer doesn't work for me.
crossfading a group of audio files with sox splice

I don't know if you are aware of the crossfade_cat.sh script offered by sox. You could just use it successively:
./crossfade_cat.sh 1 440.wav 660.wav auto auto && ./crossfade_cat.sh 1 mix.wav 880.wav auto auto
Or if you want to crossfade a high number of wav files, to use all files in a directory you could use a shell loop, something like this:
crossfade_dur=1
i=0
for file in *.wav
do
i=$((i+1))
if [ $i -eq 1 ]
then
cp $file mix.wav
else
crossfade_cat.sh $crossfade_dur mix.wav $file auto auto
fi
done

Related

Parse csv into variables and execute ffmpeg command in loop

I am trying perform a batch operation to extract specific frames from multiple video files and save as a PNG, using a bash script. I hope to do this by using ffmpeg within a bash script, supplemented by a csv file that contains the name of the input video, the specific frame number to be extracted from the input video, and the output name of the PNG file.
The video files and the csv file would all be placed within the same folder. The script can also be placed in there if necessary.
My csv - called "select.csv" - currently takes the following format (input,output,frame):
mad0.m4v,mad0_out1,9950
mad0.m4v,mad0_out2,4500
mad1.m4v,mad1_out1,3200
My current script - called "frame.sh" - takes the following form:
#!/bin/bash
OLDIFS=$IFS
IFS=“,”
SDIR=/Users/myuser/Desktop/f-input/
cd $SDIR;
while read input output frame
do
echo "$input"
echo "$output"
echo "$frame"
input1=$input
output1=$output
frame1=$frame
ffmpeg -i "$input1" -vf select='eq(n\,'"$frame1"')' -vsync 0
"$output1".png
done < $1
IFS=$OLDIFS
This should allow me to run ./frame.sh select.csv to then process all relevant files in the "f-input" folder on my desktop and extract the specified frames.
I ended up echoing the variables read from the csv so that they could actually be used as variables and looped in the ffmpeg command because carrying out the ffmpeg command using $input, $frame and $output directly after the read operation only ever completed the process on the first line of the csv, without progressing further.
Essentially I would like the following to actually loop through each csv entry, instead of only the first line:
#!/bin/bash
OLDIFS=$IFS
IFS=“,”
SDIR=/Users/myuser/Desktop/f-input/
cd $SDIR;
while read input output frame
do
ffmpeg -i "$input" -vf select='eq(n\,'"$frame"')' -vsync 0 "$output".png
done < $1
IFS=$OLDIFS
Any and all advice appreciated!
Many thanks
Replace IFS=“,” with IFS=",".
I wrote a similar script that reads csv and process movies by ffmpeg.
It works well on the first line of the csv but fails after the second lines.
I found ffmpeg in a loop seems to affect the "read" command and trim the first character of the lines after the second line.
So I ended up with adding extra "garbage" column on the left-most side of the csv and let ffmpeg trim it.
my csv is like:
101,movie1.mp4
102,movie2.mp4
103,movie3.mp4
...
and the (simplified) script is like:
while IFS="," read id movie; do
ffmpeg -v quiet -s 1280x720 -i "$movie" "$id-$movie" </dev/null
done
it generates "101-movie1.mp4" for the first line of the csv just like I expect
but after the second line it generates "02-movie1.mp4" "03-movie3.mp4" and so force because ffmpeg (seems to have) trimmed the first character of the lines.
I added a garbage column on the 1st column like this
x,101,movie1.mp4
x,102,movie2.mp4
x,103,movie3.mp4
and fix the script:
while IFS="," read garbage id movie; do
ffmpeg -v quiet -s 1280x720 -i "$movie" "$id-$movie" </dev/null
done
this worked for me.

Create m3u8 file from list of ts files

I want to create 'm3u8' file from the list of ts files. How can I do it?
I did search in google & read documentation of ffmpeg but I didn't find anything.
It's not clear which of the following cases you're asking about, so here's a quick answer for both:
If you're starting with a single file that contains your content
This is the most common case. In general, there are three steps to creating a playable HlS stream from source material.
for each desired output level (let’s say Bitrate for simplicity), you need to create a collection of segmented .ts files.
For each output level you need a playlist manifest (m3u8) that contains the list of segment files making up the content.
For the whole stream you need a single master manifest (another m3u8) that lists the playlists.
FFMpeg can do all three of these.
If you're starting with a collection of .ts files
If you really are starting with a collection of .ts files, you could either hand-build an m3u8 file as described in the previous answer, or you could write a script to do it.
In either case, there are some considerations for the .ts files:
If the segment files do not belong to an uninterrupted sequence (as they would if they were transcoded from a single source clip for use in HLS), you’ll need to insert EXT-X-DISCONTINUITY tags between segments that don’t have the same encoding characteristics or that don’t have monotonically increasing PTS (presentation timestamp) values.
While the segments don't need to all be the same length, the longest one must not exceed the (integer) number of seconds specified in the EXT-X-TARGETDURATION tag.
"For VOD content, the average segment bit rate MUST be within 10% of the AVERAGE-BANDWIDTH attribute"
When you've built your m3u8 file, it helps to run it through a validator to find any problems.This is a lot easier than scratching your head wondering why an HLS stream plays poorly or inconsistently across players/browsers.
mediaStreamValidator on macOS is very good https://developer.apple.com/documentation/http_live_streaming/about_apple_s_http_live_streaming_tools
Also consider the online tool at Theo: http://inspectstream.theoplayer.com/
You probably want a HLS structure. There's a lot of documentation at Apple (IIRC it was invented by Apple and then got adopted widely), e.g. a draft RFC and a page with example streams.
HLS consists of two levels: a master M3U8 which references other M3U8 which in turn reference the .ts files. You can omit the master M3U8 and just provide the "second level".
As a starting point, it may look something like this:
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10, no desc
media-000001.ts
#EXTINF:10, no desc
media-000002.ts
#EXTINF:10, no desc
media-000003.ts
The EXT-X-TARGETDURATION specifies how long each .ts file is (they must all be of the same length). It may either be a relative or absolute path.
Can be done with a bash script:
#!/usr/bin/env bash
file="hls.m3u8"
echo "#EXTM3U" > $file
echo "#EXT-X-VERSION:3" >> $file
echo "#EXT-X-MEDIA-SEQUENCE:24" >> $file
echo "#EXT-X-TARGETDURATION:10" >> $file
for i in `find *.ts -type f | sort -g`; do
l=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 $i)
echo "#EXTINF:$l," >> $file
echo "$i" >> $file
done
echo "#EXT-X-ENDLIST" >> $file

ffmpeg find segments of same video recording

Here's how my camera operates:
When taking a video recording, the camera will automatically segment into a new file chapter once it has reached a certain size. These files when put together with editing software will play seamlessly as if they were never chaptered, and when played back from the camera directly will also play through as one continuation.
Which results in the video being split into three video files:
$ ls -1
GH010119.MP4
GH020119.MP4
GH030119.MP4
Which may be placed in a directory alongside segments from another video. For example:
$ ls -1
GH010119.MP4
GH010120.MP4
GH010126.MP4
GH010127.MP4
GH020119.MP4
GH020126.MP4
GH020127.MP4
GH030119.MP4
In order to use ffmpeg to concatenate the same continuous segments, I need to write a file listing the filenames of the videos I want to concatenate. So I'm finding the files with the same last four characters:
find . -name "*0119*" -execdir echo '{}' ';' | xargs -n1 echo file > concat_list.txt
But is there a way to figure out, from the videos' data or metadata, which of these files are segments of the same video?

Concatenate wav files ignore if # channels is 2

I need to concatenate all the wav files in a folder.
I tried using the command
sox folder_name/*.wav folder_name.wav
But got the error
sox FAIL sox: Input files must have the same # channels
Turns out only 21 of 2864 wav files in that folder have 2 channels instead of 1.
How can I just ignore the 21 files with 2 channels and concatenate all the 2843 wavs with 1 channel?
Use soxi to get all one channel files, put them in an array and then call sox:
for file in folder_name/*.wav; do
if soxi "$file" | grep -q 'Channel.*1'; then
files+=("$file")
fi
done
sox "${files[#]}" folder_name.wav

timelapse images into a movie, 500 at a time

I am trying to make a script to turn a bunch of timelapse images into a movie, using ffmpeg.
The latest problem is how to loop thru the images in, say, batches of 500.
There could be 100 images from the day, or there could be 5000 images.
The reason for breaking this apart is due to running out of memory.
Afterwards I would need to cat them using MP4Box to join all together...
I am entirely new to bash, but not entirely programming.
What I think needs to happen is this
1) read in the folders contents as the images may not be consecutively named
2) send ffmpeg a list of 500 at a time to process (https://trac.ffmpeg.org/wiki/Concatenate)
2b) while you're looping thru this, set a counter to determine how many loops you've done
3) use the number of loops to create the MP4Box cat command line to join them all at the end.
the basic script that works if there's only say 500 images is:
#!/bin/bash
dy=$(date '+%Y-%m-%d')
ffmpeg -framerate 24 -s hd1080 -pattern_type glob -i "/mnt/cams/Camera1/$dy/*.jpg" -vcodec libx264 -pix_fmt yuv420p Cam1-"$dy".mp4
MP4Box's cat command looks like:
MP4Box -cat Cam1-$dy7.mp4 -cat Cam1-$dy6.mp4 -cat Cam1-$dy5.mp4 -cat Cam1-$dy4.mp4 -cat Cam1-$dy3.mp4 -cat Cam1-$dy2.mp4 -cat Cam1-$dy1.mp4 "Cam1 - $dy1 to $dy7.mp4"
Needless to say help is immensely appreciated for my project
Here is something to get you started. It sorts the individual frames into time order, and then chunks them up into chunks of 500 and loops through all the chunks:
#!/bin/bash
# User-changeable number of frames per chunk
chunksize=500
# Rename files by date/time so they collate in order
jhead -n%Y-%m-%d_%H-%M-%S *.jpg
# Remove any remnants from previous runs (which may have been longer)
rm chunk* sub-*mp4
# Split filename list into chunks - chunkaa, chunkab, chunkac ...
ls *jpg | split -l $chunksize - chunk
# Put 'file' keyword before each filename
sed -i.bak 's/^/file /' chunk*
n=0
for c in chunk*; do
# Generate zero-padded output filename so that collates for final assembly too
out=$(printf "sub-%03d.mp4" $n)
echo Processing chunk $c into sequence $out
ffmpeg -f concat -i "$c" ... "$out"
((n+=1))
done
# Final assembly of "sub-*.mp4"
ffmpeg ... sub-*mp4 ...

Resources