diff --git a/CMakeLists.txt b/CMakeLists.txt index fbd6d86..a0ec8d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,10 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") configure_file("${CMAKE_SOURCE_DIR}/cmake/cpack/${CMAKE_PROJECT_NAME}.desktop.in" "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.desktop" @ONLY) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") + set(OPENBSD TRUE) + set(HAVE_SNDIO TRUE) + elseif(APPLE) if("${CMAKE_OSX_DEPLOYMENT_TARGET}" STREQUAL "") message(STATUS "Build for macOS target: local version") @@ -301,6 +305,12 @@ if(LINUX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_AVAHI_CLIENT") endif() +elseif (HAVE_SNDIO) + find_package(sndio REQUIRED) + if(SNDIO_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_SNDIO") + endif() + elseif (NOT WIN32 AND NOT CYGWIN) find_package(Portaudio REQUIRED) if(PORTAUDIO_FOUND) @@ -316,6 +326,8 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_CM108") set(PORTAUDIO_INCLUDE_DIRS "") set(PORTAUDIO_LIBRARIES "") + set(SNDIO_INCLUDE_DIRS "") + set(SNDIO_LIBRARIES "") endif() # manage and fetch new data diff --git a/cmake/modules/Findsndio.cmake b/cmake/modules/Findsndio.cmake new file mode 100644 index 0000000..e7292d5 --- /dev/null +++ b/cmake/modules/Findsndio.cmake @@ -0,0 +1,42 @@ +# - Try to find sndio +# +# SNDIO_FOUND - system has sndio +# SNDIO_LIBRARIES - location of the library for sndio +# SNDIO_INCLUDE_DIRS - location of the include files for sndio + +set(SNDIO_ROOT_DIR + "${SNDIO_ROOT_DIR}" + CACHE + PATH + "Directory to search for sndio") + +# no need to check pkg-config + +find_path(SNDIO_INCLUDE_DIRS + NAMES + sndio.h + PATHS + /usr/local/include + /usr/include + /opt/local/include + HINTS + ${SNDIO_ROOT_DIR} + ) + +find_library(SNDIO_LIBRARIES + NAMES + sndio + PATHS + /usr/local/lib + /usr/lib + /usr/lib64 + /opt/local/lib + HINTS + ${SNDIIO_ROOT_DIR} + ) + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SNDIO DEFAULT_MSG SNDIO_INCLUDE_DIRS SNDIO_LIBRARIES) + +mark_as_advanced(SNDIO_INCLUDE_DIRS SNDIO_LIBRARIES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7fba03b..9fe8306 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ include_directories( ${ALSA_INCLUDE_DIRS} ${UDEV_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS} + ${SNDIO_INCLUDE_DIRS} ${CUSTOM_GEOTRANZ_DIR} ${CUSTOM_HIDAPI_DIR} ) @@ -116,7 +117,11 @@ if(LINUX) list(REMOVE_ITEM direwolf_SOURCES dwgpsd.c ) - else() # macOS freebsd openbsd + elseif(HAVE_SNDIO) + list(APPEND direwolf_SOURCES + audio.c + ) + else() # macOS freebsd list(APPEND direwolf_SOURCES audio_portaudio.c ) @@ -143,6 +148,7 @@ target_link_libraries(direwolf ${ALSA_LIBRARIES} ${UDEV_LIBRARIES} ${PORTAUDIO_LIBRARIES} + ${SNDIO_LIBRARIES} ${AVAHI_LIBRARIES} ) diff --git a/src/audio.c b/src/audio.c index 613be06..ee92786 100644 --- a/src/audio.c +++ b/src/audio.c @@ -75,18 +75,17 @@ #include #include #include +#include #if USE_ALSA #include -#else -#include -#ifdef __OpenBSD__ -#include +#elif USE_SNDIO +#include +#include #else #include #endif -#endif #include "audio.h" @@ -111,6 +110,9 @@ static struct adev_s { int bytes_per_frame; /* number of bytes for a sample from all channels. */ /* e.g. 4 for stereo 16 bit. */ +#elif USE_SNDIO + struct sio_hdl *sndio_in_handle; + struct sio_hdl *sndio_out_handle; #else int oss_audio_device_fd; /* Single device, both directions. */ @@ -141,6 +143,9 @@ static struct adev_s { #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); +#elif USE_SNDIO +static int set_sndio_params (int a, struct sio_hdl *handle, struct audio_s *pa, char *devname, char *inout); +static int poll_sndio (struct sio_hdl *hdl, int events); #else static int set_oss_params (int a, int fd, struct audio_s *pa); #endif @@ -212,7 +217,9 @@ static int calcbufsize(int rate, int chans, int bits) int audio_open (struct audio_s *pa) { +#if !USE_SNDIO int err; +#endif int chan; int a; char audio_in_name[30]; @@ -224,7 +231,11 @@ int audio_open (struct audio_s *pa) memset (adev, 0, sizeof(adev)); for (a=0; aadev[a].adevice_in, O_RDWR); @@ -439,6 +468,27 @@ int audio_open (struct audio_s *pa) return (-1); } +#elif USE_SNDIO + adev[a].sndio_out_handle = sio_open (audio_out_name, SIO_PLAY, 0); + if (adev[a].sndio_out_handle == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not open audio device %s for output\n", + audio_out_name); + return (-1); + } + + adev[a].outbuf_size_in_bytes = set_sndio_params (a, adev[a].sndio_out_handle, pa, audio_out_name, "output"); + + if (adev[a].inbuf_size_in_bytes <= 0 || adev[a].outbuf_size_in_bytes <= 0) { + return (-1); + } + + if (!sio_start (adev[a].sndio_out_handle)) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not start audio device %s for output\n", + audio_out_name); + return (-1); + } #endif /* @@ -675,6 +725,112 @@ static int set_alsa_params (int a, snd_pcm_t *handle, struct audio_s *pa, char * } /* end alsa_set_params */ +#elif USE_SNDIO + +/* + * Set parameters for sound card. (sndio) + * + * See /usr/include/sndio.h for details. + */ + +static int set_sndio_params (int a, struct sio_hdl *handle, struct audio_s *pa, char *devname, char *inout) +{ + + struct sio_par q, r; + + /* Signed 16 bit little endian or unsigned 8 bit. */ + sio_initpar (&q); + q.bits = pa->adev[a].bits_per_sample; + q.bps = (q.bits + 7) / 8; + q.sig = (q.bits == 8) ? 0 : 1; + q.le = 1; /* always little endian */ + q.msb = 0; /* LSB aligned */ + q.rchan = q.pchan = pa->adev[a].num_channels; + q.rate = pa->adev[a].samples_per_sec; + q.xrun = SIO_IGNORE; + q.appbufsz = calcbufsize(pa->adev[a].samples_per_sec, pa->adev[a].num_channels, pa->adev[a].bits_per_sample); + + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf ("suggest buffer size %d bytes for %s %s.\n", + q.appbufsz, devname, inout); +#endif + + /* challenge new setting */ + if (!sio_setpar (handle, &q)) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not set hardware parameter for %s %s.\n", + devname, inout); + return (-1); + } + + /* get response */ + if (!sio_getpar (handle, &r)) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not obtain current hardware setting for %s %s.\n", + devname, inout); + return (-1); + } + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf ("audio buffer size %d bytes for %s %s.\n", + r.appbufsz, devname, inout); +#endif + if (q.rate != r.rate) { + text_color_set(DW_COLOR_INFO); + dw_printf ("Asked for %d samples/sec but got %d for %s %s.", + pa->adev[a].samples_per_sec, r.rate, devname, inout); + pa->adev[a].samples_per_sec = r.rate; + } + + /* not supported */ + if (q.bits != r.bits || q.bps != r.bps || q.sig != r.sig || + (q.bits > 8 && q.le != r.le) || + (*inout == 'o' && q.pchan != r.pchan) || + (*inout == 'i' && q.rchan != r.rchan)) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Unsupported format for %s %s.\n", devname, inout); + return (-1); + } + + return r.appbufsz; + +} /* end set_sndio_params */ + +static int poll_sndio (struct sio_hdl *hdl, int events) +{ + struct pollfd *pfds; + int nfds, revents; + + nfds = sio_nfds (hdl); + pfds = alloca (nfds * sizeof(struct pollfd)); + + do { + nfds = sio_pollfd (hdl, pfds, events); + if (nfds < 1) { + /* no need to wait */ + return (0); + } + if (poll (pfds, nfds, -1) < 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("poll %d\n", errno); + return (-1); + } + revents = sio_revents (hdl, pfds); + } while (!(revents & (events | POLLHUP))); + + /* unrecoverable error occured */ + if (revents & POLLHUP) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("waited for %s, POLLHUP received\n", (events & POLLIN) ? "POLLIN" : "POLLOUT"); + return (-1); + } + + return (0); +} + #else @@ -842,7 +998,9 @@ __attribute__((hot)) int audio_get (int a) { int n; +#if USE_ALSA int retries = 0; +#endif #if STATISTICS /* Gather numbers for read from audio device. */ @@ -970,7 +1128,28 @@ int audio_get (int a) } -#else /* end ALSA, begin OSS */ +#elif USE_SNDIO + + while (adev[a].inbuf_next >= adev[a].inbuf_len) { + + assert (adev[a].sndio_in_handle != NULL); + if (poll_sndio (adev[a].sndio_in_handle, POLLIN) < 0) { + adev[a].inbuf_len = 0; + adev[a].inbuf_next = 0; + return (-1); + } + + n = sio_read (adev[a].sndio_in_handle, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes); + adev[a].inbuf_len = n; + adev[a].inbuf_next = 0; + + audio_stats (a, + save_audio_config_p->adev[a].num_channels, + n / (save_audio_config_p->adev[a].num_channels * save_audio_config_p->adev[a].bits_per_sample / 8), + save_audio_config_p->statistics_interval); + } + +#else /* begin OSS */ /* Fixed in 1.2. This was formerly outside of the switch */ /* so the OSS version did not process stdin or UDP. */ @@ -1250,6 +1429,37 @@ int audio_flush (int a) adev[a].outbuf_len = 0; return (-1); +#elif USE_SNDIO + + int k; + unsigned char *ptr; + int len; + + ptr = adev[a].outbuf_ptr; + len = adev[a].outbuf_len; + + while (len > 0) { + assert (adev[a].sndio_out_handle != NULL); + if (poll_sndio (adev[a].sndio_out_handle, POLLOUT) < 0) { + text_color_set(DW_COLOR_ERROR); + perror("Can't write to audio device"); + adev[a].outbuf_len = 0; + return (-1); + } + + k = sio_write (adev[a].sndio_out_handle, ptr, len); +#if DEBUGx + text_color_set(DW_COLOR_DEBUG); + dw_printf ("audio_flush(): write %d returns %d\n", len, k); + fflush (stdout); +#endif + ptr += k; + len -= k; + } + + adev[a].outbuf_len = 0; + return (0); + #else /* OSS */ int k; @@ -1351,6 +1561,10 @@ void audio_wait (int a) * Either way, the caller will now compensate for it. */ +#elif USE_SNDIO + + poll_sndio (adev[a].sndio_out_handle, POLLOUT); + #else assert (adev[a].oss_audio_device_fd > 0); @@ -1396,7 +1610,22 @@ int audio_close (void) snd_pcm_close (adev[a].audio_in_handle); snd_pcm_close (adev[a].audio_out_handle); - + + adev[a].audio_in_handle = adev[a].audio_out_handle = NULL; + +#elif USE_SNDIO + + if (adev[a].sndio_in_handle != NULL && adev[a].sndio_out_handle != NULL) { + + audio_wait (a); + + sio_stop (adev[a].sndio_in_handle); + sio_stop (adev[a].sndio_out_handle); + sio_close (adev[a].sndio_in_handle); + sio_close (adev[a].sndio_out_handle); + + adev[a].sndio_in_handle = adev[a].sndio_out_handle = NULL; + #else if (adev[a].oss_audio_device_fd > 0) { diff --git a/src/audio.h b/src/audio.h index 32868c5..7eca120 100644 --- a/src/audio.h +++ b/src/audio.h @@ -352,8 +352,8 @@ struct audio_s { #define DEFAULT_ADEVICE "" /* Mac OSX: Empty string = default audio device. */ #elif USE_ALSA #define DEFAULT_ADEVICE "default" /* Use default device for ALSA. */ -#elif __OpenBSD__ -#define DEFAULT_ADEVICE "default" /* Use default device for OpenBSD-portaudio. */ +#elif USE_SNDIO +#define DEFAULT_ADEVICE "default" /* Use default device for sndio. */ #else #define DEFAULT_ADEVICE "/dev/dsp" /* First audio device for OSS. (FreeBSD) */ #endif diff --git a/src/direwolf.c b/src/direwolf.c index 10c6cb1..fd3e293 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -67,9 +67,8 @@ #include #include #include -#ifdef __OpenBSD__ -#include -#elif __APPLE__ +#if USE_SNDIO || __APPLE__ +// no need to include #else #include #endif