1 
2 /*
3  * The Real SoundTracker - Sun (input) driver.
4  *
5  * Copyright (C) 2001, 2002, 2003 CubeSoft Communications, Inc.
6  * <http://www.csoft.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22 
23 #include <config.h>
24 
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include <fcntl.h>
31 #include <sys/audioio.h>
32 #include <sys/ioctl.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 
37 #include <glib/gi18n.h>
38 #include <gtk/gtk.h>
39 
40 #include "driver.h"
41 #include "driver-thread.h"
42 #include "errors.h"
43 #include "gui-subs.h"
44 #include "mixer.h"
45 
46 typedef struct sun_driver {
47     GtkWidget* configwidget;
48     GtkWidget* prefs_devaudio_w;
49 
50     int playrate;
51     int stereo;
52     int bits;
53     int bufsize;
54     int numbufs;
55     int mf;
56 
57     int soundfd;
58     void* sndbuf;
59     gboolean (*callback)(void *buf, guint32 count, gint mixfreq, gint mixformat);
60 
61     audio_info_t info;
62     gchar* p_devaudio;
63     int p_resolution;
64     int p_channels;
65     int p_mixfreq;
66     int p_bufsize;
67     DRIVER_THREAD_STUFF
68 } sun_driver;
69 
70 static void*
71 sun_sampling(void* data)
72 {
73     sun_driver* const d = data;
74 
75     DRIVER_THREAD_LOOP_BEGIN(d)
76     errno = 0;
77     if (read(d->soundfd, d->sndbuf, d->bufsize) != d->bufsize) {
78         error_errno(_("SUN input: reading error"));
79         DRIVER_THREAD_ERROR
80     }
81 
82     if (d->callback(d->sndbuf, d->bufsize, d->playrate, d->mf)) {
83         d->sndbuf = calloc(d->bufsize);
84         if (!d->sndbuf) {
85             error_errno(_("SUN input: out of memory error"));
86             DRIVER_THREAD_ERROR
87         }
88     }
89     DRIVER_THREAD_LOOP_END(d)
90 
91     return NULL;
92 }
93 
94 static void
95 prefs_init_from_structure(sun_driver* d)
96 {
97     gtk_entry_set_text(GTK_ENTRY(d->prefs_devaudio_w), d->p_devaudio);
98 }
99 
100 static void
101 sun_devaudio_changed(void* a,
102     sun_driver* d)
103 {
104     gchar *buf = gtk_entry_get_text(GTK_ENTRY(d->prefs_devaudio_w);
105 
106     if (strcmp(buf, d->p_devaudio)) {
107         g_free(d->p_devaudio);
108         d->p_devaudio = g_strdup(buf);
109     }
110 }
111 
112 static void
113 sun_make_config_widgets(sun_driver* d)
114 {
115     GtkWidget *thing, *mainbox, *box2;
116 
117     d->configwidget = mainbox = gtk_vbox_new(FALSE, 2);
118 
119     thing = gtk_label_new(
120         _("These changes won't take effect until you restart sampling."));
121     gtk_widget_show(thing);
122     gtk_box_pack_start(GTK_BOX(mainbox), thing, FALSE, TRUE, 0);
123 
124     thing = gtk_hseparator_new();
125     gtk_widget_show(thing);
126     gtk_box_pack_start(GTK_BOX(mainbox), thing, FALSE, TRUE, 0);
127 
128     box2 = gtk_hbox_new(FALSE, 4);
129     gtk_widget_show(box2);
130     gtk_box_pack_start(GTK_BOX(mainbox), box2, FALSE, TRUE, 0);
131 
132     thing = gtk_label_new(_("Input device (e.g. '/dev/audio'):"));
133     gtk_widget_show(thing);
134     gtk_box_pack_start(GTK_BOX(box2), thing, FALSE, FALSE, 0);
135 
136     thing = gtk_entry_new();
137     gtk_entry_set_max_length(GTK_ENTRY(thing), 126);
138     gtk_widget_show(thing);
139     gtk_box_pack_end(GTK_BOX(box2), thing, FALSE, FALSE, 0);
140     gtk_entry_set_text(GTK_ENTRY(thing), d->p_devaudio);
141     g_signal_connect_after(thing, "changed",
142         G_CALLBACK(sun_devaudio_changed), d);
143     d->prefs_devaudio_w = thing;
144 
145     prefs_init_from_structure(d);
146 }
147 
148 static GtkWidget*
149 sun_getwidget(void* dp)
150 {
151     sun_driver* const d = dp;
152 
153     return d->configwidget;
154 }
155 
156 static void*
157 sun_new(gboolean (*callback)(void *buf, guint32 count, gint mixfreq, gint mixformat))
158 {
159     sun_driver* d = g_new(sun_driver, 1);
160 
161     d->p_devaudio = g_strdup("/dev/audio");
162     d->p_mixfreq = 44100;
163     d->p_channels = 1;
164     d->p_resolution = 16;
165     d->p_bufsize = 9;
166     d->soundfd = -1;
167     d->sndbuf = NULL;
168     d->callback = callback;
169     DRIVER_THREAD_INIT(d)
170 
171     sun_make_config_widgets(d);
172 
173     return d;
174 }
175 
176 static void
177 sun_destroy(void* dp)
178 {
179     sun_driver* const d = dp;
180 
181     DRIVER_THREAD_CLEAR(d)
182     gtk_widget_destroy(d->configwidget);
183     g_free(dp);
184 }
185 
186 static gboolean
187 sun_try_format(sun_driver* d, int fmt, int precision)
188 {
189     audio_encoding_t enc;
190 
191     for (enc.index = 0; ioctl(d->soundfd, AUDIO_GETENC, &enc) == 0;
192          enc.index++) {
193         if (enc.encoding == fmt && enc.precision == precision) {
194             d->info.record.encoding = enc.encoding;
195             d->info.record.precision = enc.precision;
196             if (ioctl(d->soundfd, AUDIO_SETINFO, &d->info) == 0) {
197                 return TRUE;
198             } else {
199                 return FALSE;
200             }
201         }
202     }
203 
204     return FALSE;
205 }
206 
207 static gboolean
208 sun_try_channels(sun_driver* d, int nch)
209 {
210     d->info.record.channels = nch;
211     if (ioctl(d->soundfd, AUDIO_SETINFO, &d->info) != 0) {
212         return FALSE;
213     }
214 
215     return TRUE;
216 }
217 
218 static void
219 sun_release(void* dp)
220 {
221     sun_driver* const d = dp;
222 
223     DRIVER_THREAD_STOP(d);
224 
225     if (d->sndbuf) {
226         free(d->sndbuf);
227         d->sndbuf = NULL;
228     }
229 
230     if (d->soundfd >= 0) {
231         close(d->soundfd);
232         d->soundfd = -1;
233     }
234 }
235 
236 static gboolean
237 sun_open(void* dp)
238 {
239     char* buf;
240     sun_driver* const d = dp;
241     int mf, i, fullduplex;
242 
243     AUDIO_INITINFO(&d->info);
244 
245     errno = 0;
246     d->soundfd = open(d->p_devaudio, O_RDONLY | O_NONBLOCK);
247     if (d->soundfd < 0) {
248         buf = g_strdup_printf(_("SUN input (%s): Cannot open device"), d->p_devaudio);
249         error_errno(buf);
250         g_free(buf);
251         goto out;
252     }
253 
254     fullduplex = 1;
255     if (ioctl(d->soundfd, AUDIO_SETFD, &fullduplex) != 0) {
256         buf = g_strdup_printf(_("SUN input (%s) does not support full-duplex operation"),
257             d->p_devaudio);
258         error_warning(buf);
259         g_free(buf);
260         fullduplex = 0;
261     }
262 
263     errno = 0;
264     d->info.mode = AUMODE_RECORD;
265     if (ioctl(d->soundfd, AUDIO_SETINFO, &d->info) != 0) {
266         buf = g_strdup_printf(_("SUN input (%s) does not support recording"), d->p_devaudio);
267         error_errno(buf);
268         g_free(buf);
269         goto out;
270     }
271 
272     d->playrate = d->p_mixfreq;
273     d->info.record.sample_rate = d->playrate;
274     if (ioctl(d->soundfd, AUDIO_SETINFO, &d->info) != 0) {
275         buf = g_strdup_printf(_("SUN input (%s): Cannot handle %d Hz"), d->p_devaudio);
276         error_errno(buf);
277         g_free(buf);
278         goto out;
279     }
280 
281     d->bits = 0;
282     mf = 0;
283     if (d->p_resolution == 16) {
284         if (sun_try_format(d, AUDIO_ENCODING_SLINEAR_LE, 16)) {
285             d->bits = 16;
286             mf = ST_MIXER_FORMAT_S16_LE;
287         } else if (sun_try_format(d, AUDIO_ENCODING_SLINEAR_BE, 16)) {
288             d->bits = 16;
289             mf = ST_MIXER_FORMAT_S16_BE;
290         } else if (sun_try_format(d, AUDIO_ENCODING_ULINEAR_LE, 16)) {
291             d->bits = 16;
292             mf = ST_MIXER_FORMAT_U16_LE;
293         } else if (sun_try_format(d, AUDIO_ENCODING_ULINEAR_BE, 16)) {
294             d->bits = 16;
295             mf = ST_MIXER_FORMAT_U16_BE;
296         }
297     }
298     if (d->bits != 16) {
299         if (sun_try_format(d, AUDIO_ENCODING_SLINEAR, 8)) {
300             d->bits = 8;
301             mf = ST_MIXER_FORMAT_S8;
302         } else if (sun_try_format(d, AUDIO_ENCODING_PCM8, 8)) {
303             d->bits = 8;
304             mf = ST_MIXER_FORMAT_U8;
305         } else {
306             buf = g_strdup_printf(_("SUN input (%s): Required sound encoding not supported."),
307                 d->p_devaudio);
308             error_warning(buf);
309             g_free(buf);
310             goto out;
311         }
312     }
313 
314     if (d->p_channels == 2 && sun_try_channels(d, 2)) {
315         d->stereo = 1;
316         mf |= ST_MIXER_FORMAT_STEREO;
317     } else if (sun_try_channels(d, 1)) {
318         d->stereo = 0;
319     }
320 
321     d->mf = mf;
322 
323     i = 0x00040000 + d->p_bufsize + d->stereo + (d->bits / 8 - 1);
324     d->info.blocksize = 1 << (i & 0xffff);
325     d->info.record.buffer_size = d->info.blocksize;
326     d->info.hiwat = ((unsigned)i >> 16) & 0x7fff;
327     printf("input blocksize %d hiwat %d\n", d->info.blocksize, d->info.hiwat);
328     d->info.hiwat = 1;
329     if (d->info.hiwat == 0) {
330         d->info.hiwat = 65536;
331     }
332     errno = 0;
333     if (ioctl(d->soundfd, AUDIO_SETINFO, &d->info) != 0) {
334         buf = g_strdup_printf(_("SUN input (%s): Cannot set block size"), d->p_devaudio);
335         error_errno(buf);
336         g_free(buf);
337         goto out;
338     }
339 
340     if (ioctl(d->soundfd, AUDIO_GETINFO, &d->info) != 0) {
341         buf = g_strdup_printf(_("SUN input (%s): Cannot get device information"), d->p_devaudio);
342         error_errno(buf);
343         g_free(buf);
344         goto out;
345     }
346     d->bufsize = d->info.blocksize;
347     d->numbufs = d->info.hiwat;
348     d->sndbuf = calloc(1, d->bufsize);
349 
350     if (d->stereo == 1) {
351         d->bufsize /= 2;
352     }
353     if (d->bits == 16) {
354         d->bufsize /= 2;
355     }
356 
357 
358     DRIVER_THREAD_RESUME(d)
359     return TRUE;
360 
361 out:
362     sun_release(dp);
363     return FALSE;
364 }
365 
366 static gboolean
367 sun_loadsettings(void* dp,
368     const gchar* f)
369 {
370     sun_driver* const d = dp;
371     gchar* buf;
372 
373     if ((buf = prefs_get_string(f, "sun-devaudio", NULL))) {
374         g_free(d->p_devaudio);
375         d->p_devaudio = buf;
376     }
377 
378     prefs_init_from_structure(d);
379 
380     return TRUE;
381 }
382 
383 static gboolean
384 sun_savesettings(void* dp,
385     const gchar* f)
386 {
387     sun_driver* const d = dp;
388 
389     prefs_put_string(f, "sun-devaudio", d->p_devaudio);
390 
391     return TRUE;
392 }
393 
394 static void
395 sun_activate (void *dp, const gchar* group)
396 {
397     sun_driver* const d = dp;
398 
399     DRIVER_THREAD_NEW(d, sun_sampling, dp)
400 }
401 
402 static void
403 sun_deactivate (void *dp)
404 {
405     sun_driver* const d = dp;
406 
407     DRIVER_THREAD_CANCEL(d)
408 }
409 
410 st_driver driver_in_sun = {
411     "Sun Sampling",
412 
413     sun_new,
414     sun_destroy,
415 
416     sun_open,
417     sun_release,
418 
419     sun_getwidget,
420     sun_loadsettings,
421     sun_savesettings,
422 
423     sun_activate,
424     sun_deactivate,
425 
426     NULL,
427     NULL
428 };
429