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