/* Original written for 'rsynth' and placed in public domain by by B. Stuyts 21-feb-94. Perl modifications Copyright (c) 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 #include #include #ifdef HAVE_LIBC_H #include #endif #include /* Nested dynamic loaded extension magic ... */ #include "../../Data/Audio.m" AudioVtab *AudioVptr; /* Platform specific C level "object" data structure */ typedef struct { SNDSoundStruct *sound; long samp_rate; float gain; } play_audio_t; int audio_init(play_audio_t *dev,int wait) { dev->samp_rate = SND_RATE_CODEC; dev->sound = NULL; dev->gain = 1.0; return 1; } void audio_play16(play_audio_t *dev, int n, short *data) { if (n > 0) { SNDSoundStruct *sound = dev->sound; int err; if (!sound) { /* I hate these magic numbers - NI-S */ err = SNDAlloc(&dev->sound, 1000000, SND_FORMAT_LINEAR_16, dev->samp_rate, 1, 0); if (err) croak("SNDAlloc:%s",SNDSoundError(err)); sound = dev->sound; } else { /* Wait for previous sound to finish before changing fields in sound */ /* I hate these magic numbers - NI-S */ err = SNDWait(0); if (err) croak("SNDWait:%s",SNDSoundError(err)); } /* copying to buffer is a pain - should really convert into the buffer, unless "double buffer" means it it easier to keep up... */ sound->dataSize = n * sizeof(short); /* Patch from benstn@olivetti.nl (Ben Stuyts) Thanks to ugubser@avalon.unizh.ch for finding out why the NEXTSTEP version of rsynth didn't work on Intel systems. As suspected, it was a byte-order problem. */ #if i386 swab((char *) data, (char *) sound + sound->dataLocation, n * sizeof(short)); #else /* i386 */ bcopy(data, (char *) sound + sound->dataLocation, n * sizeof(short)); #endif /* I hate these magic numbers - NI-S */ err = SNDStartPlaying(sound, 1, 5, 0, 0, 0); if (err) croak("SNDStartPlaying:%s",SNDSoundError(err)); } } void audio_flush(play_audio_t *dev) { /* I hate these magic numbers - NI-S */ inr err = SNDWait(0); if (err) croak("SNDWait:%s",SNDSoundError(err)); } void audio_DESTROY(play_audio_t *dev) { SNDSoundStruct *sound = dev->sound; int err; if (!sound) return; audio_flush(dev); if (err = SNDFree(sound)) croak("SNDFree:%s",SNDSoundError(err)); dev->sound = NULL; } IV audio_rate(play_audio_t *dev,IV rate) { IV old = dev->samp_rate; if (rate) { /* rate != 0 is setting the rate */ if (dev->sound) { /* In progress - wait for it, and free that one - re-use DESTROY code */ audio_DESTROY(dev); } /* Note rate for allocation on next call to audio_play16() */ dev->samp_rate = rate; } return old; } float audio_gain(play_audio_t *dev,float gain) { float prev_gain = dev->gain; if (gain >= 0.0) { if (gain != 1.0) warn("Cannot change audio gain"); /* If you can tell me how, otherwise we could multiply out during conversion to short. ... NI-S */ } return prev_gain; } /* API level Play function - volume may go from the interface - it is un-natural - convert to 'short' should be done at Audio::Play level - likewise rate-matching needs to be higher level */ 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); /* Or re-sample to dev's rate ??? */ audio_play16(dev, samp, (short *) SvPVX(tmp)); SvREFCNT_dec(tmp); } /* Methods to select speaker and/or headphones etc. need adding if possible ... List of "valid" sample rates would be good too. */ 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_init(RETVAL = &buf,wait)) { XSRETURN_NO; } } OUTPUT: RETVAL void audio_DESTROY(dev) play_audio_t * dev void audio_flush(dev) play_audio_t * dev 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: { /* Nested dynamic loaded extension magic ... */ AudioVptr = (AudioVtab *) SvIV(perl_get_sv("Audio::Data::AudioVtab",5)); }