/*
Copyright (c) 1995,1996,1997 Nick Ing-Simmons. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
*/
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <signal.h>
#include "../../Data/Audio.m"
AudioVtab *AudioVptr;
#include <sys/ioctl.h>
#ifdef HAVE_SYS_IOCCOM_H
#include <sys/ioccom.h>
#endif
#ifdef HAVE_SYS_AUDIOIO_H
#include <sys/audioio.h>
#endif
#ifdef HAVE_SUN_AUDIOIO_H
#include <sun/audioio.h>
#endif
#define SAMP_RATE 8000
typedef struct
{
audio_info_t info;
int fd;
int kind;
} play_audio_t;
static char *dev_file = "/dev/audio";
audio_open(play_audio_t *dev, int Wait)
{
/* Try it quickly, first */
Zero(dev, 1, play_audio_t);
dev->fd = open(dev_file, O_WRONLY | O_NDELAY);
if ((dev->fd < 0) && (errno == EBUSY))
{
if (!Wait)
{
croak("%s is busy\n", dev_file);
}
/* Now hang until it's open */
dev->fd = open(dev_file, O_WRONLY);
}
else if (dev->fd >= 0)
{
int flags = fcntl(dev->fd, F_GETFL, NULL);
if (flags >= 0)
fcntl(dev->fd, F_SETFL, flags & ~O_NDELAY);
else
perror("fcntl - F_GETFL");
}
if (dev->fd < 0)
{
return 0;
}
else
{
#ifdef AUDIO_DEV_AMD
/* Get the device output encoding configuration */
if (ioctl(dev->fd, AUDIO_GETDEV, &dev->kind))
{
/* Old releases of SunOs don't support the ioctl,
but can only be run on old machines which have AMD device...
*/
dev->kind = AUDIO_DEV_AMD;
}
#endif
if (ioctl(dev->fd, AUDIO_GETINFO, &dev->info) != 0)
{
return 0;
}
}
return 1;
}
static int
audio_setinfo(play_audio_t *dev)
{
return (ioctl(dev->fd, AUDIO_SETINFO, &dev->info) != 0);
}
IV
audio_rate(play_audio_t *dev,IV rate)
{
IV prev_rate = dev->info.play.sample_rate;
if (rate)
{
dev->info.play.sample_rate = rate;
#ifdef AUDIO_ENCODING_LINEAR
if (rate > 8000)
{
dev->info.play.encoding = AUDIO_ENCODING_LINEAR;
dev->info.play.precision = 16;
}
else
{
dev->info.play.encoding = AUDIO_ENCODING_ULAW;
dev->info.play.precision = 8;
}
#endif
audio_setinfo(dev);
}
return prev_rate;
}
float
audio_gain(play_audio_t *dev,float gain)
{
float prev_gain = ((float) dev->info.play.gain)/AUDIO_MAX_GAIN;
if (gain >= 0.0)
{
dev->info.play.gain = (unsigned) (AUDIO_MAX_GAIN * gain);
audio_setinfo(dev);
}
return prev_gain;
}
IV
audio_speaker(play_audio_t *dev,int flag)
{
IV old = (dev->info.play.port & AUDIO_SPEAKER) != 0;
if (flag)
{
if (flag > 0)
dev->info.play.port |= AUDIO_SPEAKER;
else
dev->info.play.port &= ~AUDIO_SPEAKER;
audio_setinfo(dev);
}
return old;
}
IV
audio_headphone(play_audio_t *dev,int flag)
{
IV old = (dev->info.play.port & AUDIO_HEADPHONE) != 0;
if (flag)
{
if (flag > 0)
dev->info.play.port |= AUDIO_HEADPHONE;
else
dev->info.play.port &= ~AUDIO_HEADPHONE;
audio_setinfo(dev);
}
return old;
}
void
audio_flush(play_audio_t *dev)
{
ioctl(dev->fd, AUDIO_DRAIN, 0);
}
void
audio_DESTROY(play_audio_t *dev)
{
/* Close audio system */
if (dev->fd >= 0)
{
audio_flush(dev);
close(dev->fd);
dev->fd = -1;
}
}
void
audio_play16(play_audio_t *dev, int n, short *data)
{
if (n > 0 && dev->fd >= 0)
{
#ifdef AUDIO_ENCODING_LINEAR
if (dev->info.play.encoding == AUDIO_ENCODING_LINEAR)
{
unsigned size = n * sizeof(short);
if (write(dev->fd, data, n * sizeof(short)) != size)
perror("write");
}
else
#endif
if (dev->info.play.encoding == AUDIO_ENCODING_ULAW)
{
unsigned char *plabuf = (unsigned char *) malloc(n);
if (plabuf)
{
int w;
unsigned char *p = plabuf;
unsigned char *e = p + n;
while (p < e)
{
*p++ = short2ulaw(*data++);
}
p = plabuf;
while ((w = write(dev->fd, p, n)) != n)
{
if (w == -1)
{
fprintf(stderr, "%d,%s:%d\n", errno, __FILE__, __LINE__);
perror("audio");
abort();
}
else
{
fprintf(stderr, "Writing %u, only wrote %u\n", n, w);
p += w;
n -= w;
}
}
free(plabuf);
}
else
{
croak("No memory for ulaw data");
}
}
}
}
void
audio_play(play_audio_t *dev, Audio *au, float volume)
{
STRLEN samp = Audio_samples(au);
SV *tmp = Audio_shorts(au);
if (volume >= 0)
audio_gain(dev, volume);
if (au->rate != audio_rate(dev,0))
audio_rate(dev, au->rate);
audio_play16(dev, samp, (short *) SvPVX(tmp));
SvREFCNT_dec(tmp);
}
MODULE = Audio::Play::#OSNAME# PACKAGE=Audio::Play::#OSNAME# PREFIX = audio_
PROTOTYPES: DISABLE
play_audio_t *
audio_new(class,wait = 1)
char * class
IV wait
CODE:
{static play_audio_t buf;
if (!audio_open(RETVAL = &buf,wait))
{
XSRETURN_NO;
}
}
OUTPUT:
RETVAL
void
audio_DESTROY(dev)
play_audio_t * dev
void
audio_flush(dev)
play_audio_t * dev
IV
audio_speaker(dev,flag = 0)
play_audio_t * dev
IV flag
IV
audio_headphone(dev,flag = 0)
play_audio_t * dev
IV flag
float
audio_gain(dev,val = -1.0)
play_audio_t * dev
float val
IV
audio_rate(dev,rate = 0)
play_audio_t * dev
IV rate
void
audio_play(dev, au, vol = -1.0)
play_audio_t * dev
Audio * au;
float vol
BOOT:
{
AudioVptr = (AudioVtab *) SvIV(perl_get_sv("Audio::Data::AudioVtab",5));
}