$OpenBSD: patch-src_core_c,v 1.1 2009/01/28 00:20:57 sthen Exp $
--- src/core.c.orig	Tue Jan  1 04:11:42 2008
+++ src/core.c	Fri Jan  9 14:46:42 2009
@@ -45,6 +45,8 @@
 #include <samplerate.h>
 #endif /* HAVE_SRC */
 
+#include <sndio.h>
+
 #ifdef HAVE_OSS
 #include <sys/ioctl.h>
 #include <sys/types.h>
@@ -637,6 +639,154 @@ disk_thread(void * arg) {
 
 
 
+/* SNDIO output thread */
+void *
+sndio_thread(void * arg) {
+
+        u_int32_t i;
+        thread_info_t * info = (thread_info_t *)arg;
+	u_int32_t driver_offset = 0;
+	int bufsize = 1024;
+        int n_avail;
+	size_t bytes_written;
+	char recv_cmd;
+
+	short * sndio_short_buf;
+
+	struct sio_hdl * sndio_hdl;
+	struct timespec req_time;
+	struct timespec rem_time;
+	req_time.tv_sec = 0;
+        req_time.tv_nsec = 100000000;
+
+	sndio_hdl = info->sndio_hdl;
+	if ((info->sndio_short_buf = malloc(2*bufsize * sizeof(short))) == NULL) {
+		fprintf(stderr, "sndio_thread: malloc error\n");
+		exit(1);
+	}
+	sndio_short_buf = info->sndio_short_buf;
+
+	if ((l_buf = malloc(bufsize * sizeof(float))) == NULL) {
+		fprintf(stderr, "sndio_thread: malloc error\n");
+		exit(1);
+	}
+	if ((r_buf = malloc(bufsize * sizeof(float))) == NULL) {
+		fprintf(stderr, "sndio_thread: malloc error\n");
+		exit(1);
+	}
+#ifdef HAVE_LADSPA
+	ladspa_buflen = bufsize;
+#endif /* HAVE_LADSPA */
+
+
+	while (1) {
+	sndio_wake:
+		while (rb_read_space(rb_disk2out)) {
+			rb_read(rb_disk2out, &recv_cmd, 1);
+			switch (recv_cmd) {
+			case CMD_FLUSH:
+				while ((n_avail = rb_read_space(rb)) > 0) {
+					if (n_avail > 2*bufsize * sizeof(short))
+						n_avail = 2*bufsize * sizeof(short);
+					rb_read(rb, (char *)sndio_short_buf,
+							     2*bufsize * sizeof(short));
+				}
+				rb_write(rb_out2disk, (char *)&driver_offset, sizeof(u_int32_t));
+				goto sndio_wake;
+				break;
+			case CMD_FINISH:
+				goto sndio_finish;
+				break;
+			default:
+				fprintf(stderr, "sndio_thread: recv'd unknown command %d\n", recv_cmd);
+				break;
+			}
+		}
+
+		if ((n_avail = rb_read_space(rb) / (2*sample_size)) == 0) {
+			nanosleep(&req_time, &rem_time);
+			goto sndio_wake;
+		}
+
+		if (n_avail > bufsize)
+			n_avail = bufsize;
+		
+		for (i = 0; i < n_avail; i++) {
+			rb_read(rb, (char *)&(l_buf[i]), sample_size);
+			rb_read(rb, (char *)&(r_buf[i]), sample_size);
+		}
+
+#ifdef HAVE_LADSPA
+		if (options.ladspa_is_postfader) {
+			for (i = 0; i < n_avail; i++) {
+				l_buf[i] *= left_gain;
+				r_buf[i] *= right_gain;
+			}
+		}
+#else
+		for (i = 0; i < n_avail; i++) {
+			l_buf[i] *= left_gain;
+			r_buf[i] *= right_gain;
+		}
+#endif /* HAVE_LADSPA */
+
+		if (n_avail < bufsize) {
+			for (i = n_avail; i < bufsize; i++) {
+				l_buf[i] = 0.0f;
+				r_buf[i] = 0.0f;
+			}
+		}
+
+		/* plugin processing */
+#ifdef HAVE_LADSPA
+		plugin_lock = 1;
+		for (i = 0; i < n_plugins; i++) {
+			if (plugin_vect[i]->is_bypassed)
+				continue;
+
+			if (plugin_vect[i]->handle) {
+				plugin_vect[i]->descriptor->run(plugin_vect[i]->handle, ladspa_buflen);
+			}
+			if (plugin_vect[i]->handle2) {
+				plugin_vect[i]->descriptor->run(plugin_vect[i]->handle2, ladspa_buflen);
+			}
+		}
+		plugin_lock = 0;
+
+		if (!options.ladspa_is_postfader) {
+			for (i = 0; i < bufsize; i++) {
+				l_buf[i] *= left_gain;
+				r_buf[i] *= right_gain;
+			}
+		}
+#endif /* HAVE_LADSPA */
+
+		for (i = 0; i < bufsize; i++) {
+			if (l_buf[i] > 1.0)
+				l_buf[i] = 1.0;
+			else if (l_buf[i] < -1.0)
+				l_buf[i] = -1.0;
+
+			if (r_buf[i] > 1.0)
+				r_buf[i] = 1.0;
+			else if (r_buf[i] < -1.0)
+				r_buf[i] = -1.0;
+
+			sndio_short_buf[2*i] = floorf(32767.0 * l_buf[i]);
+			sndio_short_buf[2*i+1] = floorf(32767.0 * r_buf[i]);
+		}
+
+		/* write data to audio device */
+		bytes_written = sio_write(sndio_hdl, sndio_short_buf, 2*n_avail * sizeof(short));
+		if (bytes_written != 2*n_avail * sizeof(short))
+			fprintf(stderr, "sndio_thread: Error writing to audio device\n");
+	}
+ sndio_finish:
+	return 0;
+}
+
+
+
 /* OSS output thread */
 #ifdef HAVE_OSS
 void *
@@ -1149,6 +1299,84 @@ set_thread_priority(pthread_t thread, char * name, int
 }
 #endif /* _WIN32 */
 
+/* return values:
+ *  0 : success
+ * -1 : device busy
+ * -N : unable to start with given params
+ */
+int
+sndio_init(thread_info_t * info, int verbose, int realtime, int priority) {
+
+	struct sio_hdl * sndio_hdl;
+	struct sio_par sndio_par;
+
+        if (info->out_SR > MAX_SAMPLERATE) {
+		if (verbose) {
+			fprintf(stderr, "\nThe sample rate you set (%ld Hz) is higher than MAX_SAMPLERATE.\n",
+				info->out_SR);
+			fprintf(stderr, "This is an arbitrary limit, which you may safely enlarge "
+				"if you really need to.\n");
+			fprintf(stderr, "Currently MAX_SAMPLERATE = %d Hz.\n", MAX_SAMPLERATE);
+		}
+                return -2;
+        }
+
+	sndio_hdl = sio_open(NULL, SIO_PLAY, 0);
+	if (sndio_hdl == NULL) {
+		if (verbose) {
+			fprintf(stderr, "sio_open failed\n");
+		}
+		return -1;
+	}
+	
+	sio_initpar(&sndio_par);
+	sndio_par.bits = 16;
+	sndio_par.pchan = 2;
+	sndio_par.rate = info->out_SR;
+	sndio_par.sig = 1;
+	sndio_par.le = 1;
+	sndio_par.appbufsz = sndio_par.rate/4;
+	
+	if(sio_setpar(sndio_hdl, &sndio_par) == 0) {
+		if (verbose) {
+			fprintf(stderr, "sio_setpar failed\n");
+		}
+		sio_close(sndio_hdl);
+		return -3;
+	}
+	if(sio_getpar(sndio_hdl, &sndio_par) == 0) {
+		if (verbose) {
+			fprintf(stderr, "sio_getpar failed\n");
+		}
+		sio_close(sndio_hdl);
+		return -4;
+	}
+	if((sndio_par.bits != 16) || (sndio_par.pchan != 2) || \
+	   (sndio_par.rate != info->out_SR) || (sndio_par.sig != 1) || \
+	   (sndio_par.le != 1)) {
+		if (verbose) {
+			fprintf(stderr, "can't set sndio parameters\n");
+		}
+		sio_close(sndio_hdl);
+		return -5;
+	}
+	if(sio_start(sndio_hdl) == 0) {
+		if (verbose) {
+			fprintf(stderr, "sio_start failed\n");
+		}
+		sio_close(sndio_hdl);
+		return -3;
+	}
+
+	info->sndio_hdl = sndio_hdl;
+	AQUALUNG_THREAD_CREATE(info->sndio_thread_id, NULL, sndio_thread, info)
+	set_thread_priority(info->sndio_thread_id, "sndio output", realtime, priority);
+
+	return 0;
+}
+
+
+
 #ifdef HAVE_OSS
 /* return values:
  *  0 : success
@@ -2210,7 +2438,7 @@ main(int argc, char ** argv) {
 
 #if defined(HAVE_JACK) || defined(HAVE_ALSA) || defined(HAVE_OSS)
 	int auto_driver_found = 0;
-#endif /* jack || alsa || oss */
+#endif /* jack || alsa || oss || sndio */
 
 	if (setenv("LC_NUMERIC", "POSIX", 1) != 0) {
 		fprintf(stderr, "aqualung main(): setenv(\"LC_NUMERIC\", \"POSIX\") failed\n");
@@ -2277,6 +2505,11 @@ main(int argc, char ** argv) {
 				break;
 			case 'o':
 				output_str = strdup(optarg);
+				if (strcmp(output_str, "sndio") == 0) {
+					output = SNDIO_DRIVER;
+					free(output_str);
+					break;
+				}
 				if (strcmp(output_str, "oss") == 0) {
 #ifdef HAVE_OSS
 					output = OSS_DRIVER;
@@ -2702,6 +2935,21 @@ main(int argc, char ** argv) {
 		}
 	}
 #endif /* HAVE_ALSA */
+	if (output == 0) { /* probe sndio */
+		int ret;
+
+		printf("Probing sndio driver... ");
+		thread_info.out_SR = rate;
+
+		ret = sndio_init(&thread_info, 0, try_realtime, priority);
+		if (ret < 0) {
+			printf("unable to start with default params\n");
+		} else {
+			output = SNDIO_DRIVER;
+			auto_driver_found = 1;
+			printf("OK\n");
+		}
+	}
 #ifdef HAVE_OSS
 	if (output == 0) { /* probe OSS */
 		int ret;
@@ -2793,6 +3041,10 @@ main(int argc, char ** argv) {
 	}
 #endif /* HAVE_ALSA */
 
+	if (output == SNDIO_DRIVER) {
+		thread_info.out_SR = rate;
+	}
+
 #ifdef HAVE_OSS
 	if (output == OSS_DRIVER) {
 		thread_info.out_SR = rate;
@@ -2814,6 +3066,15 @@ main(int argc, char ** argv) {
 		disk_try_realtime, disk_priority);
 #endif /* _WIN32 */
 
+	if (output == SNDIO_DRIVER) {
+		if (!auto_driver_found) {
+			int ret = sndio_init(&thread_info, 1, try_realtime, priority);
+			if (ret < 0) {
+				exit(1);
+			}
+		}
+	}
+
 #ifdef HAVE_OSS
 	if (output == OSS_DRIVER) {
 		if (!auto_driver_found) {
@@ -2845,6 +3106,12 @@ main(int argc, char ** argv) {
 	close_app_socket();
 
 	AQUALUNG_THREAD_JOIN(thread_info.disk_thread_id)
+
+	if (output == SNDIO_DRIVER) {
+		AQUALUNG_THREAD_JOIN(thread_info.sndio_thread_id)
+		free(thread_info.sndio_short_buf);
+		sio_close(thread_info.sndio_hdl);
+	}
 
 #ifdef HAVE_OSS
 	if (output == OSS_DRIVER) {
