Help with shell script

I have this macro that normalizes audio, but it creates a copy inside a folder called "Normalized" and in most scenarios that's what I want it to do.
Now I have to normalize around 1,000 audio files, but I don't want to normalize a copy, because the files are all over the place. I'm just finding them via a Finder search and then I want to select them and normalize.

This is the script I have, but this was created by someone else, not me, because this is still too complex for me. I tried removing some lines, but I always get an error related to the section that says "fi" and I have no idea what that is...

#!/bin/bash
normalizationValue=""$KMVAR_Local__dB""

for f in "$KMVAR_Local__audioFile"
do
 normalizedFolder="$(dirname "$f")/_Normalized"
 fileName=$(basename "$f")
 if [ ! -d "${normalizedFolder}" ]; then
 mkdir "${normalizedFolder}"
 fi
 /Applications/sox/sox --norm=${normalizationValue} "$f" "${normalizedFolder}/${fileName}"
done

Hi, @alltiagocom. I recommend changing...

normalizationValue=""$KMVAR_Local__dB""

to...

normalizationValue="${KMVAR_Local__dB}"

If you still have a problem, please report back.


On a related note, when troubleshooting macros, you might find this macro helpful: Engine.log Tool

Hi Jim,

The script works as is, when the files are saved to the "_Normalized" folder, even with that thing you mentioned.

My issue right now it's not that, unfortunately.
All I want is to normalize the actual audio file, instead of creating a normalized copy inside the "_Normalized" folder.

For example this is my structure:
Audio Files (folder)
---- file 1.wav
---- file 2.wav
---- file 3.wav

right now what I get is this:
Audio Files (folder)
----_Normalized
------------ file 1.wav
------------ file 2.wav
------------ file 3.wav
---- file 1.wav
---- file 2.wav
---- file 3.wav

So I would like to modify the script to keep the original structure and just normalize the audio themselves.

Thank you for sharing. I believe you sent me the link before. I just haven't had the time to download it and fully test it, because it seems to require some time to understand how it works. Will download it now and as soon as I have some extra time, will definitely see how it works. Appreciate it!

Okay, @alltiagocom, before you try the following, please make a backup of your files.

I do not have the sox command, but assuming it can't write over the original file, here's one approach you could take:

#!/bin/bash
normalizationValue="${KMVAR_Local__dB}"

for f in "${KMVAR_Local__audioFile}"
do
    tmpFile=$(mktemp "${TMPDIR:-/tmp}tempfile.XXXXXXXXXX")
    /Applications/sox/sox --norm=${normalizationValue} "$f" "$tmpFile"
    mv "$tmpFile" "$f"
done

I have a dedicated folder just for tests :slight_smile:

I believe it can, because it seems that the ffmpeg-normalize command is just for that. I chose the route of adding the files to a folder, just because sometimes it's safer in case something goes wrong. When I want to normalize just a few files I then go and check if the normalized files are good to go and then I move and replace the old files.

Do you mind clarifying what your script is doing?
It seems that it's creating a temporary file and then moving it to the original folder, replacing the original file?

If so, I want to avoid this approach, because I'm using a plugin (Loopcloud) and I don't know how the plugin works in terms of identifying the audio files.
If you're not familiar with Loopcloud, it basically lets us add audio files and then add tags to make it easier to find them. Now since I have all my library already with tags, I'm afraid that if I replace those files with new versions, the tags will be lost, that's why I just wanted to normalize the actual files.

I can try your script, but looking at the script I have, what would I be able to remove to make the "normalizedFolder" variable be the current folder? Maybe that would overwrite the file?

I tried this, but it doesn't work:

#!/bin/bash
normalizationValue=""$KMVAR_Local__dB""

for f in "$KMVAR_Local__audioFile"
do
 fileName=$(basename "$f")
 fi
 /Applications/sox/sox --norm=${normalizationValue} "$f" "${fileName}"
done

This is what I get:
Execute a Shell Script failed with script error: text-script: line 7: syntax error near unexpected token fi' text-script: line 7: fi'.

If I remove the "fi" line I get this:
Execute a Shell Script failed with script error: /Applications/sox/sox WARN getopt: option `6' not recognized
/Applications/sox/sox FAIL sox: invalid option.

So I just tested your script.
It doesn't show any error when running the macro, but then when I look at the file it replaced, it becomes unusable, like this:

image

Yes, that's what it is doing.

Since I don't have sox I'm unable test. But if you think the command can write over the original, then you could maybe take this approach:

```bash
#!/bin/bash
normalizationValue="${KMVAR_Local__dB}"

for f in "${KMVAR_Local__audioFile}"
do
    /Applications/sox/sox --norm=${normalizationValue} "$f" "$f"
done

Even with that, however, I doubt if your tags would be retained for the files that are normalized.

Was the file's original name -6 copy.wav?

If not, try the shell script again. A minute after I shared it, I made a change to the following command:

tmpFile=$(mktemp "${TMPDIR:-/tmp}tempfile.XXXXXXXXXX")

The original version had a / after the right }. I had forgotten that mktemp "${TMPDIR:-/tmp} would end with a /.


Does the sox command have a man page? If you don't know, open Terminal and type: man sox

Tried the new script, but I get the same result, but this time instead of a zero kb file, it's like 44kb, but still not playable.

When I normalize them manually by using an app called Sound Studio (so basically I open the app, normalize, save the file), Loopcloud keeps everything intact.

Yes, that's the name of the file

Sorry, I'm a bit confused...
This is what you shared first:

#!/bin/bash
normalizationValue="${KMVAR_Local__dB}"

for f in "${KMVAR_Local__audioFile}"
do
    tmpFile=$(mktemp "${TMPDIR:-/tmp}tempfile.XXXXXXXXXX")
    /Applications/sox/sox --norm=${normalizationValue} "$f" "$tmpFile"
    mv "$tmpFile" "$f"
done

I haven't made any changes and this is the one that doesn't work.

It doesn't seem to have. It says that the command doesn't exist

I changed the post rather quickly, so apparently you didn't see the original version I uploaded with the minor error.


Based on your latest information, maybe I've made some incorrect assumptions:

  • Did the original version of the script create properly normalized files in the _Normalized subfolder. I assumed that they did and the only issue was the location of the normalized files.

  • Are you sure that the sox command can write over the original? And if so, is the syntax correct? Many times commands would require --output before the output file.


I suggest texting the sox command using Terminal on one file. Once you have the process worked out, then it can be incorporated into a script.

Yes, maybe that's what happened. I used the final script (which didn't work).

Yes, when I run the script I have been using, it creates the "_Normalized" folder and creates a normalized version of the files inside that folder, so basically I end up with the root folder with the non-normalized files and then on that same root folder, a "_Normalized" folder with the normalized files.

I'm not familiar with the command. I just copied from someone and I've been using it, because it's been enough to have that extra step with the "_Normalized" folder. It would make sense to me that it can normalize the file itself, but I can't confirm it 100%

The syntax for the current macro I have, seems to be correct, because I've been normalizing a lot of files.

You mean using cd to go to the directory and then using a specific file to normalize?
If so, do you mind sharing an example that I can then customize with the name of the file?

Here's what I found, not sure if it helps:
https://madskjeldgaard.dk/posts/sox-tutorial-batch-processing/

Inside the sox folder in the Applications folder, there are a few txt files that are probably the same as the manual. Here they are:
sox - txt files.zip (75.7 KB)

I appreciate your time and help, but please if this is too much work and time for you, I understand.

I'll have time tomorrow to take a look. In the meantime, it would be helpful if you could upload a few audio files that you have been using to test.

1 Like

Thank you, Jim.

Here they are. They are all the same audio, just peaking at different dB (hence their names).
sox.zip (2.3 MB)

Hi, @alltiagocom. Before I work through this tomorrow, I want to make sure I understand your requirements.

Based on the variable name (KMVAR_Local__audioFile) in the original script you supplied, it appears that you are only supplying one input file (per execution of the ‘Execute Shell Script’ action). However, your script includes a for command which suggests that you want the script to process multiple files each time it runs.

Is it your intention to supply only one filename and expect the script to process all files in the same folder? If so, would you want the script to only process additional files that have the same file extension (e.g, WAV)? From an execution standpoint, that might be convenient as you’d only have to select one file but you’d have the folder open so you could scan the contents to be sure all items should be processed.

There are other possibilities that come to mind. Through the KM variable, you could supply:

  1. the parent directory and then the script could process all files in that directory. The issue here, however, is that the script might attempt to process unintended files (e.g., a README file amongst the audio files).

  2. all files to be processed, with each file on a separate line. This is the safest, but also would take a little longer to run because you would need to select all of the audio files (in the Finder), then trigger the macro.


Some quick testing indicates that sox cannot write over the original file, thus a temp file will be required. When I attempted to write over one, sox returned:

WARN wav: Premature EOF on .wav input file

And the new file was not playable.

Also, sox seems to generate an error if the extension of the outfile does not match the infile. Thus that will need to be accommodated when the temporary file is created.

1 Like

Hi Jim,

I appreciate your time and help on this.
I agree that maybe writing over the same file is not possible, from other articles I read online, they all seem to mention that.
So it's safer to just keep the original macro (I changed it slightly to fit my current scenario) and just let KM normalize all files into a _Normalized folder inside each file's folder, then I will manually drag them to the root folder. It's extra work, but maybe we are trying to do something sox isn't supposed to do and I don't want to take more of your time.

I also tested this morning with a test wav file and Loopcloud. So
I created a test.wav file,
added to Loopcloud,
added a bunch of tags,
closed Loopcloud,
normalized the file (so it created a _Normalized folder in that same folder with a normalized version of test.wav.
Then dragged that to the root folder, replaced the old test.wav,
opened Loopcloud, and it seems that the tags are intact.

I will just have to backup those original (non-normalized) files just in case and will also create a txt file with all their paths so I can find their original path in case I need to replace them again.

It's some extra work, but it's ok. Having the ability to normalize the files is already a HUGE time saver anyway.

Thank you so much for trying to help me with this! :raised_hands:
I'm sorry we couldn't find a final answer, but I guess sometimes we just have to accept it and move on, otherwise we will go crazy :slight_smile:

1 Like

This works for me, just using a for loop in keyboard maestro instead of a script loop, there's definitley a way to modify your original script to do this, but I'm just more comfortable in keyboard maestro.

This takes the finder selection as input for the loop, but you can modify the for loop to use any list.

As @_jims noted... Sox does not seem to be able to overwrite the input file as output, every time i tried it it resulted in an error, and a corrupted input file, so writing to a temp file is neccessary.

Audio Normalize Test.kmmacros (4.0 KB)

1 Like

Well, @alltiagocom, yesterday evening after I had asked and before you replied, I had worked this out.

I see now that @Evan_Mangiamele has another approach. Take your pick. :grinning:


Download: Normalize With sox.kmmacros (6.0 KB)

Macro-Image


Macro-Notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.

System Information
  • macOS 14.3.1 (23D60)
  • Keyboard Maestro v11.0.2

Note: I specified a Local__dB that you'll likely want to change.

1 Like