@@ -133,8 +133,8 @@ static int calcbufsize(int rate, int chans, int bits)
133
133
134
134
static struct adev_s {
135
135
136
- enum audio_in_type_e g_audio_in_type ;
137
-
136
+ enum audio_in_type_e g_audio_in_type ;
137
+ enum audio_out_type_e g_audio_out_type ;
138
138
/*
139
139
* UDP socket for receiving audio stream.
140
140
* Buffer, length, and pointer for UDP or stdin.
@@ -146,6 +146,12 @@ static struct adev_s {
146
146
int stream_len ;
147
147
int stream_next ;
148
148
149
+ /*
150
+ * Buffer and index for stdout.
151
+ */
152
+
153
+ unsigned char stream_out_data [SDR_UDP_BUF_MAXLEN ];
154
+ int stream_out_next ;
149
155
150
156
/* For sound output. */
151
157
/* out_wavehdr.dwUser is used to keep track of output buffer state. */
@@ -343,28 +349,36 @@ int audio_open (struct audio_s *pa)
343
349
344
350
/*
345
351
* Select output device.
346
- * Only soundcard at this point.
352
+ * Only soundcard and stdout at this point.
347
353
* Purhaps we'd like to add UDP for an SDR transmitter.
348
354
*/
349
- if (strlen (pa -> adev [a ].adevice_out ) == 1 && isdigit (pa -> adev [a ].adevice_out [0 ])) {
350
- out_dev_no [a ] = atoi (pa -> adev [a ].adevice_out );
351
- }
352
- else if (strlen (pa -> adev [a ].adevice_out ) == 2 && isdigit (pa -> adev [a ].adevice_out [0 ]) && isdigit (pa -> adev [a ].adevice_out [1 ])) {
353
- out_dev_no [a ] = atoi (pa -> adev [a ].adevice_out );
354
- }
355
+ if (strcasecmp (pa -> adev [a ].adevice_out , "stdout" ) == 0 || strcmp (pa -> adev [a ].adevice_out , "-" ) == 0 ) {
356
+ A -> g_audio_out_type = AUDIO_OUT_TYPE_STDOUT ;
357
+ /* Change - to stdout for readability. */
358
+ strlcpy (pa -> adev [a ].adevice_out , "stdout" , sizeof (pa -> adev [a ].adevice_out ));
359
+ } else {
360
+ A -> g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD ;
361
+
362
+ if (strlen (pa -> adev [a ].adevice_out ) == 1 && isdigit (pa -> adev [a ].adevice_out [0 ])) {
363
+ out_dev_no [a ] = atoi (pa -> adev [a ].adevice_out );
364
+ }
365
+ else if (strlen (pa -> adev [a ].adevice_out ) == 2 && isdigit (pa -> adev [a ].adevice_out [0 ]) && isdigit (pa -> adev [a ].adevice_out [1 ])) {
366
+ out_dev_no [a ] = atoi (pa -> adev [a ].adevice_out );
367
+ }
355
368
356
- if ((UINT )(out_dev_no [a ]) == WAVE_MAPPER && strlen (pa -> adev [a ].adevice_out ) >= 1 ) {
357
- num_devices = waveOutGetNumDevs ();
358
- for (n = 0 ; n < num_devices && (UINT )(out_dev_no [a ]) == WAVE_MAPPER ; n ++ ) {
359
- if ( ! waveOutGetDevCaps (n , & woc , sizeof (WAVEOUTCAPS ))) {
360
- if (strstr (woc .szPname , pa -> adev [a ].adevice_out ) != NULL ) {
361
- out_dev_no [a ] = n ;
369
+ if ((UINT )(out_dev_no [a ]) == WAVE_MAPPER && strlen (pa -> adev [a ].adevice_out ) >= 1 ) {
370
+ num_devices = waveOutGetNumDevs ();
371
+ for (n = 0 ; n < num_devices && (UINT )(out_dev_no [a ]) == WAVE_MAPPER ; n ++ ) {
372
+ if ( ! waveOutGetDevCaps (n , & woc , sizeof (WAVEOUTCAPS ))) {
373
+ if (strstr (woc .szPname , pa -> adev [a ].adevice_out ) != NULL ) {
374
+ out_dev_no [a ] = n ;
375
+ }
362
376
}
363
377
}
364
- }
365
- if (( UINT )( out_dev_no [ a ]) == WAVE_MAPPER ) {
366
- text_color_set ( DW_COLOR_ERROR );
367
- dw_printf ( "\"%s\" doesn't match any of the output devices.\n" , pa -> adev [ a ]. adevice_out );
378
+ if (( UINT )( out_dev_no [ a ]) == WAVE_MAPPER ) {
379
+ text_color_set ( DW_COLOR_ERROR );
380
+ dw_printf ( "\"%s\" doesn't match any of the output devices.\n" , pa -> adev [ a ]. adevice_out );
381
+ }
368
382
}
369
383
}
370
384
} /* if defined */
@@ -424,7 +438,7 @@ int audio_open (struct audio_s *pa)
424
438
425
439
struct adev_s * A = & (adev [a ]);
426
440
427
- /* Display stdin or udp:port if appropriate. */
441
+ /* Display stdin or udp:port if appropriate. */
428
442
429
443
if (A -> g_audio_in_type != AUDIO_IN_TYPE_SOUNDCARD ) {
430
444
@@ -498,6 +512,36 @@ int audio_open (struct audio_s *pa)
498
512
}
499
513
}
500
514
515
+ // Add UDP or stdout to end of device list if used.
516
+
517
+ for (a = 0 ; a < MAX_ADEVS ; a ++ ) {
518
+ if (pa -> adev [a ].defined ) {
519
+
520
+ struct adev_s * A = & (adev [a ]);
521
+
522
+ /* Display stdin or udp:port if appropriate. */
523
+
524
+ if (A -> g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD ) {
525
+
526
+ int aaa ;
527
+ for (aaa = 0 ; aaa < MAX_ADEVS ; aaa ++ ) {
528
+ if (pa -> adev [aaa ].defined ) {
529
+ dw_printf (" %c" , a == aaa ? '*' : ' ' );
530
+
531
+ }
532
+ }
533
+ dw_printf (" %s " , pa -> adev [a ].adevice_out ); /* should be UDP:nnnn or stdout */
534
+
535
+ if (pa -> adev [a ].num_channels == 2 ) {
536
+ dw_printf (" (channels %d & %d)" , ADEVFIRSTCHAN (a ), ADEVFIRSTCHAN (a )+ 1 );
537
+ }
538
+ else {
539
+ dw_printf (" (channel %d)" , ADEVFIRSTCHAN (a ));
540
+ }
541
+ dw_printf ("\n" );
542
+ }
543
+ }
544
+ }
501
545
502
546
/*
503
547
* Open for each audio device input/output pair.
@@ -523,32 +567,47 @@ int audio_open (struct audio_s *pa)
523
567
524
568
/*
525
569
* Open the audio output device.
526
- * Soundcard is only possibility at this time.
570
+ * Soundcard and stdout are only possibility at this time.
527
571
*/
528
572
529
- err = waveOutOpen (& (A -> audio_out_handle ), out_dev_no [a ], & wf , (DWORD_PTR )out_callback , a , CALLBACK_FUNCTION );
530
- if (err != MMSYSERR_NOERROR ) {
531
- text_color_set (DW_COLOR_ERROR );
532
- dw_printf ("Could not open audio device for output.\n" );
533
- return (-1 );
534
- }
535
-
573
+ switch (A -> g_audio_out_type ) {
574
+
575
+ case AUDIO_OUT_TYPE_SOUNDCARD :
536
576
577
+ err = waveOutOpen (& (A -> audio_out_handle ), out_dev_no [a ], & wf , (DWORD_PTR )out_callback , a , CALLBACK_FUNCTION );
578
+ if (err != MMSYSERR_NOERROR ) {
579
+ text_color_set (DW_COLOR_ERROR );
580
+ dw_printf ("Could not open audio device for output.\n" );
581
+ return (-1 );
582
+ }
583
+ break ;
537
584
/*
538
585
* Set up the output buffers.
539
586
* We use dwUser to indicate it is available for filling.
540
587
*/
541
588
542
- memset ((void * )(A -> out_wavehdr ), 0 , sizeof (A -> out_wavehdr ));
589
+ memset ((void * )(A -> out_wavehdr ), 0 , sizeof (A -> out_wavehdr ));
543
590
544
- for (n = 0 ; n < NUM_OUT_BUF ; n ++ ) {
545
- A -> out_wavehdr [n ].lpData = malloc (A -> outbuf_size );
546
- A -> out_wavehdr [n ].dwUser = DWU_FILLING ;
547
- A -> out_wavehdr [n ].dwBufferLength = 0 ;
548
- }
549
- A -> out_current = 0 ;
591
+ for (n = 0 ; n < NUM_OUT_BUF ; n ++ ) {
592
+ A -> out_wavehdr [n ].lpData = malloc (A -> outbuf_size );
593
+ A -> out_wavehdr [n ].dwUser = DWU_FILLING ;
594
+ A -> out_wavehdr [n ].dwBufferLength = 0 ;
595
+ }
596
+ A -> out_current = 0 ;
597
+
598
+ case AUDIO_OUT_TYPE_STDOUT :
599
+
600
+ setmode (STDOUT_FILENO , _O_BINARY );
601
+ A -> stream_out_next = 0 ;
602
+ break ;
603
+
604
+ default :
605
+
606
+ text_color_set (DW_COLOR_ERROR );
607
+ dw_printf ("Internal error, invalid audio_out_type\n" );
608
+ return (-1 );
609
+ }
550
610
551
-
552
611
/*
553
612
* Open audio input device.
554
613
* More possibilities here: soundcard, UDP port, stdin.
@@ -942,49 +1001,67 @@ int audio_put (int a, int c)
942
1001
943
1002
struct adev_s * A ;
944
1003
A = & (adev [a ]);
945
-
1004
+
1005
+ switch (A -> g_audio_out_type ) {
1006
+
1007
+ case AUDIO_OUT_TYPE_SOUNDCARD :
1008
+
946
1009
/*
947
1010
* Wait if no buffers are available.
948
1011
* Don't use p yet because compiler might might consider dwFlags a loop invariant.
949
1012
*/
950
1013
951
- int timeout = 10 ;
952
- while ( A -> out_wavehdr [A -> out_current ].dwUser == DWU_PLAYING ) {
953
- SLEEP_MS (ONE_BUF_TIME );
954
- timeout -- ;
955
- if (timeout <= 0 ) {
956
- text_color_set (DW_COLOR_ERROR );
1014
+ int timeout = 10 ;
1015
+ while ( A -> out_wavehdr [A -> out_current ].dwUser == DWU_PLAYING ) {
1016
+ SLEEP_MS (ONE_BUF_TIME );
1017
+ timeout -- ;
1018
+ if (timeout <= 0 ) {
1019
+ text_color_set (DW_COLOR_ERROR );
957
1020
958
1021
// TODO: open issues 78 & 165. How can we avoid/improve this?
959
1022
960
- dw_printf ("Audio output failure waiting for buffer.\n" );
961
- dw_printf ("This can occur when we are producing audio output for\n" );
962
- dw_printf ("transmit and the operating system doesn't provide buffer\n" );
963
- dw_printf ("space after waiting and retrying many times.\n" );
964
- //dw_printf ("In recent years, this has been reported only when running the\n");
965
- //dw_printf ("Windows version with VMWare on a Macintosh.\n");
966
- ptt_term ();
967
- return (-1 );
968
- }
969
- }
1023
+ dw_printf ("Audio output failure waiting for buffer.\n" );
1024
+ dw_printf ("This can occur when we are producing audio output for\n" );
1025
+ dw_printf ("transmit and the operating system doesn't provide buffer\n" );
1026
+ dw_printf ("space after waiting and retrying many times.\n" );
1027
+ //dw_printf ("In recent years, this has been reported only when running the\n");
1028
+ //dw_printf ("Windows version with VMWare on a Macintosh.\n");
1029
+ ptt_term ();
1030
+ return (-1 );
1031
+ }
1032
+ }
970
1033
971
- p = (LPWAVEHDR )(& (A -> out_wavehdr [A -> out_current ]));
1034
+ p = (LPWAVEHDR )(& (A -> out_wavehdr [A -> out_current ]));
972
1035
973
- if (p -> dwUser == DWU_DONE ) {
974
- waveOutUnprepareHeader (A -> audio_out_handle , p , sizeof (WAVEHDR ));
975
- p -> dwBufferLength = 0 ;
976
- p -> dwUser = DWU_FILLING ;
977
- }
1036
+ if (p -> dwUser == DWU_DONE ) {
1037
+ waveOutUnprepareHeader (A -> audio_out_handle , p , sizeof (WAVEHDR ));
1038
+ p -> dwBufferLength = 0 ;
1039
+ p -> dwUser = DWU_FILLING ;
1040
+ }
1041
+
1042
+ /* Should never be full at this point. */
978
1043
979
- /* Should never be full at this point. */
1044
+ assert (p -> dwBufferLength >= 0 );
1045
+ assert (p -> dwBufferLength < (DWORD )(A -> outbuf_size ));
980
1046
981
- assert (p -> dwBufferLength >= 0 );
982
- assert (p -> dwBufferLength < (DWORD )(A -> outbuf_size ));
1047
+ p -> lpData [p -> dwBufferLength ++ ] = c ;
983
1048
984
- p -> lpData [p -> dwBufferLength ++ ] = c ;
1049
+ if (p -> dwBufferLength == (DWORD )(A -> outbuf_size )) {
1050
+ return (audio_flush (a ));
1051
+ }
1052
+ break ;
985
1053
986
- if (p -> dwBufferLength == (DWORD )(A -> outbuf_size )) {
987
- return (audio_flush (a ));
1054
+ case AUDIO_OUT_TYPE_STDOUT :
1055
+
1056
+ A -> stream_out_data [A -> stream_out_next ++ ] = c ;
1057
+
1058
+ assert (A -> stream_out_next > 0 );
1059
+ assert (A -> stream_out_next <= SDR_UDP_BUF_MAXLEN );
1060
+
1061
+ if (A -> stream_out_next == SDR_UDP_BUF_MAXLEN ) {
1062
+ return (audio_flush (a ));
1063
+ }
1064
+ break ;
988
1065
}
989
1066
990
1067
return (0 );
@@ -1015,27 +1092,54 @@ int audio_flush (int a)
1015
1092
struct adev_s * A ;
1016
1093
1017
1094
A = & (adev [a ]);
1018
-
1019
- p = (LPWAVEHDR )(& (A -> out_wavehdr [A -> out_current ]));
1020
1095
1021
- if (p -> dwUser == DWU_FILLING && p -> dwBufferLength > 0 ) {
1096
+ switch (A -> g_audio_out_type ) {
1097
+ case AUDIO_OUT_TYPE_SOUNDCARD :
1098
+ p = (LPWAVEHDR )(& (A -> out_wavehdr [A -> out_current ]));
1022
1099
1023
- p -> dwUser = DWU_PLAYING ;
1100
+ if ( p -> dwUser == DWU_FILLING && p -> dwBufferLength > 0 ) {
1024
1101
1025
- waveOutPrepareHeader ( A -> audio_out_handle , p , sizeof ( WAVEHDR )) ;
1102
+ p -> dwUser = DWU_PLAYING ;
1026
1103
1027
- e = waveOutWrite (A -> audio_out_handle , p , sizeof (WAVEHDR ));
1028
- if (e != MMSYSERR_NOERROR ) {
1029
- text_color_set (DW_COLOR_ERROR );
1030
- dw_printf ("audio out write error %d\n" , e );
1104
+ waveOutPrepareHeader (A -> audio_out_handle , p , sizeof (WAVEHDR ));
1031
1105
1032
- /* I don't expect this to ever happen but if it */
1033
- /* does, make the buffer available for filling. */
1106
+ e = waveOutWrite (A -> audio_out_handle , p , sizeof (WAVEHDR ));
1107
+ if (e != MMSYSERR_NOERROR ) {
1108
+ text_color_set (DW_COLOR_ERROR );
1109
+ dw_printf ("audio out write error %d\n" , e );
1034
1110
1035
- p -> dwUser = DWU_DONE ;
1036
- return (-1 );
1037
- }
1038
- A -> out_current = (A -> out_current + 1 ) % NUM_OUT_BUF ;
1111
+ /* I don't expect this to ever happen but if it */
1112
+ /* does, make the buffer available for filling. */
1113
+
1114
+ p -> dwUser = DWU_DONE ;
1115
+ return (-1 );
1116
+ }
1117
+ A -> out_current = (A -> out_current + 1 ) % NUM_OUT_BUF ;
1118
+ }
1119
+ break ;
1120
+
1121
+ case AUDIO_OUT_TYPE_STDOUT :;
1122
+
1123
+ int res ;
1124
+ unsigned char * ptr ;
1125
+ unsigned int len ;
1126
+
1127
+ ptr = A -> stream_out_data ;
1128
+ len = A -> stream_out_next ;
1129
+
1130
+ while (len > 0 ) {
1131
+ res = write (STDOUT_FILENO , ptr , len );
1132
+ if (res < 0 ) {
1133
+ text_color_set (DW_COLOR_ERROR );
1134
+ dw_printf ("stdout audio write error %d\n" , res );
1135
+ return (-1 );
1136
+ }
1137
+ ptr += res ;
1138
+ len -= res ;
1139
+ }
1140
+
1141
+ A -> stream_out_next = 0 ;
1142
+ break ;
1039
1143
}
1040
1144
return (0 );
1041
1145
0 commit comments