From 3b59838237e3b9842f858f3050e53be43447f1fa Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 12 Jan 2017 17:12:53 -0800 Subject: [PATCH] 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 3a9d9f6..524c713 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 907519b..fdf4210 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 0000000..9bfa245 --- /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 0000000..d880b5f --- /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 0000000..dce3f66 --- /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 8dee3e8..0dbe29b 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 958e093..347f4eb 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 749ada6..73f122e 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 */ /*-------------------------------------------------------------------