Skip to content

Commit 6ac840c

Browse files
committed
Add stdout audio for Windows
1 parent f1fcb8c commit 6ac840c

File tree

2 files changed

+185
-80
lines changed

2 files changed

+185
-80
lines changed

Diff for: src/audio_win.c

+184-80
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ static int calcbufsize(int rate, int chans, int bits)
133133

134134
static struct adev_s {
135135

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;
138138
/*
139139
* UDP socket for receiving audio stream.
140140
* Buffer, length, and pointer for UDP or stdin.
@@ -146,6 +146,12 @@ static struct adev_s {
146146
int stream_len;
147147
int stream_next;
148148

149+
/*
150+
* Buffer and index for stdout.
151+
*/
152+
153+
unsigned char stream_out_data[SDR_UDP_BUF_MAXLEN];
154+
int stream_out_next;
149155

150156
/* For sound output. */
151157
/* out_wavehdr.dwUser is used to keep track of output buffer state. */
@@ -343,28 +349,36 @@ int audio_open (struct audio_s *pa)
343349

344350
/*
345351
* Select output device.
346-
* Only soundcard at this point.
352+
* Only soundcard and stdout at this point.
347353
* Purhaps we'd like to add UDP for an SDR transmitter.
348354
*/
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+
}
355368

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+
}
362376
}
363377
}
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+
}
368382
}
369383
}
370384
} /* if defined */
@@ -424,7 +438,7 @@ int audio_open (struct audio_s *pa)
424438

425439
struct adev_s *A = &(adev[a]);
426440

427-
/* Display stdin or udp:port if appropriate. */
441+
/* Display stdin or udp:port if appropriate. */
428442

429443
if (A->g_audio_in_type != AUDIO_IN_TYPE_SOUNDCARD) {
430444

@@ -498,6 +512,36 @@ int audio_open (struct audio_s *pa)
498512
}
499513
}
500514

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+
}
501545

502546
/*
503547
* Open for each audio device input/output pair.
@@ -523,32 +567,47 @@ int audio_open (struct audio_s *pa)
523567

524568
/*
525569
* Open the audio output device.
526-
* Soundcard is only possibility at this time.
570+
* Soundcard and stdout are only possibility at this time.
527571
*/
528572

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:
536576

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;
537584
/*
538585
* Set up the output buffers.
539586
* We use dwUser to indicate it is available for filling.
540587
*/
541588

542-
memset ((void*)(A->out_wavehdr), 0, sizeof(A->out_wavehdr));
589+
memset ((void*)(A->out_wavehdr), 0, sizeof(A->out_wavehdr));
543590

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+
}
550610

551-
552611
/*
553612
* Open audio input device.
554613
* More possibilities here: soundcard, UDP port, stdin.
@@ -942,49 +1001,67 @@ int audio_put (int a, int c)
9421001

9431002
struct adev_s *A;
9441003
A = &(adev[a]);
945-
1004+
1005+
switch (A->g_audio_out_type) {
1006+
1007+
case AUDIO_OUT_TYPE_SOUNDCARD:
1008+
9461009
/*
9471010
* Wait if no buffers are available.
9481011
* Don't use p yet because compiler might might consider dwFlags a loop invariant.
9491012
*/
9501013

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);
9571020

9581021
// TODO: open issues 78 & 165. How can we avoid/improve this?
9591022

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+
}
9701033

971-
p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
1034+
p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
9721035

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. */
9781043

979-
/* Should never be full at this point. */
1044+
assert (p->dwBufferLength >= 0);
1045+
assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
9801046

981-
assert (p->dwBufferLength >= 0);
982-
assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
1047+
p->lpData[p->dwBufferLength++] = c;
9831048

984-
p->lpData[p->dwBufferLength++] = c;
1049+
if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
1050+
return (audio_flush(a));
1051+
}
1052+
break;
9851053

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;
9881065
}
9891066

9901067
return (0);
@@ -1015,27 +1092,54 @@ int audio_flush (int a)
10151092
struct adev_s *A;
10161093

10171094
A = &(adev[a]);
1018-
1019-
p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
10201095

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]));
10221099

1023-
p->dwUser = DWU_PLAYING;
1100+
if (p->dwUser == DWU_FILLING && p->dwBufferLength > 0) {
10241101

1025-
waveOutPrepareHeader(A->audio_out_handle, p, sizeof(WAVEHDR));
1102+
p->dwUser = DWU_PLAYING;
10261103

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));
10311105

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);
10341110

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;
10391143
}
10401144
return (0);
10411145

Diff for: src/textcolor.c

+1
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ int dw_printf (const char *fmt, ...)
384384
// TODO: other possible destinations...
385385

386386
fputs (buffer, g_dw_printf_dest);
387+
fflush (g_dw_printf_dest);
387388
return (len);
388389
}
389390

0 commit comments

Comments
 (0)