#!/bin/bash
#
#******************************************************************************
# startjack
#------------------------------------------------------------------------------
##
# \file        startjack
# \library     Home/Audio
# \author      Chris Ahlstrom
# \date        2020-12-08
# \update      2021-09-02
# \version     $Revision$
#
#   This script provides a way to make a fast JACK setup on Linux.  See:
#
#         https://wiki.archlinux.org/index.php/JACK_Audio_Connection_Kit
#
#   Requires the python-dbus package to be installed.  The HWPORT values
#   available are found in /proc/asound/cards:  PCH, HDMI, NVidia, CODEC, etc.
#   The "dps" rate can be 44100, 48000, and higher.  The "nperiods" value
#   should be 2 for motherboard, PCI, PCI-X audio, and 3 for USB audio.
#
#   However, we have had weird input issues, including missing input and
#   extremely slow response  with this setup, but not with qjackctrl running
#   with the macroed settings below.  Still experimenting.
#
#   With the current settings, latency = 21.3 msec.
#
#   Also note to be sure to set qjackctl to use the "seq" MIDI driver; the
#   "raw" driver seems to cause a lot of issues with the a2j bridge.
#
#------------------------------------------------------------------------------

#******************************************************************************
#  Provide a sane environment, just in case it is needed.
#------------------------------------------------------------------------------

LANG=C
export LANG
export STARTJACK_DATE="2021-09-02"

#******************************************************************************
#  Default simple parameter values
#------------------------------------------------------------------------------

BLUEDEV="hci0"
BLUEMAC="20:05:13:08:06:9B"
BLUEPORT="bluealsa"
BLUEPROFILE="a2dp"
BLUETOOTH="no"
DO_BASIC="no"
DO_FULL="yes"
DO_KILL="no"
DO_QJACK_CONTROL="no"
DO_A2J_MIDID="yes"
HWPORT="PCH"
JDRIVER="alsa"
MDRIVER=seq             # raw can cause real issues with a2jmidid
NPERIODS=4              # was 2, 3 recommended for USB devices
PFRAMES=256             # was 64
SAMPLERATE=48000
SLEEPTIME15=15
SLEEPTIME2=2
SLEEPTIME=10

#******************************************************************************
#  Default option + simple parameter values
#------------------------------------------------------------------------------

#******************************************************************************
#  ALSA backend options
#
#  The long forms give issues.
#
#------------------------------------------------------------------------------

# BACKDEV=""                          # defaults to "--device hw:0" (modules.conf)
# BACKEND="--driver=$JDRIVER"         # pretty much always alsa
# REALTIME="--realtime"               # versus --no-realtime
# MSYSTEM="--midi=$MDRIVER"           # -X seq or -X raw, raw is bad
# RATE="--rate=$SAMPLERATE"           # sampling rate
# PERIOD="--period=$PFRAMES"          # frames between calls (latency = period / rate)
# PERIODS="--nperiods=$NPERIODS"      # latency = np * period / rate; np > 2
# PORTMAX="--port-max=64"             # JACK default is 256
# DUPLEX="--duplex"                   # -D, both capture and playback ports
# CAPTURE="--capture=hw:$HWPORT"      # e.g. hw:PCH (or --duplex for both)
# PLAYBACK="--playback=hw:$HWPORT"    # e.g. hw:PCH

BACKDEV=""                          # defaults to "--device hw:0" (modules.conf)
BACKEND="-d $JDRIVER"               # pretty much always alsa
REALTIME="-Rv"                      # versus --no-realtime; v = "verbose"
MSYSTEM="-X $MDRIVER"               # -X seq or -X raw, raw is bad
RATE="-r $SAMPLERATE"               # sampling rate
PERIOD="-p $PFRAMES"                # frames between calls (latency = period / rate)
PERIODS="-n $NPERIODS"              # latency = np * period / rate; np > 2
PORTMAX="-port-max 64"              # JACK default is 256
DUPLEX="-D"                         # -D, both capture and playback ports
CAPTURE="-C hw:$HWPORT"             # e.g. hw:PCH (or --duplex for both)
PLAYBACK="-P hw:$HWPORT"            # e.g. hw:PCH

DAEMON="/usr/bin/jackd"
OPTIONS="$BACKEND $RATE $PERIOD $PERIODS $MSYSTEM $DUPLEX $CAPTURE $PLAYBACK"

if [ "$1" == "--help" ] ; then

   cat << E_O_F
Usage: startjack [ options ]                        ($STARTJACK_DATE)

Starts JACK, a2jmidid, and qjackctrl. Also provides for stopping (killing) them.

The options are:

    --basic   Just start jack, a2jmidid, and qjackctl.
    --qjack   Use qjackctl to start/stop jack, and use a2jmidid.  Assumes that
              qjackctl is set up properly.
    --full    Use jack_control and a2j_control to start things.  The default.
    --kill    Stop JACK instead of starting it.

If using the --qjack option, add the following calls of this script to start and
stop JACK in your QJackCtl setup:

    startjack --qjack
    startjack --qjack --kill

However, in this case it is preferable to use the "startqjack" script,
as it is much simpler.  Read that script for more information on setup.

E_O_F
   exit 1
fi

if [ $# -ge 1 ] ; then

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

      case "$1" in

         --blue | blue)
            BLUETOOTH="yes"
            ;;

         --basic | basic)
            DO_BASIC="yes"
            DO_FULL="no"
            ;;

         --qjack | qjack)
            DO_QJACK_CONTROL="yes"
            DO_BASIC="yes"
            DO_FULL="no"
            ;;

         --full | full)
            DO_BASIC="no"
            DO_FULL="yes"
            ;;

         --kill | kill)
            DO_KILL="yes"
            ;;

         *)
            echo "? Unsupported option; --help for more information"
            exit $EXIT_ERROR_NO_SUCH_OPTION
            ;;

      esac
      shift
   done
fi

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

    if [ "$DO_KILL" == "yes" ] ; then
        if [ "$DO_A2J_MIDID" == "yes"] ; then
            killall a2jmidid
        fi
    else
        echo "Waiting $SLEEPTIME seconds for qjackctl to settle..."
        sleep $SLEEPTIME
        if [ "$DO_A2J_MIDID" == "yes"] ; then
            a2jmidid --export-hw &
            echo "Waiting $SLEEPTIME15 seconds for a2jmidid to settle..."
            sleep $SLEEPTIME15
        fi
        echo "Ready to use QJackCtl."
    fi
    exit 0
fi

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

    if [ "$DO_BASIC" == "yes" ] ; then
        if [ "$DO_A2J_MIDID" == "yes"] ; then
            killall a2jmidid
        fi
        killall jackd
    else
        if [ "$DO_A2J_MIDID" == "yes"] ; then
            a2j_control --stop
            a2j_control --exit
        fi
        jack_control exit
    fi
    killall qjackctl

elif [ "$DO_BASIC" == "yes" ] ; then

    echo "Starting jackd..."
#   /usr/bin/jackd -dalsa -r 48000 -p 256 -n 4 -X seq -D -C hw:PCH -P hw:PCH &
    echo "$DAEMON $OPTIONS"
    $DAEMON $OPTIONS &
    echo "Waiting $SLEEPTIME seconds for jackd to settle..."
    sleep $SLEEPTIME
    if [ "$DO_A2J_MIDID" == "yes"] ; then
        echo "Starting ALSA-to-JACK bridge: 'a2jmidid --export-hw'..."
        a2jmidid --export-hw &
        echo "Waiting $SLEEPTIME15 seconds for a2jmidid to settle..."
        sleep $SLEEPTIME15
    fi
    echo "Starting QJackCtl/JACK: 'qjackctl --start'..."
    qjackctl &
    sleep $SLEEPTIME2

elif [ "$DO_BASIC" == "no" ] ; then

    jack_control start
    jack_control ds $JDRIVER

    if [ "$BLUETOOTH" == "yes" ] ; then
        echo "Audio using $BLUEPORT..."
        echo "jack_control dps device $BLUEPORT WILL NOT WORK"
    else
        echo "Audio using hw:$HWPORT..."
        jack_control dps device hw:$HWPORT
    fi

    jack_control dps rate $SAMPLERATE
    jack_control dps nperiods $NPERIODS
    jack_control dps period $PFRAMES
    echo "Waiting $SLEEPTIME seconds for jack_control to settle..."
    sleep $SLEEPTIME
    if [ "$DO_A2J_MIDID" == "yes"] ; then
        a2j_control --ehw
        a2j_control --start
        echo "Waiting $SLEEPTIME seconds for a2j_control to settle..."
        sleep $SLEEPTIME
    fi
    if [ "$BLUETOOTH" == "yes" ] ; then
        echo "Starting alsa_out..."
        alsa_out -d bluealsa:HCI=$BLUEDEV,DEV=$BLUEMAC,PROFILE=$BLUEPROFILE
    else
        echo "Starting qjackctl..."
        qjackctl &
    fi

fi

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