Compare commits
No commits in common. "master" and "main" have entirely different histories.
13 changed files with 4 additions and 1078 deletions
|
@ -1,48 +0,0 @@
|
||||||
name: Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: debian-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- run: |-
|
|
||||||
apt update
|
|
||||||
apt install -y \
|
|
||||||
python3 git curl make gcc g++ pkg-config \
|
|
||||||
libboost-dev libjack-dev nlohmann-json3-dev
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- run: ./waf configure
|
|
||||||
- run: ./waf
|
|
||||||
|
|
||||||
- name: Build Debian package
|
|
||||||
run: |-
|
|
||||||
mkdir -p deb/DEBIAN
|
|
||||||
cat <<EOF | tee deb/DEBIAN/control
|
|
||||||
Package: ampswitch
|
|
||||||
Version: 0.$(date --date="@$(git show -s --format='%ct' HEAD)" '+%4Y%m%d.%H%M%S')
|
|
||||||
Section: base
|
|
||||||
Priority: optional
|
|
||||||
Architecture: amd64
|
|
||||||
Maintainer: polyfloyd <floy@polyfloyd.net>, Bob <bob-nospam-@xbmc.org>
|
|
||||||
Description: Executes a command triggered by jack audio
|
|
||||||
Depends: libboost-dev, libjack0
|
|
||||||
EOF
|
|
||||||
|
|
||||||
install -D -m 0755 build/ampswitch deb/usr/bin/ampswitch
|
|
||||||
|
|
||||||
dpkg-deb --build deb ampswitch.deb
|
|
||||||
|
|
||||||
- name: Deploy Debian package
|
|
||||||
run: |-
|
|
||||||
curl \
|
|
||||||
--fail \
|
|
||||||
--user ${{ secrets.PKG_RELEASE_CREDENTIALS }} \
|
|
||||||
--upload-file ampswitch.deb \
|
|
||||||
$GITHUB_SERVER_URL/api/packages/$GITHUB_REPOSITORY_OWNER/debian/pool/stable/main/upload
|
|
|
@ -1,7 +1,7 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
@ -664,12 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
<http://www.gnu.org/licenses/>.
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
The GNU General Public License does not permit incorporating your program
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
may consider it more useful to permit linking proprietary applications with
|
may consider it more useful to permit linking proprietary applications with
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License. But first, please read
|
Public License instead of this License. But first, please read
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
Original description from the relay board in chinlish:
|
|
||||||
|
|
||||||
|
|
||||||
Below item is powered by Canton-electronics Ltd .
|
|
||||||
|
|
||||||
Product Name: 12V 2Ch RS232 Relay Remote Control USB PC UART TTL COM Serial Ports Smart Home
|
|
||||||
|
|
||||||
Module No.: TB252
|
|
||||||
|
|
||||||
Qty:
|
|
||||||
1 pcs Dual Function Manual control and PC UART Control Relay ;
|
|
||||||
1 pcs RS232 to TTL;
|
|
||||||
4 pcs female to 4 pins female dupont wire;
|
|
||||||
|
|
||||||
Descripion :
|
|
||||||
|
|
||||||
12V 2 Channel Relay Module;
|
|
||||||
|
|
||||||
Dual Function : Manual control & PC RS232 Control Relay;
|
|
||||||
|
|
||||||
Input and output optical isolation;
|
|
||||||
|
|
||||||
With 5V TTL 232 interface, which is convenient to achieve USB to PC by USB to TTL232 serial to control relay, you can also use 8051 AVR Arduino MCU to control relay ;
|
|
||||||
|
|
||||||
Power indicator: LED lights ;
|
|
||||||
|
|
||||||
Output indication: relay output with LED indicators, easy to see working status of the relay ;
|
|
||||||
|
|
||||||
Communication protocol: UART protocol communication, baud rate 9600kpbs, 8 data bits, one stop bit, no parity. Each data frame contains eight bytes. Two-way data transmission.
|
|
||||||
|
|
||||||
|
|
||||||
Baud rate 9600kbps, 8 data bits, one stop bit, no parity. Each data frame contains eight bytes..
|
|
||||||
|
|
||||||
1 Control commands:
|
|
||||||
|
|
||||||
1. Reading status(reading the satus of the relay (on/off))
|
|
||||||
|
|
||||||
0x55 0x56 0x00 0x00 0x00 0x00 0x00 0xAB
|
|
||||||
|
|
||||||
|
|
||||||
2. Relay open (issue this command ,Relay open , COM connect to NO )
|
|
||||||
|
|
||||||
Channel 1 :0x55 0x56 0x00 0x00 0x00 0x01 0x01 0xAD
|
|
||||||
|
|
||||||
Channel 2 :0x55 0x56 0x00 0x00 0x00 0x02 0x01 0xAE
|
|
||||||
|
|
||||||
|
|
||||||
3. Relay close (issue this command ,Relay close , COM disconnect NO , and COM connect to NC )
|
|
||||||
|
|
||||||
Channel 1 :0x55 0x56 0x00 0x00 0x00 0x01 0x02 0xAE
|
|
||||||
|
|
||||||
Channel 2 :0x55 0x56 0x00 0x00 0x00 0x02 0x02 0xAF
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
4. Relay toggle(Relay status reversal,if COM connect to NO,this commands will Disconnect COM to NO and Reverse COM connect to NC,and vice versa)
|
|
||||||
|
|
||||||
Channel 1 :0x55 0x56 0x00 0x00 0x00 0x01 0x03 0xAF
|
|
||||||
|
|
||||||
Channel 2 :0x55 0x56 0x00 0x00 0x00 0x02 0x03 0xB0
|
|
||||||
|
|
||||||
|
|
||||||
5. Relay momentary(Relay COM connect to NO,disconnect after 200MS )
|
|
||||||
|
|
||||||
Channel 1 :0x55 0x56 0x00 0x00 0x00 0x01 0x04 0xB0
|
|
||||||
|
|
||||||
Channel 2 :0x55 0x56 0x00 0x00 0x00 0x02 0x04 0xB1
|
|
||||||
|
|
||||||
Once issue a command, will have a return fame , 7th byte of return fame mean the satus of realy .
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2 return command
|
|
||||||
|
|
||||||
|
|
||||||
1、Return relay open(return this command,mean COM connect to NO )
|
|
||||||
|
|
||||||
Channel 1 : 0x33 0x3C 0x00 0x00 0x00 0x01 0x01 0x71
|
|
||||||
|
|
||||||
Channel 2 : 0x33 0x3C 0x00 0x00 0x00 0x02 0x01 0x72
|
|
||||||
|
|
||||||
|
|
||||||
2、Return relay close(return this command,mean COM disconnect NO , and COM connect to NC )
|
|
||||||
|
|
||||||
Channel 1 : 0x33 0x3C 0x00 0x00 0x00 0x01 0x02 0x72
|
|
||||||
|
|
||||||
Channel 2 : 0x33 0x3C 0x00 0x00 0x00 0x02 0x02 0x73
|
|
Binary file not shown.
Before Width: | Height: | Size: 91 KiB |
Binary file not shown.
Before Width: | Height: | Size: 122 KiB |
Binary file not shown.
Before Width: | Height: | Size: 120 KiB |
|
@ -1,401 +0,0 @@
|
||||||
/*
|
|
||||||
* ampswitch
|
|
||||||
* Copyright (C) Bob 2014
|
|
||||||
*
|
|
||||||
* ampswitch is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* ampswitch is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ampswitch.h"
|
|
||||||
|
|
||||||
#ifndef _GNU_SOURCE
|
|
||||||
#define _GNU_SOURCE //for pipe2
|
|
||||||
#endif //_GNU_SOURCE
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
volatile bool g_stop = false;
|
|
||||||
|
|
||||||
CAmpSwitch::CAmpSwitch(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
m_triggerlevel = 0.1f;
|
|
||||||
m_switchtime = 10.0f;
|
|
||||||
m_oncommand = NULL;
|
|
||||||
m_offcommand = NULL;
|
|
||||||
m_pipe[0] = -1;
|
|
||||||
m_pipe[1] = -1;
|
|
||||||
m_connected = false;
|
|
||||||
m_jackname = "Ampswitch";
|
|
||||||
m_client = NULL;
|
|
||||||
m_port = NULL;
|
|
||||||
m_samplerate = 0;
|
|
||||||
m_jackshutdown = false;
|
|
||||||
m_switchedon = false;
|
|
||||||
m_samplecounter = 0;
|
|
||||||
m_usekodi = false;
|
|
||||||
m_playing = false;
|
|
||||||
|
|
||||||
struct option longoptions[] =
|
|
||||||
{
|
|
||||||
{"jack-name", required_argument, NULL, 'j'},
|
|
||||||
{"on-command", required_argument, NULL, 'n'},
|
|
||||||
{"off-command", required_argument, NULL, 'f'},
|
|
||||||
{"switch-time", required_argument, NULL, 's'},
|
|
||||||
{"trigger-level", required_argument, NULL, 't'},
|
|
||||||
{"kodi", no_argument, NULL, 'k'},
|
|
||||||
{"help", no_argument, NULL, 'h'},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* shortoptions = "j:n:f:s:t:kh";
|
|
||||||
int c;
|
|
||||||
int optionindex = 0;
|
|
||||||
while ((c = getopt_long(argc, argv, shortoptions, longoptions, &optionindex)) != -1)
|
|
||||||
{
|
|
||||||
if (c == 'j')
|
|
||||||
{
|
|
||||||
m_jackname = optarg;
|
|
||||||
}
|
|
||||||
else if (c == 'n')
|
|
||||||
{
|
|
||||||
m_oncommand = optarg;
|
|
||||||
}
|
|
||||||
else if (c == 'f')
|
|
||||||
{
|
|
||||||
m_offcommand = optarg;
|
|
||||||
}
|
|
||||||
else if (c == 'h')
|
|
||||||
{
|
|
||||||
PrintHelpMessage();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
else if (c == 's')
|
|
||||||
{
|
|
||||||
float switchtime;
|
|
||||||
if (sscanf(optarg, "%f", &switchtime) != 1)
|
|
||||||
{
|
|
||||||
printf("ERROR: Wrong value for switch-time: \"%s\"\n", optarg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_switchtime = switchtime;
|
|
||||||
}
|
|
||||||
else if (c == 't')
|
|
||||||
{
|
|
||||||
float triggerlevel;
|
|
||||||
if (sscanf(optarg, "%f", &triggerlevel) != 1)
|
|
||||||
{
|
|
||||||
printf("ERROR: Wrong value for trigger-level: \"%s\"\n", optarg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_triggerlevel = triggerlevel;
|
|
||||||
}
|
|
||||||
else if (c == 'k')
|
|
||||||
{
|
|
||||||
m_usekodi = true;
|
|
||||||
}
|
|
||||||
else if (c == '?')
|
|
||||||
{
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CAmpSwitch::~CAmpSwitch()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CAmpSwitch::Setup()
|
|
||||||
{
|
|
||||||
//install signal handlers for exiting
|
|
||||||
signal(SIGINT, SignalHandler);
|
|
||||||
signal(SIGTERM, SignalHandler);
|
|
||||||
|
|
||||||
//create a non blocking pipe which the jack thread will use to communicate with the main thread
|
|
||||||
if (pipe2(m_pipe, O_NONBLOCK) == -1)
|
|
||||||
{
|
|
||||||
printf("ERROR: Creating pipe: %s\n", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_oncommand)
|
|
||||||
printf("on command: \"%s\"\n", m_oncommand);
|
|
||||||
if (m_offcommand)
|
|
||||||
printf("off command: \"%s\"\n", m_offcommand);
|
|
||||||
|
|
||||||
if (m_usekodi)
|
|
||||||
m_kodiclient.Start(this); //start a thread that connects to Kodi's JSONRPC
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::Process()
|
|
||||||
{
|
|
||||||
//if the off command is passed on the command line, execute that first
|
|
||||||
if (m_offcommand)
|
|
||||||
{
|
|
||||||
printf("switching off, executing \"%s\"\n", m_offcommand);
|
|
||||||
system(m_offcommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
//local switch state
|
|
||||||
bool switchedon = false;
|
|
||||||
|
|
||||||
while (!g_stop)
|
|
||||||
{
|
|
||||||
//if the jack daemon has shut down, clean up the jack client
|
|
||||||
if (m_jackshutdown)
|
|
||||||
JackDisconnect();
|
|
||||||
|
|
||||||
//try to connect to jackd if not connected yet
|
|
||||||
if (!m_connected)
|
|
||||||
Connect();
|
|
||||||
|
|
||||||
//wait for one second on the readable end of the pipe
|
|
||||||
fd_set pipeset;
|
|
||||||
FD_ZERO(&pipeset);
|
|
||||||
FD_SET(m_pipe[0], &pipeset);
|
|
||||||
struct timeval tv;
|
|
||||||
tv.tv_sec = 1;
|
|
||||||
tv.tv_usec = 0;
|
|
||||||
select(m_pipe[0] + 1, &pipeset, NULL, NULL, &tv);
|
|
||||||
|
|
||||||
//clear the readable end of the pipe
|
|
||||||
uint8_t byte;
|
|
||||||
read(m_pipe[0], &byte, 1);
|
|
||||||
|
|
||||||
//if the jack thread has written a byte to the pipe, the switch state has changed
|
|
||||||
//execute the switch on or switch off command if necessary
|
|
||||||
if (switchedon != m_switchedon)
|
|
||||||
{
|
|
||||||
switchedon = m_switchedon;
|
|
||||||
if (switchedon && m_oncommand)
|
|
||||||
{
|
|
||||||
printf("switching on, executing \"%s\"\n", m_oncommand);
|
|
||||||
system(m_oncommand);
|
|
||||||
}
|
|
||||||
else if (!switchedon && m_offcommand)
|
|
||||||
{
|
|
||||||
printf("switching off, executing \"%s\"\n", m_offcommand);
|
|
||||||
system(m_offcommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if the off command is passed on the command line, execute that before exiting
|
|
||||||
if (m_offcommand)
|
|
||||||
{
|
|
||||||
printf("switching off, executing \"%s\"\n", m_offcommand);
|
|
||||||
system(m_offcommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::Cleanup()
|
|
||||||
{
|
|
||||||
JackDisconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::PrintHelpMessage()
|
|
||||||
{
|
|
||||||
printf(
|
|
||||||
"\n"
|
|
||||||
"usage: ampswitch [OPTION]\n"
|
|
||||||
"\n"
|
|
||||||
" options:\n"
|
|
||||||
"\n"
|
|
||||||
" -j, --jack-name name of the jack client\n"
|
|
||||||
" -n, --on-command command to execute when switching on\n"
|
|
||||||
" -f, --off-command command to execute when switching off\n"
|
|
||||||
" -s, --switch-time minimum number of seconds between switches\n"
|
|
||||||
" -t, --trigger-level absolute value of trigger level\n"
|
|
||||||
" -k, --kodi use Kodi's JSONRPC to switch on when playback starts\n"
|
|
||||||
" -h, --help print this message\n"
|
|
||||||
"\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::Connect()
|
|
||||||
{
|
|
||||||
m_connected = JackConnect();
|
|
||||||
if (!m_connected)
|
|
||||||
{
|
|
||||||
JackDisconnect();
|
|
||||||
|
|
||||||
printf("Waiting 10 seconds before trying again\n");
|
|
||||||
sleep(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CAmpSwitch::JackConnect()
|
|
||||||
{
|
|
||||||
//try to connect to jackd
|
|
||||||
m_client = jack_client_open(m_jackname, JackNoStartServer, NULL);
|
|
||||||
if (m_client == NULL)
|
|
||||||
{
|
|
||||||
printf("ERROR: Unable to connect to jack\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int returnv;
|
|
||||||
|
|
||||||
//get the sample rate for timing calculations
|
|
||||||
m_samplerate = jack_get_sample_rate(m_client);
|
|
||||||
|
|
||||||
//install a callback which gets called when jackd shuts down
|
|
||||||
jack_on_info_shutdown(m_client, SJackInfoShutdownCallback, this);
|
|
||||||
|
|
||||||
//install the process callback, this will be called when a new audio frame is passed
|
|
||||||
returnv = jack_set_process_callback(m_client, SJackProcessCallback, this);
|
|
||||||
if (returnv != 0)
|
|
||||||
{
|
|
||||||
printf("ERROR: Unable to set process callback\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//install a callback for when the sample rate changes
|
|
||||||
returnv = jack_set_sample_rate_callback(m_client, SJackSamplerateCallback, this);
|
|
||||||
if (returnv != 0)
|
|
||||||
printf("ERROR: Unable to set sample rate callback\n");
|
|
||||||
|
|
||||||
//register a jack audio port
|
|
||||||
m_port = jack_port_register(m_client, "Input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
|
||||||
if (m_port == NULL)
|
|
||||||
{
|
|
||||||
printf("ERROR: Unable to register jack port\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//activate the jack client
|
|
||||||
returnv = jack_activate(m_client);
|
|
||||||
if (returnv != 0)
|
|
||||||
{
|
|
||||||
printf("ERROR: Unable to activate jack client\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Connected to jack with name %s\n", m_jackname);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::JackDisconnect()
|
|
||||||
{
|
|
||||||
if (m_client)
|
|
||||||
{
|
|
||||||
printf("Disconnecting from jack\n");
|
|
||||||
|
|
||||||
jack_client_close(m_client);
|
|
||||||
m_port = NULL;
|
|
||||||
m_client = NULL;
|
|
||||||
}
|
|
||||||
m_jackshutdown = false;
|
|
||||||
m_connected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CAmpSwitch::SJackProcessCallback(jack_nframes_t nframes, void *arg)
|
|
||||||
{
|
|
||||||
return ((CAmpSwitch*)arg)->PJackProcessCallback(nframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CAmpSwitch::PJackProcessCallback(jack_nframes_t nframes)
|
|
||||||
{
|
|
||||||
float* jackptr = (float*)jack_port_get_buffer(m_port, nframes);
|
|
||||||
|
|
||||||
//iterate over all samples
|
|
||||||
float* in = jackptr;
|
|
||||||
float* inend = in + nframes;
|
|
||||||
while (in != inend)
|
|
||||||
{
|
|
||||||
//if the absolute sample value is higher than the trigger level, set the switch state to on and reset the sample counter
|
|
||||||
bool trigger = fabsf(*(in++)) > m_triggerlevel;
|
|
||||||
|
|
||||||
//Consider kodi playing as a trigger
|
|
||||||
if (m_playing)
|
|
||||||
trigger = true;
|
|
||||||
|
|
||||||
if (trigger)
|
|
||||||
{
|
|
||||||
m_samplecounter = std::max((int)lround(m_switchtime * m_samplerate), 1);
|
|
||||||
if (!m_switchedon)
|
|
||||||
{
|
|
||||||
m_switchedon = true;
|
|
||||||
uint8_t msgbyte = 0;
|
|
||||||
write(m_pipe[1], &msgbyte, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (m_samplecounter > 0)
|
|
||||||
{
|
|
||||||
//if the sample counter expires, set the switch state to off
|
|
||||||
m_samplecounter--;
|
|
||||||
if (m_samplecounter == 0)
|
|
||||||
{
|
|
||||||
m_switchedon = false;
|
|
||||||
uint8_t msgbyte = 0;
|
|
||||||
write(m_pipe[1], &msgbyte, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CAmpSwitch::SJackSamplerateCallback(jack_nframes_t nframes, void *arg)
|
|
||||||
{
|
|
||||||
return ((CAmpSwitch*)arg)->PJackSamplerateCallback(nframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CAmpSwitch::PJackSamplerateCallback(jack_nframes_t nframes)
|
|
||||||
{
|
|
||||||
//when the sample rate changes, update the sample counter so that it will represent the same amount of time
|
|
||||||
m_samplecounter = lround((double)m_samplecounter / (double)m_samplerate * (double)nframes);
|
|
||||||
m_samplerate = nframes;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::SJackInfoShutdownCallback(jack_status_t code, const char *reason, void *arg)
|
|
||||||
{
|
|
||||||
((CAmpSwitch*)arg)->PJackInfoShutdownCallback(code, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::PJackInfoShutdownCallback(jack_status_t code, const char *reason)
|
|
||||||
{
|
|
||||||
//signal the main thread that the jack server has shut down
|
|
||||||
m_jackshutdown = true;
|
|
||||||
uint8_t msgbyte = 0;
|
|
||||||
write(m_pipe[1], &msgbyte, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::SignalHandler(int signum)
|
|
||||||
{
|
|
||||||
//signal the main thread that the process should exit
|
|
||||||
if (signum == SIGINT || signum == SIGTERM)
|
|
||||||
g_stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmpSwitch::SetPlayingState(bool playing)
|
|
||||||
{
|
|
||||||
//Inform the switch thread about the play state of kodi
|
|
||||||
m_playing = playing;
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* ampswitch
|
|
||||||
* Copyright (C) Bob 2014
|
|
||||||
*
|
|
||||||
* ampswitch is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* ampswitch is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AMPSWITCH_H
|
|
||||||
#define AMPSWITCH_H
|
|
||||||
|
|
||||||
#include <jack/jack.h>
|
|
||||||
#include "kodiclient.h"
|
|
||||||
|
|
||||||
class CAmpSwitch
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CAmpSwitch(int argc, char *argv[]);
|
|
||||||
~CAmpSwitch();
|
|
||||||
|
|
||||||
bool Setup();
|
|
||||||
void Process();
|
|
||||||
void Cleanup();
|
|
||||||
|
|
||||||
void SetPlayingState(bool playing);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void PrintHelpMessage();
|
|
||||||
|
|
||||||
void Connect();
|
|
||||||
bool JackConnect();
|
|
||||||
void JackDisconnect();
|
|
||||||
|
|
||||||
static int SJackProcessCallback(jack_nframes_t nframes, void *arg);
|
|
||||||
int PJackProcessCallback(jack_nframes_t nframes);
|
|
||||||
|
|
||||||
static int SJackSamplerateCallback(jack_nframes_t nframes, void *arg);
|
|
||||||
int PJackSamplerateCallback(jack_nframes_t nframes);
|
|
||||||
|
|
||||||
static void SJackInfoShutdownCallback(jack_status_t code, const char *reason, void *arg);
|
|
||||||
void PJackInfoShutdownCallback(jack_status_t code, const char *reason);
|
|
||||||
|
|
||||||
static void SignalHandler(int signum);
|
|
||||||
|
|
||||||
float m_triggerlevel;
|
|
||||||
float m_switchtime;
|
|
||||||
char* m_oncommand;
|
|
||||||
char* m_offcommand;
|
|
||||||
|
|
||||||
int m_pipe[2];
|
|
||||||
|
|
||||||
bool m_connected;
|
|
||||||
const char* m_jackname;
|
|
||||||
jack_client_t* m_client;
|
|
||||||
jack_port_t* m_port;
|
|
||||||
int m_samplerate;
|
|
||||||
bool m_jackshutdown;
|
|
||||||
|
|
||||||
bool m_switchedon;
|
|
||||||
int m_samplecounter;
|
|
||||||
|
|
||||||
bool m_usekodi;
|
|
||||||
CKodiClient m_kodiclient;
|
|
||||||
bool m_playing;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AMPSWITCH_H
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* ampswitch
|
|
||||||
* Copyright (C) Bob 2021
|
|
||||||
*
|
|
||||||
* ampswitch is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* ampswitch is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "kodiclient.h"
|
|
||||||
#include "ampswitch.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using namespace boost::asio;
|
|
||||||
using ip::tcp;
|
|
||||||
using namespace nlohmann;
|
|
||||||
|
|
||||||
void CKodiClient::Start(CAmpSwitch* ampswitch)
|
|
||||||
{
|
|
||||||
m_ampswitch = ampswitch;
|
|
||||||
m_thread = std::thread(SProcess, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CKodiClient::SProcess(CKodiClient* kodiclient)
|
|
||||||
{
|
|
||||||
kodiclient->Process();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CKodiClient::Process()
|
|
||||||
{
|
|
||||||
printf("Kodi client started\n");
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//TODO: make "localhost" work instead of "127.0.0.1"
|
|
||||||
boost::asio::io_service io_service;
|
|
||||||
tcp::socket socket(io_service);
|
|
||||||
boost::asio::ip::address address = boost::asio::ip::make_address("127.0.0.1");
|
|
||||||
|
|
||||||
//Connect to Kodi's JSONRPC, this will thrown an exception on failure.
|
|
||||||
socket.connect(tcp::endpoint(address, 9090));
|
|
||||||
|
|
||||||
printf("Connected to Kodi\n");
|
|
||||||
|
|
||||||
//Read data from the TCP socket, and split into separate JSON objects.
|
|
||||||
ResetSplit();
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
boost::asio::streambuf receive_buffer;
|
|
||||||
size_t bytesread = boost::asio::read(socket, receive_buffer,
|
|
||||||
boost::asio::transfer_at_least(1));
|
|
||||||
|
|
||||||
const char* data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
|
|
||||||
|
|
||||||
Split(data, bytesread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(boost::system::system_error& error)
|
|
||||||
{
|
|
||||||
m_ampswitch->SetPlayingState(false);
|
|
||||||
printf("ERROR: unable to connect to Kodi JSONRPC: %s\n", error.what());
|
|
||||||
printf("Retrying in 10 seconds\n");
|
|
||||||
sleep(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CKodiClient::ResetSplit()
|
|
||||||
{
|
|
||||||
m_bracketlevel = 0;
|
|
||||||
m_instring = false;
|
|
||||||
m_escaped = false;
|
|
||||||
m_parsebuf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Splits JSON objects from Kodi's JSON RPC and passes them to Parse().*/
|
|
||||||
void CKodiClient::Split(const char* data, uint32_t len)
|
|
||||||
{
|
|
||||||
//Parse the JSON data into separate JSON objects by finding the text from
|
|
||||||
//the starting { to ending }, while ignoring curly brackets in strings.
|
|
||||||
for (uint32_t i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
m_parsebuf.push_back(data[i]);
|
|
||||||
|
|
||||||
if (!m_instring)
|
|
||||||
{
|
|
||||||
if (data[i] == '"')
|
|
||||||
{
|
|
||||||
m_instring = true;
|
|
||||||
}
|
|
||||||
else if (data[i] == '{')
|
|
||||||
{
|
|
||||||
m_bracketlevel++;
|
|
||||||
}
|
|
||||||
else if (data[i] == '}')
|
|
||||||
{
|
|
||||||
if (m_bracketlevel > 0)
|
|
||||||
{
|
|
||||||
m_bracketlevel--;
|
|
||||||
if (m_bracketlevel == 0)
|
|
||||||
{ //Parse the full received JSON object.
|
|
||||||
Parse(m_parsebuf);
|
|
||||||
m_parsebuf.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_parsebuf.clear(); //Shouldn't happen.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!m_escaped)
|
|
||||||
{
|
|
||||||
if (data[i] == '\\')
|
|
||||||
m_escaped = true;
|
|
||||||
else if (data[i] == '"')
|
|
||||||
m_instring = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_escaped = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CKodiClient::Parse(const std::string& jsonstr)
|
|
||||||
{
|
|
||||||
json jsondata = json::parse(jsonstr);
|
|
||||||
|
|
||||||
if (jsondata.contains("method"))
|
|
||||||
{
|
|
||||||
std::string method = jsondata["method"];
|
|
||||||
|
|
||||||
if (method == "Player.OnPlay")
|
|
||||||
{
|
|
||||||
printf("Player started\n");
|
|
||||||
m_ampswitch->SetPlayingState(true);
|
|
||||||
}
|
|
||||||
else if (method == "Player.OnStop")
|
|
||||||
{
|
|
||||||
printf("Player stopped\n");
|
|
||||||
m_ampswitch->SetPlayingState(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* ampswitch
|
|
||||||
* Copyright (C) Bob 2021
|
|
||||||
*
|
|
||||||
* ampswitch is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* ampswitch is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KODICLIENT_H
|
|
||||||
#define KODICLIENT_H
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class CAmpSwitch;
|
|
||||||
|
|
||||||
class CKodiClient
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void Start(CAmpSwitch* ampswitch);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void SProcess(CKodiClient* kodiclient);
|
|
||||||
void Process();
|
|
||||||
void ResetSplit();
|
|
||||||
void Split(const char* data, uint32_t len);
|
|
||||||
void Parse(const std::string& jsonstr);
|
|
||||||
|
|
||||||
std::thread m_thread;
|
|
||||||
CAmpSwitch* m_ampswitch;
|
|
||||||
|
|
||||||
uint32_t m_bracketlevel;
|
|
||||||
bool m_instring;
|
|
||||||
bool m_escaped;
|
|
||||||
std::string m_parsebuf;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //KODICLIENT_H
|
|
33
src/main.cpp
33
src/main.cpp
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* ampswitch
|
|
||||||
* Copyright (C) Bob 2014
|
|
||||||
*
|
|
||||||
* ampswitch is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* ampswitch is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ampswitch.h"
|
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
|
||||||
{
|
|
||||||
CAmpSwitch ampswitch(argc, argv);
|
|
||||||
|
|
||||||
if (!ampswitch.Setup())
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
ampswitch.Process();
|
|
||||||
ampswitch.Cleanup();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
177
waf
vendored
177
waf
vendored
File diff suppressed because one or more lines are too long
35
wscript
35
wscript
|
@ -1,35 +0,0 @@
|
||||||
#! /usr/bin/env python
|
|
||||||
# encoding: utf-8
|
|
||||||
|
|
||||||
# the following two variables are used by the target "waf dist"
|
|
||||||
VERSION='0.0.1'
|
|
||||||
APPNAME='ampswitch'
|
|
||||||
|
|
||||||
# these variables are mandatory ('/' are converted automatically)
|
|
||||||
top = '.'
|
|
||||||
out = 'build'
|
|
||||||
|
|
||||||
def options(opt):
|
|
||||||
opt.load('compiler_cxx')
|
|
||||||
|
|
||||||
def configure(conf):
|
|
||||||
conf.load('compiler_cxx')
|
|
||||||
|
|
||||||
conf.check(header_name='jack/jack.h')
|
|
||||||
conf.check(header_name='boost/asio.hpp')
|
|
||||||
conf.check(header_name='nlohmann/json.hpp')
|
|
||||||
|
|
||||||
conf.check(lib='jack', uselib_store='jack', mandatory=True)
|
|
||||||
conf.check(lib='pthread', uselib_store='pthread', mandatory=False)
|
|
||||||
|
|
||||||
conf.write_config_header('config.h')
|
|
||||||
|
|
||||||
def build(bld):
|
|
||||||
bld.program(source='src/main.cpp\
|
|
||||||
src/ampswitch.cpp\
|
|
||||||
src/kodiclient.cpp',
|
|
||||||
use=['jack', 'pthread'],
|
|
||||||
includes='./src',
|
|
||||||
cxxflags='-Wall -g',
|
|
||||||
target='ampswitch')
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue