openbsd-ports/audio/tracker/files/audio.c
1999-03-05 16:25:49 +00:00

283 lines
5.5 KiB
C

/* openbsd/audio.c
vi:ts=3 sw=3:
*/
/* Native BSD interface */
#include "defs.h"
#include <unistd.h>
#include <fcntl.h>
#include "extern.h"
#include "prefs.h"
#include "autoinit.h"
#include "watched_var.h"
#include <sys/ioctl.h>
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 <sys/types.h>
#include <sys/audioio.h>
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(&current);
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, &current);
/* 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, &current);
}
if (ok == -1 && current.play.channels == 2)
{
current.play.channels = 1;
ok = ioctl(audio, AUDIO_SETINFO, &current);
}
while (ok == -1 && current.play.sample_rate > 8000)
{
current.play.sample_rate /= 2;
ok = ioctl(audio, AUDIO_SETINFO, &current);
}
if (ok == -1)
end_all("Can't find a suitable format");
if (ioctl(audio, AUDIO_GETINFO, &current) == -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)
{
}