/* openbsd/audio.c vi:ts=3 sw=3: */ /* Native BSD interface */ #include "defs.h" #include #include #include "extern.h" #include "prefs.h" #include "autoinit.h" #include "watched_var.h" #include struct options_set *port_options=0; #define DEFAULT_BUFFERS #define UNSIGNED8 #define DEFAULT_SET_MIX #define NEW_OUTPUT_SAMPLES_AWARE #define NEW_FUNCS /* fine-tune to get the scrolling display in sync with the music */ #define ADVANCE_TAGS 20000 #include "Arch/common.c" #include #include ID("$Id: audio.c,v 1.3 1999/03/05 16:25:49 espie Exp $") LOCAL unsigned long samples_max; LOCAL int audio; LOCAL unsigned long current_freq; unsigned long total; LOCAL int dsp_samplesize = 0; unsigned long open_audio(unsigned long f, int s) { int buf_max; unsigned long possible, current; audio = open("/dev/audio", O_WRONLY, 0); if (audio == -1) end_all("Error opening audio device"); /* iterate and find true native formats */ { struct audio_encoding query; for (query.index = 0; ioctl(audio, AUDIO_GETENC, &query) != -1; query.index++) { if (query.flags & AUDIO_ENCODINGFLAG_EMULATED) continue; if (query.precision == 16 && (query.encoding == AUDIO_ENCODING_SLINEAR_BE || query.encoding == AUDIO_ENCODING_SLINEAR_LE)) dsp_samplesize = 16; if (!dsp_samplesize && query.precision == 8 && query.encoding == AUDIO_ENCODING_ULINEAR) dsp_samplesize = 8; } } if (dsp_samplesize == 0) end_all("Sorry, no audio format supported by this binary is available"); if (!f) f = 22050; { struct audio_info current; AUDIO_INITINFO(¤t); switch(dsp_samplesize) { case 8: dsize = 1; current.play.encoding = AUDIO_ENCODING_ULINEAR; current.play.precision= 8; break; case 16: dsize = 2; current.play.encoding = AUDIO_ENCODING_SLINEAR; current.play.precision= 16; break; default: end_all("Error: unknown dsp_samplesize"); } current.play.sample_rate = f; current.play.channels = s ? 2 : 1; { int ok = ioctl(audio, AUDIO_SETINFO, ¤t); /* maybe we are trying some stunt our card can't do ? * Try lowering our expectations */ if (ok == -1 && current.play.channels == 2 && current.play.sample_rate > 22050) { current.play.sample_rate = 22050; ok = ioctl(audio, AUDIO_SETINFO, ¤t); } if (ok == -1 && current.play.channels == 2) { current.play.channels = 1; ok = ioctl(audio, AUDIO_SETINFO, ¤t); } while (ok == -1 && current.play.sample_rate > 8000) { current.play.sample_rate /= 2; ok = ioctl(audio, AUDIO_SETINFO, ¤t); } if (ok == -1) end_all("Can't find a suitable format"); if (ioctl(audio, AUDIO_GETINFO, ¤t) == -1) end_all("Error retrieving format"); } buf_max = current.blocksize; current_freq = current.play.sample_rate; stereo = current.play.channels == 2 ? 1 : 0; } buffer = malloc(buf_max); buffer16 = (short *)buffer; idx = 0; samples_max = buf_max / dsize; set_watched_scalar(FREQUENCY, current_freq); total = 0; return current_freq; } /* synchronize stuff with audio output */ LOCAL struct tagged { struct tagged *next; /* simply linked list */ void (*f)(GENERIC p); /* function to call */ void (*f2)(GENERIC p); /* function to call for flush */ GENERIC p; /* and parameter */ unsigned long when; /* number of bytes to let go before calling */ } *start, /* what still to output */ *end; /* where to add new tags */ /* flush_tags: use tags that have gone by recently */ LOCAL void flush_tags(void) { audio_offset_t off; if (audio != -1) ioctl(audio, AUDIO_GETOOFFS, &off); if (start) { while (start && start->when <= off.samples + ADVANCE_TAGS) { struct tagged *tofree; (*start->f)(start->p); tofree = start; start = start->next; free(tofree); } } } /* remove unused tags at end */ LOCAL void remove_pending_tags(void) { while (start) { struct tagged *tofree; (*start->f2)(start->p); tofree = start; start = start->next; free(tofree); } } void sync_audio(void (*function)(void *p), void (*f2)(void *p), void *parameter) { struct tagged *t; if (audio != -1) { t = malloc(sizeof(struct tagged)); if (!t) { (*function)(parameter); return; } /* build new tag */ t->next = 0; t->f = function; t->f2 = f2; t->p = parameter; t->when = total; /* add it to list */ if (start) end->next = t; else start = t; end = t; /* set up for next tag */ } else (*function)(parameter); } LOCAL void actually_flush_buffer(void) { int l,i; if (idx) { total += idx * dsize; write(audio, buffer, dsize * idx); } idx = 0; } void output_samples(long left, long right, int n) { if (idx >= samples_max - 1) actually_flush_buffer(); switch(dsp_samplesize) { case 16: /* Cool! 16 bits samples */ add_samples16(left, right, n); break; case 8: add_samples8(left, right, n); break; default: /* should not happen */ } } void flush_buffer(void) { actually_flush_buffer(); flush_tags(); } /* * Closing the Linux sound device waits for all pending samples to play. */ void close_audio(void) { actually_flush_buffer(); close(audio); free(buffer); } unsigned long update_frequency(void) { return 0; } void discard_buffer(void) { #if 0 if (audio) ioctl(audio, AUDIO_FLUSH, 0); #endif remove_pending_tags(); total = 0; } void audio_ui(char c) { }