Monday 17 May 2021

Bash Script for sonification of images using sox in batch mode.


 

Image created using script below

 

Bash scripts are really quite useful, they enable me to build up a toolbox of effects and methods tailored to the way that I work and think, once I have one worked out I can usually see a few different ways to use it - this script comes out of earlier scripts and also builds on conversations with and work of people in Facebooks Gac group such as  Avery Chester, Dawnia Darkstone and the weekly online Glitchlab meetings run by Vedran Gligo and Dina Dina Karadžić ( here).

 

Oh those fancy audacity effects you can use on  images, but maybe you want to do more than one image at a time , maybe you want to split a video into stills and apply all those juicy echoes and flangers and maybe a little noise as well, well this script is for you. 

This script was made on linuxmint 19.3 and also linuxmint 20.1 , you might be able to adapt it for windows using git bash and chocolatey but I don't use windows so on that your on your own until I get round to working a guide out for that OS (gmic support seems a bit sketchy with windows as well which is why I've commented it out  but it works just fine in Linux) If you look at the previous post I wrote a guide for Windows 10 here - https://crash-stop.blogspot.com/2021/05/quick-and-dirty-guide-to-using-shell.html

Requirements for linux - sox and gmic ( if you want to also use gmic effects on your files from the script - I just use the displacement map ) so on Debian based distros sudo apt install sox gmic. You will also want to have imagemagick installed because it just makes things easier for transcoding to different image formats, what could be simpler than turning jpgs into ppm with this command 'mogrify -format ppm  *.jpg' ?).

Caveats and warnings - The script uses ppm as an example image format but you are free to use whatever you choose but be aware that the format you use needs to be a near raw / uncompressed format otherwise you will end up with unreadable files ( well unreadable using  a standard imageviewer but that's a different story ) . I should also state that this script works on the files in place ie the script works on the directory it is in so remember to back up source files elsewhere i generally have the jpgs I work from in the directory I'm working in and transcode in place to a different format ie ppm/tga/bmp etc.

To run the script copy from #!/bin/bash to done then paste that into a blank txt file save it with a name and .sh extension so something like soundburger.sh , you must then make that file executable in linux so either right click and select properties , permission and tick the box which says make this file executable or open a bash shell  in the folder you've saved the file to and type in

$ chmod u+x soundburger.sh

To run the script copy and paste it into the folder where you have your prepared images , open a bash shell in that folder ( or navigate to it in a shell using cd ) and type in ./soundburger.sh and watch your images change before your eyes.

 Just to explain what its doing ( think of the script as a loop which looks for files, acts on a single file at a  time, moving on to the next file until there are no more files to work on at which points the script exits) .

1) find . -type f -name '*.ppm' - look in the current folder for any file with extension  .ppm . Having found a file matching that move on to the next stage

2) | - pipe each file it finds to the next stage which reads the filename  and echos the filename on the commandline in the bash terminal where the script is being run from  ( handy to know which file you are at ) that filename is now passed to the rest of the script as  ${filename}  

3)  'head -n 3 ${filename} > head.ppm;' - take the first three lines of the file ( basically the header which tells an image viewer what kind of file it is and how to display it) and store that to add back onto the file after the program has glitched the rest of the file 

4) 'sed '1d'  ${filename} > swap.ppm' - strip the header from the file and save the rest of the file as a new file called swap.ppm

5) 'cp swap.ppm frame.raw' - as the file is now headerless and for all intents and purposes a raw file copy swap.ppm and rename it as frame.raw as we need a raw file for sox to read as we are fooling sox into thinking we are working with a raw audio file.

 6) 'sox -r 482170 -e u-law frame.raw frame2.raw phaser 0.8 0.74 3 0.7 0.5' - apply an audio effect to frame.raw ( the commented out parts after with the # can be added after where I have frame2.raw just delete and copy and paste as applicable ) and save the output as frame2.raw

7) finished with the audio effects its time to put the file back together , first we 'cp frame2.raw swap.ppm;' copy frame2.raw overwriting swap.ppm

8)  Then finally put the file back together and overwrite the initial file by doing ' cat head.ppm swap.ppm> ${filename};' cat is unix/linux speak for concatenate ie add one file to the top of the other creating a new file ( in this case we are overwriting the source file ${filename} ,obviously you could redirect the output at this stage to another folder as its only this final part which overwrites the original .

9) If using ffmpeg displacement  uncomment the 5 lines after the above, ffmpeg then uses a displacement map on the files for extra glitchiness ( script for windows 10 though it will work on linux as well with ffmpeg installed).

10) Clean up by removing all those temporary files with 

'rm head.ppm
rm swap.ppm
rm frame.raw
rm frame2.raw'

11) Check if there are any more files left ( remember this loops over all the files in the folder you are running it from ) to act on if not then 'done' and exit to command prompt

#!/bin/bash

find . -type f -name '*.ppm'|while read filename; do echo ${filename};

#get header
  head -n 3 ${filename} > head.ppm;

#strip header ( to avoid damaging it)
  sed '1d'  ${filename} > swap.ppm

#copy that to raw file so we can use sox
cp swap.ppm frame.raw
 
#apply effect you want by uncommenting or changing.
 #echo
#sox -r 482170 -e u-law frame.raw frame2.raw echos 0.8 0.7 700 0.25 900 0.3 norm;
#reverb
#sox -r 482170 -e u-law frame.raw frame2.raw gain -3 pad 2 8 reverb
#hilbert
#sox -r 482170 -e u-law frame.raw frame2.raw hilbert -n 5001
#try these and alter to taste ( obviously without the quotes)
sox -r 482170 -e u-law frame.raw frame2.raw phaser 0.8 0.74 3 0.7 0.5
    #"bass 5"
    #"echo 0.8 0.88 60 0.4"
    #"flanger 0 2 0 71 0.5 25 lin"
    #"hilbert -n 5001"
    #"loudness 6"
    #"norm 90"
    #"overdrive 17"
    #"phaser 0.8 0.74 3 0.7 0.5"
    #"phaser 0.8 0.74 3 0.4 0.5"
    #"pitch 2"
    #"riaa"
    #"sinc 20-4k"
    #"vol 10"

cp frame2.raw swap.ppm;

#uncomment three lines below if using gmic
#cat head.ppm swap.ppm> swap2.ppm;
#gmic ${filename} swap2.ppm -blend xor -o ${filename};
#rm swap2.ppm

#if using gmic comment out line below
cat head.ppm swap.ppm> ${filename};

rm head.ppm
rm swap.ppm
rm frame.raw
rm frame2.raw

done

On Windows 10 rather than using gmic for displacement maps we could use ffmpeg , as there are problems with installing gmic for use on the command line in Win 10, so with chocolatey installed ( see previous post if you haven't already to see how to run bash scripts on Windows 10)   go here https://community.chocolatey.org/packages/ffmpeg ) copy choco install ffmpeg , paste that into power shell running as adminstrator follow the prompts and install ffmpeg.  Place  this script in the folder where your pictures are, make it executable using git-bash terminal ( see previous post) then run it from the terminal ( remembering to alter the script to your taste and to reflect the images you are working on ) : 

 

#!/bin/bash 

find . -type f -name '*.ppm'|while read filename; do echo ${filename};

#get header
  head -n 3 ${filename} > head.ppm;

#strip header ( to avoid damaging it)
  sed '1d'  ${filename} > swap.ppm

#copy that to raw file so we can use sox
cp swap.ppm frame.raw
 
#apply effect you want by uncommenting or changing.
 #echo
sox -r 482170 -e u-law frame.raw frame2.raw echos 0.8 0.7 700 0.25 900 0.3 norm;
#reverb
#sox -r 482170 -e u-law frame.raw frame2.raw gain -3 pad 2 8 reverb
#hilbert
#sox -r 482170 -e u-law frame.raw frame2.raw hilbert -n 5001
#try these and alter to taste ( obviously without the quotes)
#sox -r 482170 -e u-law frame.raw frame2.raw phaser 0.8 0.74 3 0.7 0.5
    #"bass 5"
    #"echo 0.8 0.88 60 0.4"
    #"flanger 0 2 0 71 0.5 25 lin"
    #"hilbert -n 5001"
    #"loudness 6"
    #"norm 90"
    #"overdrive 17"
    #"phaser 0.8 0.74 3 0.7 0.5"
    #"phaser 0.8 0.74 3 0.4 0.5"
    #"pitch 2"
    #"riaa"
    #"sinc 20-4k"
    #"vol 10"

cp frame2.raw swap.ppm;

#uncomment the 5 lines below if using ffmpeg for displacement
cat head.ppm swap.ppm> swap2.ppm;
rm swap.ppm
ffmpeg -i ${filename} -i swap2.ppm -lavfi '[1]split[x][y],[0][x][y]displace' swap.ppm;
rm swap2.ppm
cp swap.ppm ${filename}

#if using ffmpeg for displacement comment out line below
#cat head.ppm swap.ppm> ${filename};

rm head.ppm
rm swap.ppm
rm frame.raw
rm frame2.raw

done




 

ikillerpulse ( requires tomato.py in working directory)

I've been working on this script for the last week or so. What does it do? It takes an input video/s,  converts to a format we can use f...