From 085051cd407b4781f27f551941515ab8d84ac81e Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 11 Jan 2017 10:04:53 -0800 Subject: [PATCH 01/12] Update Makefile --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 0ae53948..be2aa07a 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ # equal to some value. Your mileage my vary. win := $(shell uname | grep CYGWIN) +ifeq ($(win),) + win := $(shell uname | grep MINGW) +endif dar := $(shell uname | grep Darwin) ifneq ($(win),) From 72cd5dbc26a848e5f2baa08a73f907e68a0e40e9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 11 Jan 2017 11:33:37 -0800 Subject: [PATCH 02/12] start changes --- audio.h | 3 ++- config.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/audio.h b/audio.h index 674c27ed..907519bf 100644 --- a/audio.h +++ b/audio.h @@ -30,7 +30,8 @@ enum ptt_method_e { PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ - PTT_METHOD_HAMLIB }; /* HAMLib, Linux only. */ + PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ + PTT_METHOD_AUDIO }; /* Audio channel. */ typedef enum ptt_method_e ptt_method_t; diff --git a/config.c b/config.c index 8090c859..8dee3e8b 100644 --- a/config.c +++ b/config.c @@ -1582,7 +1582,10 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname); #endif } - else { + else if (strcasecmp( t, "CHN") == 0) { + p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_AUDIO; + } + else { /* serial port case. */ From 3b59838237e3b9842f858f3050e53be43447f1fa Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 12 Jan 2017 17:12:53 -0800 Subject: [PATCH 03/12] add audio channel option for ptt (Windows only) --- Makefile.win | 2 +- audio.h | 10 +++ audio_ptt.c | 20 +++++ audio_ptt.h | 11 +++ audio_ptt_win.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ config.c | 50 ++++++++++++- direwolf.h | 8 ++ ptt.c | 60 ++++++++++++++- 8 files changed, 348 insertions(+), 6 deletions(-) create mode 100644 audio_ptt.c create mode 100644 audio_ptt.h create mode 100644 audio_ptt_win.c diff --git a/Makefile.win b/Makefile.win index 3a9d9f6f..524c7137 100644 --- a/Makefile.win +++ b/Makefile.win @@ -70,7 +70,7 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \ fcs_calc.o ax25_pad.o \ decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ - gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \ + gen_tone.o morse.o audio_win.o audio_ptt_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \ dwgps.o dwgpsnmea.o dtime_now.o \ diff --git a/audio.h b/audio.h index 907519bf..fdf4210e 100644 --- a/audio.h +++ b/audio.h @@ -196,6 +196,16 @@ struct audio_s { int ptt_invert; /* Invert the output. */ int ptt_invert2; /* Invert the secondary output. */ + int ptt_channel; /* Channel number for audio PTT. */ + int ptt_frequency; /* Audio frequency for audio PTT. */ +#if __WIN32__ + HANDLE ptt_start; /* Handle for event that starts ptt tone. */ + HANDLE ptt_stop; /* Handle for event that stops ptt tone. */ + HANDLE ptt_close; /* Handle for event that closes ptt. */ +#else + +#endif + #ifdef USE_HAMLIB int ptt_model; /* HAMLIB model. -1 for AUTO. 2 for rigctld. Others are radio model. */ diff --git a/audio_ptt.c b/audio_ptt.c new file mode 100644 index 00000000..9bfa2456 --- /dev/null +++ b/audio_ptt.c @@ -0,0 +1,20 @@ +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 . +// + + diff --git a/audio_ptt.h b/audio_ptt.h new file mode 100644 index 00000000..d880b5fc --- /dev/null +++ b/audio_ptt.h @@ -0,0 +1,11 @@ + +#ifndef AUDIO_PTT_H +#define AUDIO_PTT_H 1 + +#if __WIN32__ +extern HANDLE start_ptt_thread ( struct audio_s *pa, int ch ); +#else +extern int start_ptt_thread ( struct audio_s *pa, int ch ); +#endif + +#endif \ No newline at end of file diff --git a/audio_ptt_win.c b/audio_ptt_win.c new file mode 100644 index 00000000..dce3f66f --- /dev/null +++ b/audio_ptt_win.c @@ -0,0 +1,193 @@ + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 . +// + + +/*------------------------------------------------------------------ + * + * Module: audio_ptt_win.c + * + * Purpose: Interface to audio device commonly called a "sound card" for + * historical reasons. + * + * This version uses the native Windows sound interface. + * + *---------------------------------------------------------------*/ + +#if __WIN32__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef WAVE_FORMAT_96M16 +#define WAVE_FORMAT_96M16 0x40000 +#define WAVE_FORMAT_96S16 0x80000 +#endif + +#include "direwolf.h" +#include "audio.h" +#include "audio_stats.h" +#include "textcolor.h" +#include "ptt.h" +#include "demod.h" /* for alevel_t & demod_get_audio_level() */ +#include "audio_ptt.h" + +static struct audio_s *save_audio_config_p; + +static unsigned __stdcall ptt_thread ( void *arg ); + +HANDLE start_ptt_thread (struct audio_s *pa , int ch) +{ + save_audio_config_p = pa; + + return (HANDLE)_beginthreadex (NULL, 0, ptt_thread, (void*)(long)ch, 0, NULL); +} + +unsigned __stdcall ptt_thread (void *arg) +{ + WAVEFORMATEX wf; + HWAVEOUT hWaveOut; + int ch = (int)(long)arg; // channel number. + int channel = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel; + int freq = save_audio_config_p->achan[channel].octrl[OCTYPE_PTT].ptt_frequency; + int a = ACHAN2ADEV( channel ); + int err; + + if( save_audio_config_p->adev[a].defined ) { + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = save_audio_config_p->adev[a].num_channels; + wf.nSamplesPerSec = save_audio_config_p->adev[a].samples_per_sec; + wf.wBitsPerSample = save_audio_config_p->adev[a].bits_per_sample; + wf.nBlockAlign = ( wf.wBitsPerSample / 8 ) * wf.nChannels; + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.cbSize = 0; + + /* + * Open the audio output device. + * Soundcard is only possibility at this time. + */ + + err = waveOutOpen ( &hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL ); + if( err == MMSYSERR_NOERROR ) { + WAVEHDR waveHeader; + SHORT* pnData; + SHORT sample; + int nsamples = save_audio_config_p->adev[a].samples_per_sec / freq; + int i; + + if( save_audio_config_p->adev[a].num_channels == 1 ) { + waveHeader.dwBufferLength = 1 * nsamples * sizeof( SHORT ); + } + else { + waveHeader.dwBufferLength = 2 * nsamples * sizeof( SHORT ); + } + waveHeader.lpData = malloc( waveHeader.dwBufferLength ); + waveHeader.dwUser = 0; + waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; + waveHeader.dwLoops = 0xFFFF; + + pnData = (SHORT*)waveHeader.lpData; + + if( save_audio_config_p->adev[a].num_channels == 1 ) { + for( i = 0; i < nsamples; i++ ) { + sample = (SHORT)( 32000.0 * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); + pnData[i] = sample; + } + } + else { + for( i = 0; i < nsamples; i++ ) { + sample = (SHORT)( 32000.0 * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); + if( channel == ADEVFIRSTCHAN( a ) ) { + + // Stereo, left channel. + + pnData[i*2 + 0] = sample; + pnData[i*2 + 1] = 0; + } + else { + + // Stereo, right channel. + + pnData[i*2 + 0] = 0; + pnData[i*2 + 1] = sample; + } + } + } + + err = waveOutPrepareHeader ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + if( err == MMSYSERR_NOERROR ) { + HANDLE handles[3]; + DWORD dwWait; + + handles[0] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_start; + handles[1] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_stop; + handles[2] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_close; + + while( 1 ) + { + dwWait = WaitForMultipleObjects ( 3, handles, FALSE, INFINITE ); + + if( dwWait == WAIT_OBJECT_0 + 0 ) + { + // + // ptt_set on + // + + waveOutWrite ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + } + else if( dwWait == WAIT_OBJECT_0 + 1 ) + { + // + // ptt_set off + // + + waveOutReset ( hWaveOut ); + } + else if( dwWait == WAIT_OBJECT_0 + 2 ) + { + // + // close + // + + waveOutReset ( hWaveOut ); + waveOutUnprepareHeader ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + + break; + } + } + } + + waveOutClose ( hWaveOut ); + + free( waveHeader.lpData ); + } + } + + return 0; +} +#endif \ No newline at end of file diff --git a/config.c b/config.c index 8dee3e8b..0dbe29bf 100644 --- a/config.c +++ b/config.c @@ -666,7 +666,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_lpt_bit = 0; p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 0; - } + p_audio_config->achan[channel].octrl[ot].ptt_channel = 0; + p_audio_config->achan[channel].octrl[ot].ptt_frequency = PTT_FREQ_DEFAULT; + } p_audio_config->achan[channel].dwait = DEFAULT_DWAIT; p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; @@ -1582,8 +1584,52 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname); #endif } - else if (strcasecmp( t, "CHN") == 0) { + else if (strcasecmp( t, "CHANNEL") == 0) { + t = split(NULL, 0); + if (t == NULL) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ("Config file line %d: Missing channel number for %s.\n", line, otname); + continue; + } + + int channel_ptt = atoi(t); + + if (channel_ptt < 0 || channel_ptt >= MAX_CHANS) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ( "Config file line %d: Invalid PTT channel number for %s.\n", line, otname ); + continue; + } + + if (channel == channel_ptt) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ( "Config file line %d: PTT channel number must not be the same as the channel number itself.\n", line ); + continue; + } + + int freq_ptt = PTT_FREQ_DEFAULT; + + t = split(NULL, 0); + if (t != NULL) { + freq_ptt = atoi(t); + + if (freq_ptt < PTT_FREQ_MIN || freq_ptt > PTT_FREQ_MAX) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ("Config file line %d: Invalid value %d for PTT frequency. Using default of %d.\n", + line, freq_ptt, PTT_FREQ_DEFAULT ); + + freq_ptt = PTT_FREQ_DEFAULT; + } + } + p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_AUDIO; + p_audio_config->achan[channel].octrl[ot].ptt_channel = channel_ptt; + p_audio_config->achan[channel].octrl[ot].ptt_frequency = freq_ptt; +#ifdef __WIN32__ + p_audio_config->achan[channel].octrl[ot].ptt_start = CreateEvent (NULL, FALSE, FALSE, NULL); + p_audio_config->achan[channel].octrl[ot].ptt_stop = CreateEvent (NULL, FALSE, FALSE, NULL); + p_audio_config->achan[channel].octrl[ot].ptt_close = CreateEvent (NULL, FALSE, FALSE, NULL); +#else +#endif } else { diff --git a/direwolf.h b/direwolf.h index 958e0932..347f4eb2 100644 --- a/direwolf.h +++ b/direwolf.h @@ -38,6 +38,14 @@ #define MAX_RIGS MAX_CHANS #endif +/* + * PTT frequency settings + */ + +#define PTT_FREQ_MIN 50 +#define PTT_FREQ_MAX 20000 +#define PTT_FREQ_DEFAULT 1000 + /* * Get audio device number for given channel. * and first channel for given device. diff --git a/ptt.c b/ptt.c index 749ada61..73f122e5 100644 --- a/ptt.c +++ b/ptt.c @@ -126,6 +126,7 @@ typedef int HANDLE; #include "textcolor.h" #include "audio.h" #include "ptt.h" +#include "audio_ptt.h" #if __WIN32__ @@ -357,6 +358,13 @@ void ptt_init (struct audio_s *audio_config_p) { int ch; HANDLE fd = INVALID_HANDLE_VALUE; + +#if __WIN32__ + HANDLE audio_ptt_th[MAX_CHANS]; +#else + pthread_t audio_ptt_tid[MAX_CHANS]; +#endif + #if __WIN32__ #else int using_gpio; @@ -386,7 +394,7 @@ void ptt_init (struct audio_s *audio_config_p) if (ptt_debug_level >= 2) { text_color_set(DW_COLOR_DEBUG); - dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n", + dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d, channel=%d, freq=%d\n", ch, otnames[ot], audio_config_p->achan[ch].octrl[ot].ptt_method, @@ -394,7 +402,9 @@ void ptt_init (struct audio_s *audio_config_p) audio_config_p->achan[ch].octrl[ot].ptt_line, audio_config_p->achan[ch].octrl[ot].ptt_gpio, audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit, - audio_config_p->achan[ch].octrl[ot].ptt_invert); + audio_config_p->achan[ch].octrl[ot].ptt_invert, + audio_config_p->achan[ch].octrl[ot].ptt_channel, + audio_config_p->achan[ch].octrl[ot].ptt_frequency ); } } } @@ -501,6 +511,7 @@ void ptt_init (struct audio_s *audio_config_p) ptt_set (ot, ch, 0); } /* if serial method. */ + } /* for each output type. */ } /* if channel valid. */ } /* For each channel. */ @@ -747,6 +758,35 @@ void ptt_init (struct audio_s *audio_config_p) #endif + /* + * Set up audio channel. + */ + + for (ch = 0; chachan[ch].valid) { + if (audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_AUDIO) { +#if __WIN32__ + audio_ptt_th[ch] = start_ptt_thread (audio_config_p, ch); + if (audio_ptt_th[ch] == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not create audio_ptt thread on channel %d for PTT of channel %d.\n", + audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel, ch); + return; + } +#else + int e; + + e = pthread_create (&(ptt_tid[j]), NULL, ptt_thread, (void *)(long)ch); + if (e != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not create audio_ptt thread on channel %d for PTT of channel %d.\n", + audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel, ch ); + return; + } +#endif + } + } + } /* Why doesn't it transmit? Probably forgot to specify PTT option. */ @@ -968,7 +1008,21 @@ void ptt_set (int ot, int chan, int ptt_signal) } #endif - + if( save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_AUDIO ) { + if( ptt_signal ) { +#ifdef __WIN32__ + SetEvent( save_audio_config_p->achan[chan].octrl[ot].ptt_start ); +#else +#endif + } + else + { +#ifdef __WIN32__ + SetEvent( save_audio_config_p->achan[chan].octrl[ot].ptt_stop ); +#else +#endif + } + } } /* end ptt_set */ /*------------------------------------------------------------------- From aa5176e7b43cce014b3077ac7fab6f8f2d124825 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 12 Jan 2017 22:57:53 -0800 Subject: [PATCH 04/12] cleanup ptt audio thread when application closed --- audio_ptt_win.c | 5 +++-- config.c | 6 ++++++ ptt.c | 18 +++++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/audio_ptt_win.c b/audio_ptt_win.c index dce3f66f..2b044d7c 100644 --- a/audio_ptt_win.c +++ b/audio_ptt_win.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -115,13 +116,13 @@ unsigned __stdcall ptt_thread (void *arg) if( save_audio_config_p->adev[a].num_channels == 1 ) { for( i = 0; i < nsamples; i++ ) { - sample = (SHORT)( 32000.0 * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); + sample = (SHORT)( (double)SHRT_MAX * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); pnData[i] = sample; } } else { for( i = 0; i < nsamples; i++ ) { - sample = (SHORT)( 32000.0 * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); + sample = (SHORT)( (double)SHRT_MAX * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); if( channel == ADEVFIRSTCHAN( a ) ) { // Stereo, left channel. diff --git a/config.c b/config.c index 0dbe29bf..e8b828db 100644 --- a/config.c +++ b/config.c @@ -668,6 +668,12 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 0; p_audio_config->achan[channel].octrl[ot].ptt_channel = 0; p_audio_config->achan[channel].octrl[ot].ptt_frequency = PTT_FREQ_DEFAULT; +#ifdef __WIN32__ + p_audio_config->achan[channel].octrl[ot].ptt_start = NULL; + p_audio_config->achan[channel].octrl[ot].ptt_stop = NULL; + p_audio_config->achan[channel].octrl[ot].ptt_close = NULL; +#else +#endif } p_audio_config->achan[channel].dwait = DEFAULT_DWAIT; diff --git a/ptt.c b/ptt.c index 73f122e5..c948a0b0 100644 --- a/ptt.c +++ b/ptt.c @@ -1008,11 +1008,16 @@ void ptt_set (int ot, int chan, int ptt_signal) } #endif + /* + * Using audio channel? + */ + if( save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_AUDIO ) { if( ptt_signal ) { #ifdef __WIN32__ SetEvent( save_audio_config_p->achan[chan].octrl[ot].ptt_start ); #else + #endif } else @@ -1020,6 +1025,7 @@ void ptt_set (int ot, int chan, int ptt_signal) #ifdef __WIN32__ SetEvent( save_audio_config_p->achan[chan].octrl[ot].ptt_stop ); #else + #endif } } @@ -1110,7 +1116,7 @@ void ptt_term (void) ptt_set (ot, n, 0); } } - } + } for (n = 0; n < MAX_CHANS; n++) { if (save_audio_config_p->achan[n].valid) { @@ -1128,6 +1134,16 @@ void ptt_term (void) } } + for (n = 0; n < MAX_CHANS; n++) { + if (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_AUDIO) { +#ifdef __WIN32__ + SetEvent (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_close) ; +#else + +#endif + } + } + #ifdef USE_HAMLIB for (n = 0; n < MAX_CHANS; n++) { From 52bf2ff5bd78a475746c06b38bf860962959b299 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 13 Jan 2017 20:05:00 -0800 Subject: [PATCH 05/12] build under linux and start ALSA implementation --- Makefile.linux | 2 +- audio_ptt.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++ audio_ptt_win.c | 25 ++++---- ptt.c | 6 +- 4 files changed, 160 insertions(+), 20 deletions(-) diff --git a/Makefile.linux b/Makefile.linux index be9bd593..5960a297 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -232,7 +232,7 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \ fcs_calc.o ax25_pad.o \ decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ - gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ + gen_tone.o audio.o audio_ptt.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \ dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o \ diff --git a/audio_ptt.c b/audio_ptt.c index 9bfa2456..ae91e3fa 100644 --- a/audio_ptt.c +++ b/audio_ptt.c @@ -2,6 +2,7 @@ // This file is part of Dire Wolf, an amateur radio packet TNC. // // Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// Copyright (C) 2017 Andrew Walker, VA7YAA // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,3 +19,149 @@ // +/*------------------------------------------------------------------ + * + * Module: audio_ptt.c + * + * Purpose: Interface to audio device commonly called a "sound card" for + * historical reasons. + * + * This version uses the native Windows sound interface. + * + *---------------------------------------------------------------*/ + +#if __WIN32__ +#else +#include +#include +#include + +#if USE_ALSA +#include +#else +#include +#ifdef __OpenBSD__ +#include +#else +#include +#endif +#endif + +#include "direwolf.h" +#include "audio.h" +#include "audio_stats.h" +#include "textcolor.h" +#include "ptt.h" +#include "audio_ptt.h" + +#if USE_ALSA +static int set_alsa_params (int a, snd_pcm_t *handle, struct audio_s *pa, char *name, char *dir); +//static void alsa_select_device (char *pick_dev, int direction, char *result); +#else +static int set_oss_params (int a, int fd, struct audio_s *pa); +#endif + +static struct audio_s *save_audio_config_p; + +static void * ptt_thread (void *arg); + +int start_ptt_thread (struct audio_s *pa, int ch) +{ + pthread_t tid = 0; + int e; + + save_audio_config_p = pa; + + e = pthread_create (&tid, NULL, ptt_thread, (void*)(long)ch); + + return tid; +} + +static void * ptt_thread (void *arg) +{ + int ch = (int)(long)arg; // channel number. + int channel = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel; + int freq = save_audio_config_p->achan[channel].octrl[OCTYPE_PTT].ptt_frequency; + int a = ACHAN2ADEV( channel ); + + if( save_audio_config_p->adev[a].defined ) { +#if USE_ALSA + snd_pcm_t *handle; + int err; + + err = snd_pcm_open(&handle, save_audio_config_p->adev[a].adevice_out, SND_PCM_STREAM_PLAYBACK, 0); + if (err == 0) { + snd_pcm_sframes_t frames; + snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; + + err = snd_pcm_set_params(handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, + save_audio_config_p->adev[a].num_channels, + save_audio_config_p->adev[a].samples_per_sec, 1, 500000); + if (err == 0) { + short* pnData; + short sample; + int nSamples = save_audio_config_p->adev[a].samples_per_sec / 10; + int nBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof(short); + int i; + + pnData = (short*)malloc (nBufferLength); + + if (save_audio_config_p->adev[a].num_channels == 1) { + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)save_audio_config_p->adev[a].samples_per_sec ) * 2.0 * M_PI ) ); + pnData[i] = sample; + } + } + else { + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)save_audio_config_p->adev[a].samples_per_sec ) * 2.0 * M_PI ) ); + if (channel == ADEVFIRSTCHAN( a )) { + + // Stereo, left channel. + + pnData[i*2 + 0] = sample; + pnData[i*2 + 1] = 0; + } + else { + + // Stereo, right channel. + + pnData[i*2 + 0] = 0; + pnData[i*2 + 1] = sample; + } + } + } + + // + // ptt_set on + // + + for (i=0; i<50; i++) { + frames = snd_pcm_writei(handle, pnData, nSamples); + } + + // + // ptt_set off + // + + // + // close + // + + free (pnData); + } + + snd_pcm_close(handle); + } +#else + int oss_audio_device_fd; + + oss_audio_device_fd = open (save_audio_config_p->adev[a].adevice_out, O_WRONLY); + if (oss_audio_device_fd != -1) { + + } +#endif + } +} + +#endif diff --git a/audio_ptt_win.c b/audio_ptt_win.c index 2b044d7c..92c0c505 100644 --- a/audio_ptt_win.c +++ b/audio_ptt_win.c @@ -96,33 +96,28 @@ unsigned __stdcall ptt_thread (void *arg) err = waveOutOpen ( &hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL ); if( err == MMSYSERR_NOERROR ) { WAVEHDR waveHeader; - SHORT* pnData; - SHORT sample; - int nsamples = save_audio_config_p->adev[a].samples_per_sec / freq; + short* pnData; + short sample; + int nSamples = save_audio_config_p->adev[a].samples_per_sec / freq; int i; - if( save_audio_config_p->adev[a].num_channels == 1 ) { - waveHeader.dwBufferLength = 1 * nsamples * sizeof( SHORT ); - } - else { - waveHeader.dwBufferLength = 2 * nsamples * sizeof( SHORT ); - } + waveHeader.dwBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof( short ); waveHeader.lpData = malloc( waveHeader.dwBufferLength ); waveHeader.dwUser = 0; waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; waveHeader.dwLoops = 0xFFFF; - pnData = (SHORT*)waveHeader.lpData; + pnData = (short*)waveHeader.lpData; if( save_audio_config_p->adev[a].num_channels == 1 ) { - for( i = 0; i < nsamples; i++ ) { - sample = (SHORT)( (double)SHRT_MAX * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); + for( i = 0; i < nSamples; i++ ) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i / (double)nSamples ) * 2.0 * M_PI ) ); pnData[i] = sample; } } else { - for( i = 0; i < nsamples; i++ ) { - sample = (SHORT)( (double)SHRT_MAX * sin( ( (double)i / (double)nsamples ) * 2.0 * M_PI ) ); + for( i = 0; i < nSamples; i++ ) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i / (double)nSamples ) * 2.0 * M_PI ) ); if( channel == ADEVFIRSTCHAN( a ) ) { // Stereo, left channel. @@ -191,4 +186,4 @@ unsigned __stdcall ptt_thread (void *arg) return 0; } -#endif \ No newline at end of file +#endif diff --git a/ptt.c b/ptt.c index c948a0b0..1b2e34cc 100644 --- a/ptt.c +++ b/ptt.c @@ -774,10 +774,8 @@ void ptt_init (struct audio_s *audio_config_p) return; } #else - int e; - - e = pthread_create (&(ptt_tid[j]), NULL, ptt_thread, (void *)(long)ch); - if (e != 0) { + audio_ptt_tid[ch] = start_ptt_thread (audio_config_p, ch); + if (audio_ptt_tid[ch] == 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Could not create audio_ptt thread on channel %d for PTT of channel %d.\n", audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel, ch ); From 99e7095f17b864908f8156b6be49e71f6eb23d9b Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 15 Jan 2017 13:42:52 -0800 Subject: [PATCH 06/12] build under ALSA --- audio.h | 24 ++++-- audio_ptt.c | 86 ++++++++++---------- audio_ptt_win.c | 210 ++++++++++++++++++++++-------------------------- config.c | 4 + ptt.c | 30 ++++--- 5 files changed, 178 insertions(+), 176 deletions(-) diff --git a/audio.h b/audio.h index fdf4210e..6e2e93cf 100644 --- a/audio.h +++ b/audio.h @@ -30,11 +30,17 @@ enum ptt_method_e { PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ - PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ - PTT_METHOD_AUDIO }; /* Audio channel. */ + PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ + PTT_METHOD_AUDIO }; /* Audio channel. */ typedef enum ptt_method_e ptt_method_t; +enum ptt_audio_state_e { + PTT_AUDIO_STATE_STOP, + PTT_AUDIO_STATE_START, + PTT_AUDIO_STATE_CLOSE }; +typedef enum ptt_audio_state_e ptt_audio_state_t; + enum ptt_line_e { PTT_LINE_NONE = 0, PTT_LINE_RTS = 1, PTT_LINE_DTR = 2 }; // Important: 0 for neither. typedef enum ptt_line_e ptt_line_t; @@ -196,14 +202,16 @@ struct audio_s { int ptt_invert; /* Invert the output. */ int ptt_invert2; /* Invert the secondary output. */ - int ptt_channel; /* Channel number for audio PTT. */ - int ptt_frequency; /* Audio frequency for audio PTT. */ + int ptt_channel; /* Channel number for audio PTT. */ + int ptt_frequency; /* Audio frequency for audio PTT. */ #if __WIN32__ - HANDLE ptt_start; /* Handle for event that starts ptt tone. */ - HANDLE ptt_stop; /* Handle for event that stops ptt tone. */ - HANDLE ptt_close; /* Handle for event that closes ptt. */ + HANDLE ptt_start; /* Handle for event that starts ptt tone. */ + HANDLE ptt_stop; /* Handle for event that stops ptt tone. */ + HANDLE ptt_close; /* Handle for event that closes ptt. */ #else - + pthread_mutex_t ptt_mutex; /* Mutex for controlling ptt tone state. */ + pthread_cond_t ptt_condition; /* Condition for controlling ptt tone state. */ + ptt_audio_state_t ptt_state; /* State of ptt tone. */ #endif #ifdef USE_HAMLIB diff --git a/audio_ptt.c b/audio_ptt.c index ae91e3fa..19bc4fd0 100644 --- a/audio_ptt.c +++ b/audio_ptt.c @@ -91,67 +91,64 @@ static void * ptt_thread (void *arg) err = snd_pcm_open(&handle, save_audio_config_p->adev[a].adevice_out, SND_PCM_STREAM_PLAYBACK, 0); if (err == 0) { - snd_pcm_sframes_t frames; snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; err = snd_pcm_set_params(handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, save_audio_config_p->adev[a].num_channels, save_audio_config_p->adev[a].samples_per_sec, 1, 500000); if (err == 0) { - short* pnData; + short* pnData; short sample; - int nSamples = save_audio_config_p->adev[a].samples_per_sec / 10; + int nSamples = save_audio_config_p->adev[a].samples_per_sec / 5; int nBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof(short); int i; + int j; pnData = (short*)malloc (nBufferLength); - if (save_audio_config_p->adev[a].num_channels == 1) { - for (i = 0; i < nSamples; i++) { - sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)save_audio_config_p->adev[a].samples_per_sec ) * 2.0 * M_PI ) ); - pnData[i] = sample; + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)save_audio_config_p->adev[a].samples_per_sec ) * 2.0 * M_PI ) ); + + for (j = 0; j < save_audio_config_p->adev[a].num_channels; j++) { + if (channel == ADEVFIRSTCHAN( a ) + j) { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = sample; + } else { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = 0; + } } } - else { - for (i = 0; i < nSamples; i++) { - sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)save_audio_config_p->adev[a].samples_per_sec ) * 2.0 * M_PI ) ); - if (channel == ADEVFIRSTCHAN( a )) { - - // Stereo, left channel. - - pnData[i*2 + 0] = sample; - pnData[i*2 + 1] = 0; - } - else { - - // Stereo, right channel. - - pnData[i*2 + 0] = 0; - pnData[i*2 + 1] = sample; - } - } - } - - // - // ptt_set on - // - - for (i=0; i<50; i++) { - frames = snd_pcm_writei(handle, pnData, nSamples); - } - - // - // ptt_set off - // - - // - // close - // + + while (1) { + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_audio_state_t ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + + if (ptt_state == PTT_AUDIO_STATE_STOP) { + snd_pcm_drop (handle); + + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + pthread_cond_wait (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_condition, &save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + + if (ptt_state == PTT_AUDIO_STATE_START) { + snd_pcm_prepare (handle); + } + } + + if (ptt_state == PTT_AUDIO_STATE_START) { + snd_pcm_writei (handle, pnData, nSamples); + } + else if (ptt_state == PTT_AUDIO_STATE_CLOSE) { + snd_pcm_drop (handle); + + break; + } + } free (pnData); } - - snd_pcm_close(handle); + snd_pcm_close (handle); } #else int oss_audio_device_fd; @@ -165,3 +162,4 @@ static void * ptt_thread (void *arg) } #endif + diff --git a/audio_ptt_win.c b/audio_ptt_win.c index 92c0c505..e690ea41 100644 --- a/audio_ptt_win.c +++ b/audio_ptt_win.c @@ -64,126 +64,110 @@ static unsigned __stdcall ptt_thread ( void *arg ); HANDLE start_ptt_thread (struct audio_s *pa , int ch) { - save_audio_config_p = pa; + save_audio_config_p = pa; - return (HANDLE)_beginthreadex (NULL, 0, ptt_thread, (void*)(long)ch, 0, NULL); + return (HANDLE)_beginthreadex (NULL, 0, ptt_thread, (void*)(long)ch, 0, NULL); } unsigned __stdcall ptt_thread (void *arg) { - WAVEFORMATEX wf; - HWAVEOUT hWaveOut; - int ch = (int)(long)arg; // channel number. - int channel = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel; - int freq = save_audio_config_p->achan[channel].octrl[OCTYPE_PTT].ptt_frequency; - int a = ACHAN2ADEV( channel ); - int err; - - if( save_audio_config_p->adev[a].defined ) { - wf.wFormatTag = WAVE_FORMAT_PCM; - wf.nChannels = save_audio_config_p->adev[a].num_channels; - wf.nSamplesPerSec = save_audio_config_p->adev[a].samples_per_sec; - wf.wBitsPerSample = save_audio_config_p->adev[a].bits_per_sample; - wf.nBlockAlign = ( wf.wBitsPerSample / 8 ) * wf.nChannels; - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.cbSize = 0; - - /* - * Open the audio output device. - * Soundcard is only possibility at this time. - */ - - err = waveOutOpen ( &hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL ); - if( err == MMSYSERR_NOERROR ) { - WAVEHDR waveHeader; - short* pnData; - short sample; - int nSamples = save_audio_config_p->adev[a].samples_per_sec / freq; - int i; - - waveHeader.dwBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof( short ); - waveHeader.lpData = malloc( waveHeader.dwBufferLength ); - waveHeader.dwUser = 0; - waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; - waveHeader.dwLoops = 0xFFFF; - - pnData = (short*)waveHeader.lpData; - - if( save_audio_config_p->adev[a].num_channels == 1 ) { - for( i = 0; i < nSamples; i++ ) { - sample = (short)( (double)SHRT_MAX * sin( ( (double)i / (double)nSamples ) * 2.0 * M_PI ) ); - pnData[i] = sample; - } - } - else { - for( i = 0; i < nSamples; i++ ) { - sample = (short)( (double)SHRT_MAX * sin( ( (double)i / (double)nSamples ) * 2.0 * M_PI ) ); - if( channel == ADEVFIRSTCHAN( a ) ) { - - // Stereo, left channel. - - pnData[i*2 + 0] = sample; - pnData[i*2 + 1] = 0; - } - else { - - // Stereo, right channel. - - pnData[i*2 + 0] = 0; - pnData[i*2 + 1] = sample; - } - } - } - - err = waveOutPrepareHeader ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); - if( err == MMSYSERR_NOERROR ) { - HANDLE handles[3]; - DWORD dwWait; - - handles[0] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_start; - handles[1] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_stop; - handles[2] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_close; - - while( 1 ) - { - dwWait = WaitForMultipleObjects ( 3, handles, FALSE, INFINITE ); - - if( dwWait == WAIT_OBJECT_0 + 0 ) - { - // - // ptt_set on - // - - waveOutWrite ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); - } - else if( dwWait == WAIT_OBJECT_0 + 1 ) - { - // - // ptt_set off - // - - waveOutReset ( hWaveOut ); - } - else if( dwWait == WAIT_OBJECT_0 + 2 ) - { - // - // close - // - - waveOutReset ( hWaveOut ); - waveOutUnprepareHeader ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); - - break; - } - } - } - - waveOutClose ( hWaveOut ); - - free( waveHeader.lpData ); + WAVEFORMATEX wf; + HWAVEOUT hWaveOut; + int ch = (int)(long)arg; // channel number. + int channel = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel; + int freq = save_audio_config_p->achan[channel].octrl[OCTYPE_PTT].ptt_frequency; + int a = ACHAN2ADEV( channel ); + int err; + + if (save_audio_config_p->adev[a].defined) { + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = save_audio_config_p->adev[a].num_channels; + wf.nSamplesPerSec = save_audio_config_p->adev[a].samples_per_sec; + wf.wBitsPerSample = save_audio_config_p->adev[a].bits_per_sample; + wf.nBlockAlign = ( wf.wBitsPerSample / 8 ) * wf.nChannels; + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.cbSize = 0; + + /* + * Open the audio output device. + * Soundcard is only possibility at this time. + */ + + err = waveOutOpen ( &hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL ); + if (err == MMSYSERR_NOERROR) { + WAVEHDR waveHeader; + short* pnData; + short sample; + int nSamples = save_audio_config_p->adev[a].samples_per_sec / freq; + int i; + int j; + + waveHeader.dwBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof( short ); + waveHeader.lpData = malloc( waveHeader.dwBufferLength ); + waveHeader.dwUser = 0; + waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; + waveHeader.dwLoops = 0xFFFF; + + pnData = (short*)waveHeader.lpData; + + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i / (double)nSamples ) * 2.0 * M_PI ) ); + + for (j = 0; j < save_audio_config_p->adev[a].num_channels; j++) { + if (channel == ADEVFIRSTCHAN( a ) + j) { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = sample; + } else { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = 0; + } } + } + + err = waveOutPrepareHeader (hWaveOut, &waveHeader, sizeof( WAVEHDR )); + if (err == MMSYSERR_NOERROR) { + HANDLE handles[3]; + DWORD dwWait; + + handles[0] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_start; + handles[1] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_stop; + handles[2] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_close; + + while (1) + { + dwWait = WaitForMultipleObjects (3, handles, FALSE, INFINITE); + + if (dwWait == WAIT_OBJECT_0 + 0) { + // + // ptt_set on + // + + waveOutWrite ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + } + else if (dwWait == WAIT_OBJECT_0 + 1) { + // + // ptt_set off + // + + waveOutReset ( hWaveOut ); + } + else if( dwWait == WAIT_OBJECT_0 + 2 ) { + // + // close + // + + waveOutReset ( hWaveOut ); + waveOutUnprepareHeader ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + + break; + } + } + } + + waveOutClose ( hWaveOut ); + + free( waveHeader.lpData ); } + } - return 0; + return 0; } #endif diff --git a/config.c b/config.c index e8b828db..363d9de8 100644 --- a/config.c +++ b/config.c @@ -673,6 +673,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_stop = NULL; p_audio_config->achan[channel].octrl[ot].ptt_close = NULL; #else + p_audio_config->achan[channel].octrl[ot].ptt_state = PTT_AUDIO_STATE_STOP; #endif } @@ -1635,6 +1636,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_stop = CreateEvent (NULL, FALSE, FALSE, NULL); p_audio_config->achan[channel].octrl[ot].ptt_close = CreateEvent (NULL, FALSE, FALSE, NULL); #else + p_audio_config->achan[channel].octrl[ot].ptt_state = PTT_AUDIO_STATE_STOP; + pthread_mutex_init(&p_audio_config->achan[channel].octrl[ot].ptt_mutex, 0); + pthread_cond_init(&p_audio_config->achan[channel].octrl[ot].ptt_condition, 0); #endif } else { diff --git a/ptt.c b/ptt.c index 1b2e34cc..bec7a979 100644 --- a/ptt.c +++ b/ptt.c @@ -1010,22 +1010,27 @@ void ptt_set (int ot, int chan, int ptt_signal) * Using audio channel? */ - if( save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_AUDIO ) { - if( ptt_signal ) { + if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_AUDIO) { + if (ptt_signal) { #ifdef __WIN32__ - SetEvent( save_audio_config_p->achan[chan].octrl[ot].ptt_start ); + SetEvent (save_audio_config_p->achan[chan].octrl[ot].ptt_start); #else - + pthread_mutex_lock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); + save_audio_config_p->achan[chan].octrl[ot].ptt_state = PTT_AUDIO_STATE_START; + pthread_cond_signal (&save_audio_config_p->achan[chan].octrl[ot].ptt_condition); + pthread_mutex_unlock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); #endif - } - else - { + } + else + { #ifdef __WIN32__ - SetEvent( save_audio_config_p->achan[chan].octrl[ot].ptt_stop ); + SetEvent (save_audio_config_p->achan[chan].octrl[ot].ptt_stop); #else - + pthread_mutex_lock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); + save_audio_config_p->achan[chan].octrl[ot].ptt_state = PTT_AUDIO_STATE_STOP; + pthread_mutex_unlock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); #endif - } + } } } /* end ptt_set */ @@ -1137,7 +1142,10 @@ void ptt_term (void) #ifdef __WIN32__ SetEvent (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_close) ; #else - + pthread_mutex_lock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); + save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_state = PTT_AUDIO_STATE_CLOSE; + pthread_cond_signal (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_condition); + pthread_mutex_unlock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); #endif } } From 74638537573ed46a06de50013e40af23cfe39af9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 15 Jan 2017 20:37:10 -0800 Subject: [PATCH 07/12] build under OSS --- audio_ptt.c | 103 ++++++++++++++++++++++++++++++++++++++++++++---- audio_ptt_win.c | 6 +-- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/audio_ptt.c b/audio_ptt.c index 19bc4fd0..4c6a3bc3 100644 --- a/audio_ptt.c +++ b/audio_ptt.c @@ -35,6 +35,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #if USE_ALSA #include @@ -89,11 +96,11 @@ static void * ptt_thread (void *arg) snd_pcm_t *handle; int err; - err = snd_pcm_open(&handle, save_audio_config_p->adev[a].adevice_out, SND_PCM_STREAM_PLAYBACK, 0); + err = snd_pcm_open (&handle, save_audio_config_p->adev[a].adevice_out, SND_PCM_STREAM_PLAYBACK, 0); if (err == 0) { snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; - err = snd_pcm_set_params(handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, + err = snd_pcm_set_params (handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, save_audio_config_p->adev[a].num_channels, save_audio_config_p->adev[a].samples_per_sec, 1, 500000); if (err == 0) { @@ -136,8 +143,8 @@ static void * ptt_thread (void *arg) } } - if (ptt_state == PTT_AUDIO_STATE_START) { - snd_pcm_writei (handle, pnData, nSamples); + if (ptt_state == PTT_AUDIO_STATE_START) { + snd_pcm_writei (handle, pnData, nSamples); } else if (ptt_state == PTT_AUDIO_STATE_CLOSE) { snd_pcm_drop (handle); @@ -147,15 +154,97 @@ static void * ptt_thread (void *arg) } free (pnData); - } + } else { + dw_printf("Failed to configure ALSA device. PTT tone will not be enabled.\n"); + } snd_pcm_close (handle); - } + } else { + dw_printf("Failed to open ALSA device. PTT tone will not be enabled.\n"); + } #else int oss_audio_device_fd; oss_audio_device_fd = open (save_audio_config_p->adev[a].adevice_out, O_WRONLY); - if (oss_audio_device_fd != -1) { + if (oss_audio_device_fd >= 0) { + int devcaps; + int num_channels; + int samples_per_sec; + int bits_per_sample; + int err; + + num_channels = save_audio_config_p->adev[a].num_channels; + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_CHANNELS, &num_channels); + + if (err != -1) { + samples_per_sec = save_audio_config_p->adev[a].samples_per_sec; + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_SPEED, &samples_per_sec); + } + + if (err != -1) { + bits_per_sample = save_audio_config_p->adev[a].bits_per_sample; + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_SETFMT, &bits_per_sample); + } + + if (err != -1) { + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_GETCAPS, &devcaps); + } + + if (err != -1) { + short* pnData; + short sample; + int nBufferLength; + int nSamples; + int written; + int i; + int j; + + nSamples = samples_per_sec / 5; + nBufferLength = num_channels * nSamples * sizeof(short); + pnData = (short*)malloc (nBufferLength); + + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)samples_per_sec ) * 2.0 * M_PI ) ); + + for (j = 0; j < num_channels; j++) { + if (channel == ADEVFIRSTCHAN( a ) + j) { + pnData[i*num_channels + j] = sample; + } else { + pnData[i*num_channels + j] = 0; + } + } + } + + while (1) { + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_audio_state_t ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + + if (ptt_state == PTT_AUDIO_STATE_STOP) { + ioctl (oss_audio_device_fd, SNDCTL_DSP_RESET, NULL); + + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + pthread_cond_wait (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_condition, &save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + } + + if (ptt_state == PTT_AUDIO_STATE_START) { + written = write (oss_audio_device_fd, pnData, nBufferLength); + } + else if (ptt_state == PTT_AUDIO_STATE_CLOSE) { + ioctl (oss_audio_device_fd, SNDCTL_DSP_RESET, NULL); + + break; + } + } + free (pnData); + } else { + dw_printf("Failed to configure OSS device. PTT tone will not be enabled.\n"); + } + close (oss_audio_device_fd); + } else { + dw_printf("Failed to open OSS device. PTT tone will not be enabled.\n"); } #endif } diff --git a/audio_ptt_win.c b/audio_ptt_win.c index e690ea41..8fcdc332 100644 --- a/audio_ptt_win.c +++ b/audio_ptt_win.c @@ -149,13 +149,13 @@ unsigned __stdcall ptt_thread (void *arg) waveOutReset ( hWaveOut ); } - else if( dwWait == WAIT_OBJECT_0 + 2 ) { + else if (dwWait == WAIT_OBJECT_0 + 2) { // // close // - waveOutReset ( hWaveOut ); - waveOutUnprepareHeader ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + waveOutReset (hWaveOut); + waveOutUnprepareHeader (hWaveOut, &waveHeader, sizeof(WAVEHDR)); break; } From 7685502c9bddfd8601ea7dea6e2a2aee5e59a033 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 17 Jan 2017 13:18:14 -0800 Subject: [PATCH 08/12] reduce phase shiift between subsequent tone buffers --- audio_ptt.c | 4 ++-- config.c | 2 +- ptt.c | 14 ++++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/audio_ptt.c b/audio_ptt.c index 4c6a3bc3..5188ac9a 100644 --- a/audio_ptt.c +++ b/audio_ptt.c @@ -106,7 +106,7 @@ static void * ptt_thread (void *arg) if (err == 0) { short* pnData; short sample; - int nSamples = save_audio_config_p->adev[a].samples_per_sec / 5; + int nSamples = (int)( ( (double)save_audio_config_p->adev[a].samples_per_sec / (double)freq ) * ceil( (double)freq / 5.0 ) ); int nBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof(short); int i; int j; @@ -198,7 +198,7 @@ static void * ptt_thread (void *arg) int i; int j; - nSamples = samples_per_sec / 5; + nSamples = (int)( ( (double)samples_per_sec / (double)freq ) * ceil( (double)freq / 5.0 ) ); nBufferLength = num_channels * nSamples * sizeof(short); pnData = (short*)malloc (nBufferLength); diff --git a/config.c b/config.c index 363d9de8..166c4380 100644 --- a/config.c +++ b/config.c @@ -1591,7 +1591,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname); #endif } - else if (strcasecmp( t, "CHANNEL") == 0) { + else if (strcasecmp(t, "CHANNEL") == 0) { t = split(NULL, 0); if (t == NULL) { text_color_set( DW_COLOR_ERROR ); diff --git a/ptt.c b/ptt.c index bec7a979..5ccd71bc 100644 --- a/ptt.c +++ b/ptt.c @@ -1138,15 +1138,17 @@ void ptt_term (void) } for (n = 0; n < MAX_CHANS; n++) { - if (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_AUDIO) { + if (save_audio_config_p->achan[n].valid) { + if (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_AUDIO) { #ifdef __WIN32__ - SetEvent (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_close) ; + SetEvent (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_close) ; #else - pthread_mutex_lock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); - save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_state = PTT_AUDIO_STATE_CLOSE; - pthread_cond_signal (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_condition); - pthread_mutex_unlock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); + pthread_mutex_lock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); + save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_state = PTT_AUDIO_STATE_CLOSE; + pthread_cond_signal (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_condition); + pthread_mutex_unlock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); #endif + } } } From c58720a89f33526137069eb4e81657f9faeaa769 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 17 Jan 2017 14:21:23 -0800 Subject: [PATCH 09/12] formatiing changes --- audio.c | 2 +- audio_ptt_win.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/audio.c b/audio.c index 84aa854a..823aaf99 100644 --- a/audio.c +++ b/audio.c @@ -692,7 +692,6 @@ static int set_oss_params (int a, int fd, struct audio_s *pa) char message[100]; int ossbuf_size_in_bytes; - err = ioctl (fd, SNDCTL_DSP_CHANNELS, &(pa->adev[a].num_channels)); if (err == -1) { text_color_set(DW_COLOR_ERROR); @@ -1249,6 +1248,7 @@ int audio_flush (int a) } if (k < len) { /* presumably full but didn't block. */ + dw_printf("problem\n"); usleep (10000); } ptr += k; diff --git a/audio_ptt_win.c b/audio_ptt_win.c index 8fcdc332..7ccdd378 100644 --- a/audio_ptt_win.c +++ b/audio_ptt_win.c @@ -93,7 +93,7 @@ unsigned __stdcall ptt_thread (void *arg) * Soundcard is only possibility at this time. */ - err = waveOutOpen ( &hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL ); + err = waveOutOpen (&hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL); if (err == MMSYSERR_NOERROR) { WAVEHDR waveHeader; short* pnData; @@ -103,7 +103,7 @@ unsigned __stdcall ptt_thread (void *arg) int j; waveHeader.dwBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof( short ); - waveHeader.lpData = malloc( waveHeader.dwBufferLength ); + waveHeader.lpData = malloc (waveHeader.dwBufferLength); waveHeader.dwUser = 0; waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; waveHeader.dwLoops = 0xFFFF; @@ -140,31 +140,31 @@ unsigned __stdcall ptt_thread (void *arg) // ptt_set on // - waveOutWrite ( hWaveOut, &waveHeader, sizeof( WAVEHDR ) ); + waveOutWrite (hWaveOut, &waveHeader, sizeof( WAVEHDR )); } else if (dwWait == WAIT_OBJECT_0 + 1) { // // ptt_set off // - waveOutReset ( hWaveOut ); + waveOutReset (hWaveOut); } - else if (dwWait == WAIT_OBJECT_0 + 2) { + else if( dwWait == WAIT_OBJECT_0 + 2 ) { // // close // waveOutReset (hWaveOut); - waveOutUnprepareHeader (hWaveOut, &waveHeader, sizeof(WAVEHDR)); + waveOutUnprepareHeader (hWaveOut, &waveHeader, sizeof( WAVEHDR )); break; } } } - waveOutClose ( hWaveOut ); + waveOutClose (hWaveOut); - free( waveHeader.lpData ); + free (waveHeader.lpData); } } From b561ec0849f15f4810b9cf9c4562f4a05d7031c7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 18 Jan 2017 11:26:45 -0800 Subject: [PATCH 10/12] all blank changes --- Makefile.linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.linux b/Makefile.linux index 5960a297..70a23ce0 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -197,7 +197,7 @@ endif # If you want to use OSS (for FreeBSD, OpenBSD) instead of # ALSA (for Linux), comment out (or remove) the two lines below. -CFLAGS += -DUSE_ALSA +# CFLAGS += -DUSE_ALSA LDFLAGS += -lasound From 762799aaad04e42a9c2b7a8b9bdff140030b47b8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 18 Jan 2017 11:27:08 -0800 Subject: [PATCH 11/12] Revert "all blank changes" This reverts commit b561ec0849f15f4810b9cf9c4562f4a05d7031c7. --- Makefile.linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.linux b/Makefile.linux index 70a23ce0..5960a297 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -197,7 +197,7 @@ endif # If you want to use OSS (for FreeBSD, OpenBSD) instead of # ALSA (for Linux), comment out (or remove) the two lines below. -# CFLAGS += -DUSE_ALSA +CFLAGS += -DUSE_ALSA LDFLAGS += -lasound From 910ca29fed9df01c97a11b5e0e33b1e869266e31 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 20 Jan 2017 10:56:57 -0800 Subject: [PATCH 12/12] update direwolf.txt with descption of audio PTT usage --- direwolf.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/direwolf.txt b/direwolf.txt index c8577395..e45d9c17 100644 --- a/direwolf.txt +++ b/direwolf.txt @@ -234,10 +234,16 @@ C# Both can be used for interfaces that want them driven with opposite polarity. C# L# COM1 can be used instead of /dev/ttyS0, COM2 for /dev/ttyS1, and so on. L# +C# With a suitable interface circuit an audio channel can also be used to +C# trigger the PTT control. The device should be specified as CHANNEL, +C# followed by the channel number and (optionally) the desired frequency, +C# in Hz, of the audio tone to be used. The frequency must be between +C# 50 and 20000 Hz and defaults to 1000 Hz. C C#PTT COM1 RTS C#PTT COM1 RTS -DTR L#PTT /dev/ttyUSB0 RTS +C#PTT CHANNEL 1 1000 C L# L# On Linux, you can also use general purpose I/O pins if