#!/bin/sh

# Converts value-per-line input file to simple MIDI output.
# Input value on line must be blank for silence, or 0-127 for note.
# Output is MIDI format 0.

# Usage:
#     $0 file.txt [OFFSET [INSTRUMENT]]
#
# OFFSET is the value added to a zero value to make the MIDI note.
# INSTRUMENT is the MIDI instrument number, eg 47 for harp.

# Sample input:
#
#23
#24
# 
#25

IN=$1
if [ "" = "$IN" -o ! -s "$IN" ]; then
    echo "ERROR: input file must be specified." 1>&2
    exit 1
fi

OFFSET=60
if [ $# -gt 1 ]; then
    OFFSET="$2";
    if [ "$OFFSET" -lt 1 -o "$OFFSET" -gt 120 ]; then
        echo "ERROR: OFFSET ($OFFSET) invalid." 1>&2
        exit 1
    fi
    shift
fi

INSTRUMENT=80
if [ $# -gt 1 ]; then
    INSTRUMENT="$2";
    if [ "$INSTRUMENT" -lt 1 -o "$INSTRUMENT" -gt 128 ]; then
        echo "ERROR: INSTRUMENT ($INSTRUMENT) invalid." 1>&2
        exit 1
    fi
    shift
fi

LENGTH=`wc -l < $IN | awk '{print $1}'`


# Write MIDI (format 0) binary directly to output file.
# For format see eg:
#     http://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfileformat.html
#     https://www.csie.ntu.edu.tw/~r92092/ref/midi/

# Force output to C locale (avoid UTF-8 for example).
LANG=C
export LANG

awk <$IN \
    -v LENGTH=$LENGTH \
    -v OFFSET=$OFFSET \
    -v INSTRUMENT=$INSTRUMENT \
        'BEGIN {
         printf("MThd");
         printf("%c%c%c%c", 0, 0, 0, 6); # Chunk length 6.
         printf("%c%c", 0, 0); # Format 0.
         printf("%c%c", 0, 1); # One track.
         printf("%c%c", 0, 96); # 96 per quarter-note.
NoteDeltaTime=48;
MTrkMinLength=29;
MTrkLength=MTrkMinLength + 8*LENGTH;
ll = int(MTrkLength % 256);
l1 = int(int(MTrkLength / 256) % 256);
l2 = int(int(MTrkLength / 65536) % 256);
l3 = int(int(MTrkLength / 16777216) % 256);
printf("MTrk");
printf("%c%c%c%c", l3, l2, l1, l0); # Chunk length.
printf("%c%c%c%c%c%c%c%c", 0, 255, 88, 4, 4, 2, 24, 8); # Time signature.
printf("%c%c%c%c%c%c%c", 0, 255, 81, 3, 7, 161, 32); # Tempo.
printf("%c%c%c", 0, 192, INSTRUMENT-1); # Channel 0 select instrument.
        }
    {
    NOTE=$1+OFFSET
    if(("" == $1) || (NOTE > 127)) {
        printf("%c%c%c%c", 0, 144, 49, 32); # Note on.
        printf("%c%c%c%c", NoteDeltaTime, 128, 49, 0); # Note off.
        } else {
        printf("%c%c%c%c", 0, 144, NOTE, 64); # Note on.
        printf("%c%c%c%c", NoteDeltaTime, 128, NOTE, 0); # Note off.
        }
    }
    END {
        EOFDELTA=NoteDeltaTime;
printf("%c%c%c%c", EOFDELTA, 255, 47, 0); # End of track.
    }'
