#!/bin/bash
#
#******************************************************************************
# session
#------------------------------------------------------------------------------
##
# \file        session
# \library     Home/Audio
# \author      Chris Ahlstrom
# \date        2014-03-18
# \update      2019-09-28
# \version     $Revision$
#
#     This script provides a way to make a pretty specific connection setup
#     on Linux using JACK.
#
#     See the file seq-session-layout.odg (LibreOffice Draw file).
#
#     This provides an extended version of seq-session that can also
#     stop/start the mpd and jackd daemons.
#
#  Dependencies:
#
#     -  sudo and the text noted below in a file in /etc/sudoers.d
#     -  jackd and jack_connect
#     -  aplay (for listing devices)
#     -  a2jmidid
#     -  zita-j2a
#     -  qjackctl
#     -  yoshimi (software synthesizer)
#     -  qseq66 (MIDI loop application)
#
#  systemctl-doers:
#
#     Create this file in /etc/sudoers.d, with the following line of text:
#
#        myusername myhostname =NOPASSWD:
#           /bin/systemctl stop mpd,/bin/systemctl start mpd
#
#     This is actually one line of text.  It is specific to Debian Sid
#     (unstable) running systemd.  It will differ between systems.
#
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Adjustable variables
#------------------------------------------------------------------------------

SEQ66="qseq66"

REDHAT_START_MPD="systemctl start mpd"
REDHAT_STOP_MPD="systemctl stop mpd"

UBUNTU_START_MPD="service mpd start"
UBUNTU_STOP_MPD="service mpd stop"

START_MPD="$UBUNTU_START_MPD"
STOP_MPD="$UBUNTU_STOP_MPD"

TEST_SONG="$HOME/Home/Audio/Seq24/click_4_4.midi"

# YOSHIMI_STATE="$HOME/.config/yoshimi/yoshimi.state"

YOSHIMI_STATE="$HOME/Home/ca/mls/git/yoshimi-cookbook/sequencer64/b4uacuse/yoshimi-b4uacuse-gm.state"

#------------------------------------------------------------------------------
# State variables
#------------------------------------------------------------------------------

DO_SETUP="yes"
DO_TEARDO_WN="no"
DO_HELP="no"
DO_ADVICE="yes"
DO_QJACKCTL="yes"
DO_A2JMIDID="yes"
DO_ZITAJ2A="no"
DO_SEQ66="yes"
DO_QSYNTH="no"
DO_YOSHIMI="yes"
DO_YOSH_CONNECT="no"
DO_MIDI_CONNECT="no"
DO_SKIP_CONNECTS="yes"
DO_MANUAL_PORTS="yes"

#------------------------------------------------------------------------------
# die $exitcode $project $errormessage
#------------------------------------------------------------------------------
#
# Unfortunately, we need to figure out to get failure data from a daemon.
#
#------------------------------------------------------------------------------

function die()
{
   EXITCODE=$1
   CURPROJECT=$2
   MESSAGE="? [$CURPROJECT] $3"
   MESSAGE+=$'\n'
   shift 3
   while [ "$1" != "" ] ; do
      MESSAGE+="  "
      MESSAGE+="$1"
      MESSAGE+=$'\n'
      shift
   done

   if [ "$DO_LOG" == "yes" ] && [ "$LOGFILENAME" != "" ] ; then
      echo "$MESSAGE" >> $LOGFILENAME
   fi

   echo "$MESSAGE"
   echo "Run this script with the --help option for more information."
   echo "Will stop all processes already started."
   echo ""
   DO_TEARDO_WN="yes"
   exit $EXITCODE
}

#------------------------------------------------------------------------------
# USB Audio detection
#------------------------------------------------------------------------------
#
#     Figures out the device numbers for our USB audio device.  Might also
#     search for "CODEC", instead of "USB". Depends on the system.
#
# HWUSB="hw:2"
#
#------------------------------------------------------------------------------

MAINHWUSB=$(aplay -l | grep USB | awk '{ print substr($2, 0, 1) }')
if [ "$MAINHWUSB" == "" ] ; then
   DO_ZITAJ2A="no"
   echo "Warning: No USB audio device plugged in."
else
   SUBHWUSB=$(aplay -l | grep USB | awk '{ print substr($8, 0, 1) }')
   export HWUSB="hw:$MAINHWUSB"        # HWUSB="hw:$MAINHWUSB,$SUBHWUSB"
fi

#------------------------------------------------------------------------------
# Options-processing loop
#------------------------------------------------------------------------------

if [ $# -ge 1 ] ; then

   while [ "$1" != "" ] ; do

      case "$1" in

         --setup | -s | --start)
            DO_SETUP="yes"
            DO_TEARDO_WN="no"
            ;;

         --teardown | -t | --stop)
            DO_SETUP="no"
            DO_TEARDO_WN="yes"
            DO_ADVICE="no"
            ;;

         --no-advice | --na)
            DO_ADVICE="no"
            ;;

         --no-connects)
            DO_SKIP_CONNECTS="yes"
            ;;

         --manual)
            DO_MANUAL="yes"
            ;;

         --auto)
            DO_MANUAL="no"
            ;;

         --j2a)
            DO_ZITAJ2A="yes"
            shift
            if [ "$1" != "" ] ; then
               export HWUSB="$1"
            fi
            ;;

         --no-j2a)
            DO_ZITAJ2A="no"
            ;;

         --song)
            shift
            if [ "$1" != "" ] ; then
               TEST_SONG="$1"
            fi
            ;;

         --state)
            shift
            if [ "$1" != "" ] ; then
               YOSHIMI_STATE="$1"
            fi
            ;;

         --help)
            DO_HELP="yes"
            DO_SETUP="no"
            DO_ADVICE="no"
            ;;

      esac
      shift
   done
fi

if [ "$DO_SETUP" == "yes" ] ; then

#------------------------------------------------------------------------------
# Stop MPD and start JACK
#------------------------------------------------------------------------------

    sudo $UBUNTU_STOP_MPD

#------------------------------------------------------------------------------
# QJackCtl
#------------------------------------------------------------------------------
#
#     Run qjackctl and start jack, with this setting in ~/.jackdrc or
#     ~/.config/rncbc.org/QjackCtl.conf:
#
#        /usr/bin/jackd -dalsa -dhw:0 -r48000 -p1024 -n2
#
#     Can also add --active-patchbay=<path>
#
#------------------------------------------------------------------------------

    if [ "$DO_QJACKCTL" == "yes" ] ; then
      echo "Starting QJackCtl/JACK: 'qjackctl --start'"
      qjackctl --start &
      sleep 2
    fi

#------------------------------------------------------------------------------
# a2jmidid
#------------------------------------------------------------------------------
#
#     Start the ALSA-to-JACK bridge, bridging the hardware ports as well as
#     the software ports.
#
#     An alternative is a2jmidi_bridge, which creates only one MIDI capture
#     (readable) output port and a writable ALSA playback client.
#
#------------------------------------------------------------------------------

    if [ "$DO_A2JMIDID" == "yes" ] ; then
      echo "Starting ALSA-to-JACK bridge: 'a2jmidid --export-hw'"
      a2jmidid --export-hw &
      sleep 2
    fi

#------------------------------------------------------------------------------
# zita-j2a
#------------------------------------------------------------------------------
#
#     Create a writable audio client/port accessible from JACK, to support
#     audio output to the USB audio device.  Note that we'll need a way to
#     detect the correct hw port for this device, which is named "CODEC" on
#     my Linux box.
#
#------------------------------------------------------------------------------

   if [ "$DO_ZITAJ2A" == "yes" ] ; then
      echo "Starting Zira JACK-to-ALSA bridge: 'zita-j2a -d $HWUSB'"
      if [ "$HWUSB" == "" ] ; then
         echo "? Need to export HWUSB=hw:X, use 'aplay -l' to find X."
      else
         zita-j2a -d $HWUSB &
         sleep 2
      fi
   fi

#------------------------------------------------------------------------------
# qseq66
#------------------------------------------------------------------------------
#
#     With qseq66.rc containing:
#
#        [manual-ports]
#        1
#
#     or using the --manual-ports option on the command line.
#
#------------------------------------------------------------------------------

   if [ "$DO_SEQ66" == "yes" ] ; then
      echo "Starting sequencer with song '$TEST_SONG':"
      if [ "$DO_MANUAL" == "yes" ] ; then
          echo "   $SEQ66 --manual-ports ..."
          $SEQ66  --manual-ports "$TEST_SONG" &
      else
          echo "   $SEQ66 --auto-ports ..."
          $SEQ66  --auto-ports "$TEST_SONG" &
      fi
      sleep 2
   fi

#------------------------------------------------------------------------------
# yoshimi and jack_connect
#------------------------------------------------------------------------------
#
#     Start yoshimi and bug out.  Starts it with options to use JACK MIDI
#     input, JACK audio output, and to auto-connect to JACK audio.
#
#------------------------------------------------------------------------------

   if [ "$DO_YOSHIMI" == "yes" ] ; then
      if [ "$DO_ZITAJ2A" == "yes" ] ; then
         if [ "$DO_YOSH_CONNECT" == yes ] ; then
            echo "Starting yoshimi soft synth:"
            echo "   yoshimi -j -J --state=$YOSHIMI_STATE"
            yoshimi -j -J --state=$YOSHIMI_STATE &
            sleep 2
            echo "jack_connect yoshimi:left zita-j2a:playback_1"
            if [ "$DO_SKIP_CONNECTS" == "no" ] ; then
               jack_connect yoshimi:left zita-j2a:playback_1
            else
               echo "... skipped."
            fi
            echo "jack_connect yoshimi:right zita-j2a:playback_2"
            if [ "$DO_SKIPCONNECTS" == "no" ] ; then
               jack_connect yoshimi:right zita-j2a:playback_2
            else
               echo "... skipped."
            fi
         else
            echo "Starting yoshimi synthesizer, unconnected to audio:"
            echo "   yoshimi -j -J --state=$YOSHIMI_STATE"
            yoshimi -j -J --state=$YOSHIMI_STATE &
         fi
      else
         if [ "$DO_YOSH_CONNECT" == yes ] ; then
            echo "Starting yoshimi synthesizer, output to default audio:"
            echo "   yoshimi -j -J -K --state=$YOSHIMI_STATE"
            yoshimi -j -J -K --state=$YOSHIMI_STATE &
            sleep 2
            echo "jack_connect yoshimi:left system:playback_1"
            if [ "$DO_SKIP_CONNECTS" == "no" ] ; then
               jack_connect yoshimi:left system:playback_1
            else
               echo "... skipped."
            fi
            sleep 1
            echo "jack_connect yoshimi:right system:playback_2"
            if [ "$DO_SKIP_CONNECTS" == "no" ] ; then
               jack_connect yoshimi:right system:playback_2
            else
               echo "... skipped."
            fi
         else
            echo "Starting yoshimi synthesizer, unconnected to audio:"
            echo "   yoshimi -j -J --state=$YOSHIMI_STATE"
            yoshimi -j -J --state=$YOSHIMI_STATE &
         fi
      fi
   fi

#------------------------------------------------------------------------------
# Other
#------------------------------------------------------------------------------
#
#     Other connections for setup.  The names of JACK ports can be found
#     using the jack_lsp command.  Unfortunately, the exact names can
#     change, and thus the connection commands can therefore FAIL.
#
#     jack_connect "a2j:qseq66 [131] (capture): [1] qseq66 1" "yoshimi:midi in"
#     jack_connect "a2j:nanoKEY2 [20] (capture): nanoKEY2 MIDI 1"
#         "a2j:qseq66 [131] (playback): qseq66 in"
#
#------------------------------------------------------------------------------

   if [ "$DO_MIDI_CONNECT" == yes ] ; then
      sleep 2
      NANOKEYOUT=$(jack_lsp | grep "nanoKEY2.*capture")
      SEQ66IN=$(jack_lsp | grep "$SEQ66.*playback")
      SEQ66OUT=$(jack_lsp | grep "$SEQ66.*capture.* 1$")

      echo "jack_connect '$SEQ66OUT' output to"
      echo "   'yoshimi:midi in'"
      if [ "$DO_SKIP_CONNECTS" == "no" ] ; then
         jack_connect "$SEQ66OUT" "yoshimi:midi in"
      else
         echo "... skipped."
      fi
      echo "jack_connect '$NANOKEYOUT' to"
      echo "   '$SEQ66IN'"
      if [ "$DO_SKIP_CONNECTS" == "no" ] ; then
         jack_connect "$NANOKEYOUT" "$SEQ66IN"
      else
         echo "... skipped."
      fi
   fi

fi

if [ "$DO_TEARDO_WN" == "yes" ] ; then

   echo "Killing yoshimi..."
   killall yoshimi
   sleep 1
   echo "Killing $SEQ66..."
   killall $SEQ66
   sleep 1
   echo "Killing a2jmidid..."
   killall a2jmidid
   sleep 1
   echo "Killing zita-j2a..."
   killall zita-j2a
   sleep 1
   echo "Killing qjackctl..."
   killall qjackctl
   sleep 1
   echo "Killing jackd..."
   killall jackd
   echo "Restarting mpd..."
   sudo systemctl start mpd

fi

if [ "$DO_HELP" == "yes" ] ; then

   cat << E_O_F

seq-session v 0.1 2014-09-28

   Usage: seq-session [ options | --help]

This script helps you set up for doing MIDI work using JACK, Seq66,
and the Yoshimi software synthesizer.

Options:

   --setup,       The default, this option starts all the applications, with
     --start, -s  pauses in between to allow settling.
   --teardown,    This option stop all of the applications in reverse order.
     --stop, -t
   --auto         Use the auto-connect ports feature of $SEQ66.
   --manual       Use the manual (virtual ports) feature of $SEQ66.
   --j2a [hw:x]   Start a writable client to get access to another audio output
                  device.  This is now the default, and the hardware device
                  is detected
   --state file   Open the file as the Yoshimi state file.  The default value
                  is '$YOSHIMI_STATE'.
   --no-advice    Don't show the 'advice' after running the script.
   --no-connects  Don't make connections.  Useful for debugging.
   --help         Show this help banner and the advice.
E_O_F

fi

if [ "$DO_ADVICE" == "yes" ] ; then

   cat << E_O_F

Check or make the following settings; jack_connect makes most of them:

   -  Verify that Yoshimi, Seq66, QJackCtl, and JACK are running.
   -  In QJackCtl's Audio tab, verify that Yoshimi is connected to "System"
      or, for USB audio output, "Zita-j2a".
   -  In QJackCtl's MIDI tab, connect "$SEQ66 [131](capture): [1] $SEQ66 1" to
      "yoshimi: midi in", if not already connected by jack_connect.
   -  In Seq66, open the file "~/Home/Audio/Seq24/click_4_4.midi".
      Right-click on the "Updown Clicks" pattern and edit it.  Verify that
      the output bus is set to [1] $SEQ66 1, and that the MIDI channel is 2.
      Turn on the "sequence dumps to MIDI buss" button.
   -  In Yoshimi, do Instrument / Show Instrument Bank, and select Drums.
      Then select "Drums Kit1b" or "CR78 Combo".  Close that dialog; set
      Midi Chan to 2. (One can also save the state of Yoshimi in a file
      with the same base-name as the MIDI sequence you create, and open that
      state file to achieve the settings very quickly.)
   -  In Seq66, verify that "Updown Clicks" is selected.  Click the play
      button and verify that the pattern plays.
   -  In the QJackCtls MIDI tab, connect keyboard to "a2j / $SEQ66 [131](playback):
      $SEQ66 in".  In Seq66, edit the "Keyboard" pattern (empty) and make
      sure that the output bus is set to [1] $SEQ66 1, and that the MIDI
      channel is 1.  In Yoshimi, set Part 2 to MIDI channel 1 and pick the
      desired patch from the desired instrument bank.  Make sure the part is
      "Enabled". Now you should be able to play along with the beat.
   -  Open a mixer application (e.g. XFce4-mixer), and make sure the audio
      output (default or USB audio) is turned up enough to hear.

E_O_F

fi

#------------------------------------------------------------------------------
# vim: ft=sh
#------------------------------------------------------------------------------
