P i
P i

Reputation: 30684

Retrieve index of array element matching string in Bash

I have sound files for each of the 88 keys on a piano keyboard.

p-book:OUT pi$ ls
Piano.ff.A0.aiff    Piano.ff.Bb7.aiff   Piano.ff.Eb1.aiff
Piano.ff.A1.aiff    Piano.ff.C1.aiff    Piano.ff.Eb2.aiff
Piano.ff.A2.aiff    Piano.ff.C2.aiff    Piano.ff.Eb3.aiff
Piano.ff.A3.aiff    Piano.ff.C3.aiff    Piano.ff.Eb4.aiff
Piano.ff.A4.aiff    Piano.ff.C4.aiff    Piano.ff.Eb5.aiff
Piano.ff.A5.aiff    Piano.ff.C5.aiff    Piano.ff.Eb6.aiff
Piano.ff.A6.aiff    Piano.ff.C6.aiff    Piano.ff.Eb7.aiff
Piano.ff.A7.aiff    Piano.ff.C7.aiff    Piano.ff.F1.aiff
Piano.ff.Ab1.aiff   Piano.ff.C8.aiff    Piano.ff.F2.aiff
Piano.ff.Ab2.aiff   Piano.ff.D1.aiff    Piano.ff.F3.aiff
Piano.ff.Ab3.aiff   Piano.ff.D2.aiff    Piano.ff.F4.aiff
Piano.ff.Ab4.aiff   Piano.ff.D3.aiff    Piano.ff.F5.aiff
Piano.ff.Ab5.aiff   Piano.ff.D4.aiff    Piano.ff.F6.aiff
Piano.ff.Ab6.aiff   Piano.ff.D5.aiff    Piano.ff.F7.aiff
Piano.ff.Ab7.aiff   Piano.ff.D6.aiff    Piano.ff.G1.aiff
Piano.ff.B0.aiff    Piano.ff.D7.aiff    Piano.ff.G2.aiff
Piano.ff.B1.aiff    Piano.ff.Db1.aiff   Piano.ff.G3.aiff
Piano.ff.B2.aiff    Piano.ff.Db2.aiff   Piano.ff.G4.aiff
Piano.ff.B3.aiff    Piano.ff.Db3.aiff   Piano.ff.G5.aiff
Piano.ff.B4.aiff    Piano.ff.Db4.aiff   Piano.ff.G6.aiff
Piano.ff.B5.aiff    Piano.ff.Db5.aiff   Piano.ff.G7.aiff
Piano.ff.B6.aiff    Piano.ff.Db6.aiff   Piano.ff.Gb1.aiff
Piano.ff.B7.aiff    Piano.ff.Db7.aiff   Piano.ff.Gb2.aiff
Piano.ff.Bb0.aiff   Piano.ff.E1.aiff    Piano.ff.Gb3.aiff
Piano.ff.Bb1.aiff   Piano.ff.E2.aiff    Piano.ff.Gb4.aiff
Piano.ff.Bb2.aiff   Piano.ff.E3.aiff    Piano.ff.Gb5.aiff
Piano.ff.Bb3.aiff   Piano.ff.E4.aiff    Piano.ff.Gb6.aiff
Piano.ff.Bb4.aiff   Piano.ff.E5.aiff    Piano.ff.Gb7.aiff
Piano.ff.Bb5.aiff   Piano.ff.E6.aiff
Piano.ff.Bb6.aiff   Piano.ff.E7.aiff

I wish to rename them to their MIDI note number:

Piano.ff.A0.aiff  -> 21.aiff
Piano.ff.Bb0.aiff -> 22.aiff
Piano.ff.B0.aiff  -> 23.aiff
Piano.ff.C1.aiff  -> 24.aiff
:

(21 is the MIDI number for the lowest note on a piano)

While 88 is probably more a 'do it by hand' size, I'm curious whether it can be automated in a few lines of Bash

If:

'C'  ~ 0  
'Db' ~ 1  
'D'  ~ 2  
:  
'B'  ~ 11  

Then I could do:

MidiNote = NumberForPitchclass( pitchclassstring ) + 12 * octave

But does Bash have the apparatus for this operation?

Upvotes: 0

Views: 217

Answers (1)

Mark Reed
Mark Reed

Reputation: 95252

If you have bash 4, using associative arrays would be the way to go:

   noteNames=(C Db D Eb E F Gb G Ab A Bb B)
   declare -A noteNumbers
   for (( i=0; i<${#noteNames[@]}; ++i )); do
      noteNumbers[${noteNames[i]}]=$i
   done
   for f in *.aiff; do
      note="${f#Piano.ff.}"
      note="${note%.aiff}"
      name="${note%%[0-9]*}"
      octave="${note#$name}"
      if [ ! -n "${noteNumbers[$name]}" ]; then
         echo >&2 "$0: not renaming $f - note not found"
      else
         let midiNote=${noteNumbers[$name]}+12*octave
         mv "$f" "$midiNote.aiff"
      fi
   done

If you don't have bash 4, you can do it more manually by looping through the notes for every file instead of just once at the beginning:

noteNames=(C Db D Eb E F Gb G Ab A Bb B)
for f in *.aiff; do
   note="${f#Piano.ff.}"
   note="${note%.aiff}"
   name="${note%%[0-9]*}"
   octave="${note#$name}"
   for (( base=0; base<${#noteNames[@]}; ++base )); do
      if [[ "${noteNames[base]}" == $name ]]; then
         break
      fi
   done
   if (( base >= ${#noteNames[@]} )); then
      echo >&2 "$0: not renaming $f - note not found"
   else
      let midiNote=base+12*octave
      mv "$f" "$midiNote.aiff"
   fi
done

However, that gives A0 the number 10, where you said it was 21, so you apparently need to add 11 to those numbers.

Upvotes: 2

Related Questions