From fca7a8fa47b60fb80108d4302c11872c05020331 Mon Sep 17 00:00:00 2001 From: curt Date: Thu, 16 Jul 1998 17:48:11 +0000 Subject: [PATCH] Initial revision. --- Audio/CHANGES | 119 ++++++++ Audio/Makefile.am | 1 + Audio/NOTICE | 9 + Audio/README | 9 + Audio/README.freebsd | 13 + Audio/README.linux | 13 + Audio/README.openbsd | 13 + Audio/README.sgi | 13 + Audio/README.unix | 20 ++ Audio/README.win | 15 + example/Makefile.am | 13 + example/example.cxx | 89 ++++++ src/Makefile.am | 12 + src/sl.h | 635 +++++++++++++++++++++++++++++++++++++++ src/slDSP.cxx | 663 +++++++++++++++++++++++++++++++++++++++++ src/slEnvelope.cxx | 92 ++++++ src/slPortability.h | 72 +++++ src/slSample.cxx | 505 +++++++++++++++++++++++++++++++ src/slSamplePlayer.cxx | 150 ++++++++++ src/slScheduler.cxx | 370 +++++++++++++++++++++++ src/sm.h | 88 ++++++ src/smMixer.cxx | 258 ++++++++++++++++ 22 files changed, 3172 insertions(+) create mode 100644 Audio/CHANGES create mode 100644 Audio/Makefile.am create mode 100644 Audio/NOTICE create mode 100644 Audio/README create mode 100644 Audio/README.freebsd create mode 100644 Audio/README.linux create mode 100644 Audio/README.openbsd create mode 100644 Audio/README.sgi create mode 100644 Audio/README.unix create mode 100644 Audio/README.win create mode 100644 example/Makefile.am create mode 100644 example/example.cxx create mode 100644 src/Makefile.am create mode 100644 src/sl.h create mode 100644 src/slDSP.cxx create mode 100644 src/slEnvelope.cxx create mode 100644 src/slPortability.h create mode 100644 src/slSample.cxx create mode 100644 src/slSamplePlayer.cxx create mode 100644 src/slScheduler.cxx create mode 100644 src/sm.h create mode 100644 src/smMixer.cxx diff --git a/Audio/CHANGES b/Audio/CHANGES new file mode 100644 index 00000000..7c6a79a2 --- /dev/null +++ b/Audio/CHANGES @@ -0,0 +1,119 @@ + +/**********************************************\ +* * +* W A R N I N G * +* * +* This file is now kept in reverse chronolog- * +* ical order so recent changes are now at the * +* top. * +* * +\**********************************************/ + +* 6th July 1998 -- Fixed an initialisation problem when + slScheduler was not a static/global. + + Tom Knienieder's port to SGI/IRIX is now + working, documentation updated to reflect that. + +* 16th June 1998 -- Added some slPortability.h fixes for + FreeBSD and the Cygnus WIN32 compiler. + Many thanks to Curt. + +* 14th June 1998 -- Tom Knienieder's port to OpenBSD is now + working, documentation updated to reflect that. + Tom's improved Makefiles included, also some + example sound samples that were accidentally + left out of the release are now present. + A couple of typo's in the WIN32 section + have been fixed. The top level Makefile + now requires you to type 'make linux', + 'make win' or 'make openbsd'. + +* 13th June 1998 -- Tom Knienieder's port to WIN32 engine is now + working, documentation updated to reflect that + revised status. Some default constructor parameters + have changed, slDSP no longer supports setRate/setBps/setStereo. + You now have to delete the slDSP and recreate it with + new parameters. This makes porting a little easier. + 'sound_test' renamed 'example'. + +* 7th June 1998 -- Volume envelopes (and inverse volume envelopes) + now work correctly. Pan envelopes won't work + until stereo is implemented. Pitch and filter + envelopes turn out to be a major pain to implement + with the present slSceduler/slSamplePlayer interface, + so some significant internal changes are to be + expected. + + Changed the CHANGES file to be in reverse + chronological order. + + This version is officially SL v0.3 (beta) + +* 3rd June 1998 -- Moved sample program and it's data files into + 'example', moved documents into 'doc' and sources + into 'src'. Final library goes into 'lib'. + + The entire preempting mechanism was broken - + now it's fixed. + + Added a callback mechanism that allows + applications to know when a sound + loops, finishes playing, is pre-empted, etc. + + New mechanisms added to stop/pause/resume a + playing sample. + + All the documentation - and some of the code - + for slEnvelopes has been added, they don't + work yet - so don't bother with them for now. + + Made some code a little more bullet-proof. + slSample's are now reference-counted so you + can't accidentally delete one while it's + playing without getting a FATAL error. + +* 2nd June 1998 -- Fixed bug in initialisation that prevented SL + from functioning correctly in the case were there + is no sound card present. + + This version is officially SL v0.2 (beta) + +* 1st June 1998 -- Split library into two parts - libsm and + libsl. libsm contains only the Mixer class + since it is likely to be hard to port to + a lot of non-OSS systems - and most programs + won't need it anyway. Hence the documentation + has blossomed into three files and all the + 'slMixer' references have turned into 'smMixer'. + Also, I finally got a hold of the OSS documentation, + which is a lot more complete - and straightened + me out on a few points. slDSP has changed + (internally) somewhat as a result and in particular, + you can no longer mess with the sampling rate, + stereo and bps settings after the slDSP or + slScheduler has been created. This also allows the + scheduler to enforce it's rule about only mono/8bps + operations. + + I also added an 'autoMatch' function to the slSample + class to automagically match incoming samples to the + current slDSP/slScheduler. This makes using the library + a lot less painful and error-prone. + + This version is officially SL v0.1 (beta) + + We need a better name! + +* 30th May 1998 -- Almost total rewrite, library can now + play multiple sounds without interruption, + supports '.WAV' and '.AU' file formats as + well as raw binary files. Able to copy with + much shorter safetyMargin on sound buffers, + and play without using the 'stop' call. + All class and external symbols now begin + with 'sl' or 'SL'. HTML documentation now + available. + +* 27th May 1998 -- First hack + diff --git a/Audio/Makefile.am b/Audio/Makefile.am new file mode 100644 index 00000000..835577f8 --- /dev/null +++ b/Audio/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src example diff --git a/Audio/NOTICE b/Audio/NOTICE new file mode 100644 index 00000000..ba95093a --- /dev/null +++ b/Audio/NOTICE @@ -0,0 +1,9 @@ +NOTICE: This Sound Library (SL) distribution contains source code that is +placed into the public domain without copyright. These programs are freely +distributable without licensing fees. These programs are provided without +guarantee or warrantee expressed or implied. + +If you use SL in a commercial or shareware product, it would be nice if you +gave credit where it is due. If you make any modifications or improvements +to SL, I would greatly appreciate a copy of the improved code. + diff --git a/Audio/README b/Audio/README new file mode 100644 index 00000000..8bf6b4da --- /dev/null +++ b/Audio/README @@ -0,0 +1,9 @@ + +Hi! + + This is the fifth prototype of Steve's 'SL' sound library. + + Check out 'CHANGES' and the new HTML documentation. + +Steve + diff --git a/Audio/README.freebsd b/Audio/README.freebsd new file mode 100644 index 00000000..540c00e4 --- /dev/null +++ b/Audio/README.freebsd @@ -0,0 +1,13 @@ + +Building SL for Linux. +~~~~~~~~~~~~~~~~~~~~~~ + + % make freebsd + % su root + % make install + +...that's all folks. + +Header files go into /usr/include/SL (analogous to /usr/include/GL for graphics) +Library file(s) go into /usr/lib + diff --git a/Audio/README.linux b/Audio/README.linux new file mode 100644 index 00000000..7df00e8f --- /dev/null +++ b/Audio/README.linux @@ -0,0 +1,13 @@ + +Building SL for Linux. +~~~~~~~~~~~~~~~~~~~~~~ + + % make linux + % su root + % make install + +...that's all folks. + +Header files go into /usr/include/SL (analogous to /usr/include/GL for graphics) +Library file(s) go into /usr/lib + diff --git a/Audio/README.openbsd b/Audio/README.openbsd new file mode 100644 index 00000000..9ef788f3 --- /dev/null +++ b/Audio/README.openbsd @@ -0,0 +1,13 @@ + +Building SL for OpenBSD. +~~~~~~~~~~~~~~~~~~~~~~~~ + + % make openbsd + % su root + % make install + +...that's all folks. + +Header files go into /usr/include/SL (analogous to /usr/include/GL for graphics) +Library file(s) go into /usr/lib + diff --git a/Audio/README.sgi b/Audio/README.sgi new file mode 100644 index 00000000..844c9c90 --- /dev/null +++ b/Audio/README.sgi @@ -0,0 +1,13 @@ + +Building SL for SGI. +~~~~~~~~~~~~~~~~~~~~~~ + + % make sgi + % su root + % make install + +...that's all folks. + +Header files go into /usr/include/SL (analogous to /usr/include/GL for graphics) +Library file(s) go into /usr/lib + diff --git a/Audio/README.unix b/Audio/README.unix new file mode 100644 index 00000000..771e856d --- /dev/null +++ b/Audio/README.unix @@ -0,0 +1,20 @@ +Building SL for UNIX +~~~~~~~~~~~~~~~~~~~~ + +If your UNIX box is Linux or OpenBSD then +check out README.linux or README.openbsd. + +If your UNIX box supports OSS (the Open +Sound System) then in principal, you should +only need to type: + + % make oss + % su root + % make install + +...however, your milage may vary. If you succeed +in getting a non-Linux, non-OpenBSD version to +work, I'd like to hear about it. + + Steve Baker + diff --git a/Audio/README.win b/Audio/README.win new file mode 100644 index 00000000..a9d52822 --- /dev/null +++ b/Audio/README.win @@ -0,0 +1,15 @@ + +Building SL for win32 (msvc) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + C:>nmake win32 + + +don't forget to set the environment ! + +example: + +set include=c:\msdev\include; +set lib=c:\msdev\lib + +path c:\msdev\bin;c:\bin;c:\winnt;...... diff --git a/example/Makefile.am b/example/Makefile.am new file mode 100644 index 00000000..bc0d25e1 --- /dev/null +++ b/example/Makefile.am @@ -0,0 +1,13 @@ +bin_PROGRAMS = example + +example_SOURCES = example.cxx + +example_LDADD = \ + $(top_builddir)/Lib/Audio/src/libsl.la \ + $(top_builddir)/Lib/Audio/src/libsm.la + +INCLUDES += -I$(top_builddir)/Lib/Audio/src + +if ENABLE_WIN32_AUDIO +LIBS += -lwinmm +endif diff --git a/example/example.cxx b/example/example.cxx new file mode 100644 index 00000000..ae519786 --- /dev/null +++ b/example/example.cxx @@ -0,0 +1,89 @@ + + +#include "sl.h" +#include "sm.h" +#include + +/* + Construct a sound scheduler and a mixer. +*/ + +slScheduler sched ( 8000 ) ; +smMixer mixer ; + +int main () +{ + mixer . setMasterVolume ( 30 ) ; + sched . setSafetyMargin ( 0.128 ) ; + + /* Just for fun, let's make a one second synthetic engine sample... */ + + Uchar buffer [ 8000 ] ; + + for ( int i = 0 ; i < 8000 ; i++ ) + { + /* Sum some sin waves and convert to range 0..1 */ + + float level = ( sin ( (double) i * 2.0 * M_PI / (8000.0/ 50.0) ) + + sin ( (double) i * 2.0 * M_PI / (8000.0/149.0) ) + + sin ( (double) i * 2.0 * M_PI / (8000.0/152.0) ) + + sin ( (double) i * 2.0 * M_PI / (8000.0/192.0) ) + ) / 8.0f + 0.5f ; + + /* Convert to unsigned byte */ + + buffer [ i ] = (Uchar) ( level * 255.0 ) ; + } + + /* Set up four samples and a loop */ + + slSample *s = new slSample ( buffer, 8000 ) ; + slSample *s1 = new slSample ( "scream.ub", & sched ) ; + slSample *s2 = new slSample ( "zzap.wav" , & sched ) ; + slSample *s3 = new slSample ( "cuckoo.au", & sched ) ; + slSample *s4 = new slSample ( "wheeee.ub", & sched ) ; + + /* Mess about with some of the samples... */ + + s1 -> adjustVolume ( 2.2 ) ; + s2 -> adjustVolume ( 0.5 ) ; + s3 -> adjustVolume ( 0.2 ) ; + + /* Play the engine sample continuously. */ + + sched . loopSample ( s ) ; + + int tim = 0 ; /* My periodic event timer. */ + + while ( SL_TRUE ) + { + tim++ ; /* Time passes */ + + if ( tim % 200 == 0 ) sched.playSample ( s1 ) ; + if ( tim % 180 == 0 ) sched.playSample ( s2 ) ; + if ( tim % 150 == 0 ) sched.playSample ( s3 ) ; + if ( tim % 120 == 0 ) sched.playSample ( s4 ) ; + + /* + For the sake of realism, I'll delay for 1/30th second to + simulate a graphics update process. + */ + +#ifdef WIN32 + Sleep ( 1000 / 30 ) ; /* 30Hz */ +#elif defined(sgi) + sginap( 3 ); /* ARG */ +#else + usleep ( 1000000 / 30 ) ; /* 30Hz */ +#endif + + /* + This would normally be called just before the graphics buffer swap + - but it could be anywhere where it's guaranteed to get called + fairly often. + */ + + sched . update () ; + } +} + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..72aa7dff --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,12 @@ +libdir = ${exec_prefix}/lib + +lib_LTLIBRARIES = libsl.la libsm.la + +libsl_la_SOURCES = \ + sl.h slPortability.h \ + slDSP.cxx slSample.cxx slEnvelope.cxx \ + slSamplePlayer.cxx slScheduler.cxx + +libsm_la_SOURCES = sm.h slPortability.h smMixer.cxx + +INCLUDES += -I$(top_builddir) -I. diff --git a/src/sl.h b/src/sl.h new file mode 100644 index 00000000..79b63e65 --- /dev/null +++ b/src/sl.h @@ -0,0 +1,635 @@ + +#ifndef __SL_H__ +#define __SL_H__ 1 + +#include "slPortability.h" + +#ifdef SL_USING_OSS_AUDIO +#define SLDSP_DEFAULT_DEVICE "/dev/dsp" +#elif defined(WIN32) +#define SLDSP_DEFAULT_DEVICE "dsp" +#elif defined(__OpenBSD__) +#define SLDSP_DEFAULT_DEVICE "/dev/audio" +#elif defined(sgi) +#define SLDSP_DEFAULT_DEVICE "dsp" // dummy ... +#else +#error "Port me !" +#endif + +# define SL_TRUE 1 +# define SL_FALSE 0 + +typedef unsigned char Uchar ; +typedef unsigned short Ushort ; + +#define SL_DEFAULT_SAMPLING_RATE 11025 + +class slSample ; +class slSamplePlayer ; +class slEnvelope ; +class slScheduler ; +class slDSP ; + +class slDSP +{ +private: + + int stereo ; + int rate ; + int bps ; + + int error ; + int fd ; + +#ifdef __OpenBSD__ + audio_info_t ainfo; // ioctl structure + audio_offset_t audio_offset; // offset in audiostream + long counter; // counter-written packets +#elif defined(SL_USING_OSS_AUDIO) + audio_buf_info buff_info ; +#elif defined(sgi) + ALconfig config; // configuration stuff + ALport port; // .. we are here +#endif + + +#ifndef WIN32 + int ioctl ( int cmd, int param = 0 ) + { + if ( error ) return param ; + + if ( ::ioctl ( fd, cmd, & param ) == -1 ) + { + perror ( "slDSP: ioctl" ) ; + error = SL_TRUE ; + } + + return param ; + } + +#elif defined(WIN32) + + HWAVEOUT hWaveOut; // device handle + WAVEFORMATEX Format; // open needs this + MMTIME mmt; // timing + WAVEHDR wavehdr[ 3 ]; // for round robin .. + int curr_header; // index of actual wavehdr + long counter; // counter-written packets + +#endif + + void open ( char *device, int _rate, int _stereo, int _bps ) ; + void close () ; + void getBufferInfo () ; + void write ( void *buffer, size_t length ) ; + +protected: + + void setError () { error = SL_TRUE ; } + int getDriverBufferSize () ; + +public: + + slDSP ( int _rate = SL_DEFAULT_SAMPLING_RATE, + int _stereo = SL_FALSE, int _bps = 8 ) + { + open ( SLDSP_DEFAULT_DEVICE, _rate, _stereo, _bps ) ; + } + + slDSP ( char *device, int _rate = SL_DEFAULT_SAMPLING_RATE, + int _stereo = SL_FALSE, int _bps = 8 ) + { + open ( device, _rate, _stereo, _bps ) ; + } + + ~slDSP () { close () ; } + + float secondsRemaining () ; + float secondsUsed () ; + + void play ( void *buffer, size_t length ) { write ( buffer, length ) ; } + + int not_working () { return error ; } + + int getBps () { return bps ; } + int getRate () { return rate ; } + int getStereo() { return stereo ; } + + void sync () ; + void stop () ; +} ; + + +class slSample +{ + int ref_count ; +protected: + + char *comment; + int rate ; + int bps ; + int stereo ; + + Uchar *buffer ; + int length ; + + void init () + { + ref_count = 0 ; + comment = NULL ; + buffer = NULL ; + length = 0 ; + rate = SL_DEFAULT_SAMPLING_RATE ; + bps = 8 ; + stereo = SL_FALSE ; + } + +public: + + slSample () { init () ; } + + slSample ( Uchar *buff, int leng ) + { + init () ; + setBuffer ( buff, leng ) ; + } + + slSample ( char *fname, class slDSP *dsp = NULL ) + { + init () ; + loadFile ( fname ) ; + autoMatch ( dsp ) ; + } + + ~slSample () + { + if ( ref_count != 0 ) + { + fprintf ( stderr, + "slSample: FATAL ERROR - Application deleted a sample while it was playing.\n" ) ; + exit ( 1 ) ; + } + + delete buffer ; + } + + void ref () { ref_count++ ; } + void unRef () { ref_count-- ; } + + int getPlayCount () { return ref_count ; } + + char *getComment () { return comment ; } + + void setComment ( char *nc ) + { + delete comment ; + comment = new char [ strlen ( nc ) + 1 ] ; + strcpy ( comment, nc ) ; + } + + Uchar *getBuffer () { return buffer ; } + int getLength () { return length ; } + + void autoMatch ( slDSP *dsp ) ; + + void setBuffer ( Uchar *buff, int leng ) + { + delete buffer ; + + buffer = new Uchar [ leng ] ; + + if ( buff != NULL ) + memcpy ( buffer, buff, leng ) ; + + length = leng ; + } + + /* These routines only set flags - use changeXXX () to convert a sound */ + + void setRate ( int r ) { rate = r ; } + void setBps ( int b ) { bps = b ; } + void setStereo ( int s ) { stereo = s ; } + + int getRate () { return rate ; } + int getBps () { return bps ; } + int getStereo () { return stereo ; } + + float getDuration () { return (float) getLength() / + (float) ( (getStereo()?2.0f:1.0f)* + (getBps()/8.0f)*getRate() ) ; } + + int loadFile ( char *fname ) ; + + int loadRawFile ( char *fname ) ; + int loadAUFile ( char *fname ) ; + int loadWavFile ( char *fname ) ; + + void changeRate ( int r ) ; + void changeBps ( int b ) ; + void changeStereo ( int s ) ; + + void adjustVolume ( float vol ) ; + + void print ( FILE *fd ) + { + if ( buffer == NULL ) + { + fprintf ( fd, "Empty sample buffer\n" ) ; + } + else + { + fprintf ( fd, "\"%s\"\n",(getComment() == NULL || + getComment()[0]=='\0') ? "Sample" : comment ) ; + fprintf ( fd, "%s, %d bits per sample.\n", + getStereo() ? "Stereo" : "Mono", getBps() ) ; + fprintf ( fd, "%gKHz sample rate.\n", (float) getRate() / 1000.0f ) ; + fprintf ( fd, "%d bytes of samples == %g seconds duration.\n", getLength(), getDuration() ) ; + } + } +} ; + + +enum slSampleStatus +{ + SL_SAMPLE_WAITING, /* Sound hasn't started playing yet */ + SL_SAMPLE_RUNNING, /* Sound has started playing */ + SL_SAMPLE_DONE , /* Sound is complete */ + SL_SAMPLE_PAUSED /* Sound hasn't started playing yet */ +} ; + + +enum slPreemptMode +{ + SL_SAMPLE_CONTINUE, /* Don't allow yourself to be preempted */ + SL_SAMPLE_ABORT , /* Abort playing the sound when preempted */ + SL_SAMPLE_RESTART , /* Restart the sound when load permits */ + SL_SAMPLE_MUTE , /* Continue silently until load permits */ + SL_SAMPLE_DELAY /* Pause until load permits */ +} ; + + +enum slReplayMode +{ + SL_SAMPLE_LOOP, /* Loop sound so that it plays forever */ + SL_SAMPLE_ONE_SHOT /* Play sound just once */ +} ; + +enum slEvent +{ + SL_EVENT_COMPLETE, /* Sound finished playing */ + SL_EVENT_LOOPED, /* Sound looped back to the start */ + SL_EVENT_PREEMPTED /* Sound was preempted */ +} ; + +typedef void (*slCallBack) ( slSample *, slEvent, int ) ; + +class slEnvelope +{ + float *time ; + float *value ; + int nsteps ; + int ref_count ; + + slReplayMode replay_mode ; + + int getStepDelta ( float *_time, float *delta ) ; + +public: + + slEnvelope ( int _nsteps, slReplayMode _rm, float *_times, float *_values ) + { + ref_count = 0 ; + nsteps = _nsteps ; + time = new float [ nsteps ] ; + value = new float [ nsteps ] ; + memcpy ( time , _times , sizeof(float) * nsteps ) ; + memcpy ( value, _values, sizeof(float) * nsteps ) ; + + replay_mode = _rm ; + } + + + slEnvelope ( int _nsteps = 1, slReplayMode _rm = SL_SAMPLE_ONE_SHOT ) + { + ref_count = 0 ; + nsteps = _nsteps ; + time = new float [ nsteps ] ; + value = new float [ nsteps ] ; + + for ( int i = 0 ; i < nsteps ; i++ ) + time [ i ] = value [ i ] = 0.0 ; + + replay_mode = _rm ; + } + + ~slEnvelope () + { + if ( ref_count != 0 ) + { + fprintf ( stderr, + "slEnvelope: FATAL ERROR - Application deleted an envelope while it was playing.\n" ) ; + exit ( 1 ) ; + } + + delete time ; + delete value ; + } + + void ref () { ref_count++ ; } + void unRef () { ref_count-- ; } + + int getPlayCount () { return ref_count ; } + + void setStep ( int n, float _time, float _value ) + { + if ( n >= 0 && n < nsteps ) + { + time [ n ] = _time ; + value [ n ] = _value ; + } + } + + float getStepValue ( int s ) { return value [ s ] ; } + float getStepTime ( int s ) { return time [ s ] ; } + + int getNumSteps () { return nsteps ; } + + float getValue ( float _time ) ; + + void applyToVolume ( Uchar *dst, Uchar *src, int nframes, int start ) ; + void applyToInvVolume ( Uchar *dst, Uchar *src, int nframes, int start ) ; +} ; + +#define SL_MAX_PRIORITY 16 +#define SL_MAX_SAMPLES 16 +#define SL_MAX_CALLBACKS (SL_MAX_SAMPLES * 2) +#define SL_MAX_ENVELOPES 4 + +enum slEnvelopeType +{ + SL_PITCH_ENVELOPE , SL_INVERSE_PITCH_ENVELOPE , + SL_VOLUME_ENVELOPE, SL_INVERSE_VOLUME_ENVELOPE, + SL_FILTER_ENVELOPE, SL_INVERSE_FILTER_ENVELOPE, + SL_PAN_ENVELOPE , SL_INVERSE_PAN_ENVELOPE , + SL_ECHO_ENVELOPE , SL_INVERSE_ECHO_ENVELOPE , + + SL_NULL_ENVELOPE +} ; + +struct slPendingCallBack +{ + slCallBack callback ; + slSample *sample ; + slEvent event ; + int magic ; +} ; + +class slSamplePlayer +{ + int lengthRemaining ; + Uchar *bufferPos ; + slSample *sample ; + + slEnvelope *env [ SL_MAX_ENVELOPES ] ; + slEnvelopeType env_type [ SL_MAX_ENVELOPES ] ; + int env_start_time [ SL_MAX_ENVELOPES ] ; + + slReplayMode replay_mode ; + slPreemptMode preempt_mode ; + slSampleStatus status ; + int priority ; + + slCallBack callback ; + int magic ; + +public: + + slSamplePlayer ( slSample *s, slReplayMode rp_mode = SL_SAMPLE_ONE_SHOT, + int pri = 0, slPreemptMode pr_mode = SL_SAMPLE_DELAY, + int _magic = 0, slCallBack cb = NULL ) + { + magic = _magic ; + sample = s ; + callback = cb ; + + for ( int i = 0 ; i < SL_MAX_ENVELOPES ; i++ ) + { + env [ i ] = NULL ; + env_type [ i ] = SL_NULL_ENVELOPE ; + } + + if ( sample ) sample -> ref () ; + + reset () ; + + replay_mode = rp_mode ; + preempt_mode = pr_mode ; + priority = pri ; + } + + ~slSamplePlayer () ; + + int getAmountLeft () + { + return lengthRemaining ; + } + + slPreemptMode getPreemptMode () { return preempt_mode ; } + + int getPriority () + { + return ( isRunning() && + preempt_mode == SL_SAMPLE_CONTINUE ) ? (SL_MAX_PRIORITY+1) : + priority ; + } + + int preempt ( int delay ) ; + + void addEnvelope ( int i, slEnvelope *_env, slEnvelopeType _type ) ; + + void pause () + { + if ( status != SL_SAMPLE_DONE ) + status = SL_SAMPLE_PAUSED ; + } + + void resume () + { + if ( status == SL_SAMPLE_PAUSED ) + status = SL_SAMPLE_RUNNING ; + } + + void reset () + { + status = SL_SAMPLE_WAITING ; + lengthRemaining = sample->getLength () ; + bufferPos = sample->getBuffer () ; + } + + void start () + { + status = SL_SAMPLE_RUNNING ; + lengthRemaining = sample->getLength () ; + bufferPos = sample->getBuffer () ; + } + + void stop () + { + status = SL_SAMPLE_DONE ; + lengthRemaining = 0 ; + bufferPos = NULL ; + } + + int getMagic () { return magic ; } + slSample *getSample () { return sample ; } + + int isWaiting () { return status == SL_SAMPLE_WAITING ; } + int isPaused () { return status == SL_SAMPLE_PAUSED ; } + int isRunning () { return status == SL_SAMPLE_RUNNING ; } + int isDone () { return status == SL_SAMPLE_DONE ; } + + void skip ( int nframes ) ; + Uchar *read ( int nframes, Uchar *spare1, Uchar *spare2 ) ; +} ; + + +class slScheduler : public slDSP +{ + slPendingCallBack pending_callback [ SL_MAX_CALLBACKS ] ; + int num_pending_callbacks ; + + float safety_margin ; + int mixer_buffer_size ; + Uchar *mixer_buffer ; + Uchar *mixer ; + int amount_left ; + + slSamplePlayer *samplePlayer [ SL_MAX_SAMPLES ] ; + + Uchar *spare_buffer1 [ 3 ] ; + Uchar *spare_buffer2 [ 3 ] ; + + void init () ; + + Uchar *mergeBlock ( Uchar *d ) ; + Uchar *mergeBlock ( Uchar *d, slSamplePlayer *spa ) ; + Uchar *mergeBlock ( Uchar *d, slSamplePlayer *spa, + slSamplePlayer *spb ) ; + Uchar *mergeBlock ( Uchar *d, slSamplePlayer *spa, + slSamplePlayer *spb, + slSamplePlayer *spc ) ; + void mixBuffer () ; + void mixBuffer ( slSamplePlayer *a ) ; + void mixBuffer ( slSamplePlayer *a, + slSamplePlayer *b ) ; + void mixBuffer ( slSamplePlayer *a, + slSamplePlayer *b, + slSamplePlayer *c ) ; + + Uchar mix ( Uchar a, Uchar b ) + { + register int r = a + b - 0x80 ; + return ( r > 255 ) ? 255 : + ( r < 0 ) ? 0 : r ; + } + + Uchar mix ( Uchar a, Uchar b, Uchar c ) + { + register int r = a + b + c - 0x80 - 0x80 ; + return ( r > 255 ) ? 255 : + ( r < 0 ) ? 0 : r ; + } + + void realUpdate ( int dump_first = SL_FALSE ) ; + + void initBuffers () ; + + int now ; + + static slScheduler *current ; + +public: + + slScheduler ( int _rate = SL_DEFAULT_SAMPLING_RATE ) : slDSP ( _rate, SL_FALSE, 8 ) { init () ; } + slScheduler ( char *device, + int _rate = SL_DEFAULT_SAMPLING_RATE ) : slDSP ( device, _rate, SL_FALSE, 8 ) { init () ; } + ~slScheduler () ; + + static slScheduler *getCurrent () { return current ; } + + int getTimeNow () { return now ; } + float getElapsedTime ( int then ) { return (float)(now-then)/(float)getRate() ; } + + void flushCallBacks () ; + void addCallBack ( slCallBack c, slSample *s, slEvent e, int m ) ; + + void update () { realUpdate ( SL_FALSE ) ; } + void dumpUpdate () { realUpdate ( SL_TRUE ) ; } + + void addSampleEnvelope ( slSample *s = NULL, int magic = 0, + int slot = 1, slEnvelope *e = NULL, + slEnvelopeType t = SL_VOLUME_ENVELOPE ) + { + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( samplePlayer [ i ] != NULL && + ( s == NULL || samplePlayer [ i ] -> getSample () == s ) && + ( magic == 0 || samplePlayer [ i ] -> getMagic () == magic ) ) + samplePlayer [ i ] -> addEnvelope ( slot, e, t ) ; + } + + void resumeSample ( slSample *s = NULL, int magic = 0 ) + { + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( samplePlayer [ i ] != NULL && + ( s == NULL || samplePlayer [ i ] -> getSample () == s ) && + ( magic == 0 || samplePlayer [ i ] -> getMagic () == magic ) ) + samplePlayer [ i ] -> resume () ; + } + + void pauseSample ( slSample *s = NULL, int magic = 0 ) + { + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( samplePlayer [ i ] != NULL && + ( s == NULL || samplePlayer [ i ] -> getSample () == s ) && + ( magic == 0 || samplePlayer [ i ] -> getMagic () == magic ) ) + samplePlayer [ i ] -> pause () ; + } + + void stopSample ( slSample *s = NULL, int magic = 0 ) + { + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( samplePlayer [ i ] != NULL && + ( s == NULL || samplePlayer [ i ] -> getSample () == s ) && + ( magic == 0 || samplePlayer [ i ] -> getMagic () == magic ) ) + samplePlayer [ i ] -> stop () ; + } + + int loopSample ( slSample *s, int pri = 0, slPreemptMode mode = SL_SAMPLE_MUTE, int magic = 0, slCallBack cb = NULL ) + { + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( samplePlayer [ i ] == NULL ) + { + samplePlayer [ i ] = new slSamplePlayer ( s, SL_SAMPLE_LOOP, pri, mode, magic, cb ) ; + return i ; + } + + return -1 ; + } + + int playSample ( slSample *s, int pri = 1, slPreemptMode mode = SL_SAMPLE_ABORT, int magic = 0, slCallBack cb = NULL ) + { + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + if ( samplePlayer [ i ] == NULL ) + { + samplePlayer [ i ] = new slSamplePlayer ( s, SL_SAMPLE_ONE_SHOT, pri, mode, magic, cb ) ; + return SL_TRUE ; + } + + return SL_FALSE ; + } + + void setSafetyMargin ( float seconds ) { safety_margin = seconds ; } +} ; + +#endif + diff --git a/src/slDSP.cxx b/src/slDSP.cxx new file mode 100644 index 00000000..fbb0da32 --- /dev/null +++ b/src/slDSP.cxx @@ -0,0 +1,663 @@ + +#include "sl.h" + +static int init_bytes ; + +#ifdef SL_USING_OSS_AUDIO + +/* ------------------------------------------------------------ */ +/* OSSAUDIO - Linux, FreeBSD, etc */ +/* ------------------------------------------------------------ */ + +void slDSP::open ( char *device, int _rate, int _stereo, int _bps ) +{ + fd = ::open ( device, O_WRONLY ) ; + + if ( fd < 0 ) + { + perror ( "slDSP: open" ) ; + error = SL_TRUE ; + + stereo = SL_FALSE ; + bps = 1 ; + rate = 8000 ; + init_bytes = 0 ; + } + else + { + error = SL_FALSE ; + + /* Set up a driver fragment size of 1024 (ie 2^10) */ + + ioctl ( SNDCTL_DSP_SETFRAGMENT, 0x7FFF000A ) ; + + stereo = ioctl ( SOUND_PCM_WRITE_CHANNELS, _stereo ? 2 : 1 ) >= 2 ; + bps = ioctl ( SOUND_PCM_WRITE_BITS, _bps ) ; + rate = ioctl ( SOUND_PCM_WRITE_RATE, _rate ) ; + + getBufferInfo () ; + init_bytes = buff_info.bytes ; + } +} + + +void slDSP::close () +{ + if ( fd >= 0 ) + ::close ( fd ) ; +} + + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + getBufferInfo () ; + return buff_info.fragsize ; +} + +void slDSP::getBufferInfo () +{ + if ( error ) + return ; + + if ( ::ioctl ( fd, SNDCTL_DSP_GETOSPACE, & buff_info ) < 0 ) + { + perror ( "slDSP: getBufferInfo" ) ; + error = SL_TRUE ; + return ; + } +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + if ( error || length <= 0 ) + return ; + + int nwritten = ::write ( fd, (const char *) buffer, length ) ; + + if ( nwritten < 0 ) + perror ( "slDSP: write" ) ; + else + if ( nwritten != length ) + perror ( "slDSP: short write" ) ; +} + + +float slDSP::secondsRemaining () +{ + if ( error ) + return 0.0f ; + + getBufferInfo () ; + + int samples_left = buff_info.fragments * buff_info.fragsize ; + + if ( stereo ) samples_left /= 2 ; + if ( bps == 16 ) samples_left /= 2 ; + return (float) samples_left / (float) rate ; +} + + +float slDSP::secondsUsed () +{ + if ( error ) + return 0.0f ; + + getBufferInfo () ; + + int samples_used = init_bytes - buff_info.bytes ; + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + return (float) samples_used / (float) rate ; +} + + +void slDSP::sync () +{ + if ( !error) ::ioctl ( fd, SOUND_PCM_SYNC , 0 ) ; +} + +void slDSP::stop () +{ + if ( !error) ::ioctl ( fd, SOUND_PCM_RESET, 0 ) ; +} + +#endif + +#ifdef WIN32 + +/* ------------------------------------------------------------ */ +/* win32 */ +/* ------------------------------------------------------------ */ + +static void wperror(MMRESULT num) +{ + char buffer[0xff]; // yes, this is hardcoded :-) + + waveOutGetErrorText( num, buffer, sizeof(buffer)-1); + + fprintf( stderr, "SlDSP: %s\n", buffer ); + fflush ( stderr ); +} + + + +void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + switch( uMsg ) + { + case WOM_CLOSE: + break; + + case WOM_OPEN: + break; + + case WOM_DONE: + waveOutUnprepareHeader( (HWAVEOUT)dwParam1, + (LPWAVEHDR)dwParam2, sizeof( WAVEHDR )); + break; + } +} + + +void slDSP::open ( char *device, int _rate, int _stereo, int _bps ) +{ + MMRESULT result; + + hWaveOut = NULL; + curr_header = 0; + counter = 0; + + Format.wFormatTag = WAVE_FORMAT_PCM; + Format.nChannels = _stereo ? 2 : 1; + Format.nSamplesPerSec = _rate; + Format.wBitsPerSample = _bps; + Format.nBlockAlign = 1; + Format.nAvgBytesPerSec = _rate * Format.nChannels; + Format.cbSize = 0; + + result = waveOutOpen( & hWaveOut, WAVE_MAPPER, & Format, NULL, + 0L, WAVE_FORMAT_QUERY ); + + if ( result != MMSYSERR_NOERROR ) + { + wperror( result); + + error = SL_TRUE ; + stereo = SL_FALSE ; + bps = _bps ; + rate = _rate ; + init_bytes = 0 ; + + return; + } + + // Now the hwaveouthandle "should" be valid + + if ( ( result = waveOutOpen( & hWaveOut, WAVE_MAPPER, + (WAVEFORMATEX *)& Format, (DWORD)waveOutProc, + 0L, CALLBACK_FUNCTION )) != MMSYSERR_NOERROR ) + { + wperror( result); + + error = SL_TRUE ; + stereo = SL_FALSE ; + bps = _bps ; + rate = _rate ; + init_bytes = 0 ; + return; + } + else + { + error = SL_FALSE ; + stereo = _stereo; + bps = _bps; + rate = _rate; + + /* hmm ?! */ + + init_bytes = 1024*8; + } +} + + +void slDSP::close () +{ + if ( hWaveOut != NULL ) + { + waveOutClose( hWaveOut ); + hWaveOut = NULL; + } +} + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + /* hmm ?! */ + + return 1024*8; +} + +void slDSP::getBufferInfo () +{ + return ; +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + MMRESULT result; + + if ( error || length <= 0 ) + return ; + + wavehdr[curr_header].lpData = (LPSTR) buffer; + wavehdr[curr_header].dwBufferLength = (long) length; + wavehdr[curr_header].dwBytesRecorded = 0L; + wavehdr[curr_header].dwUser = NULL; + wavehdr[curr_header].dwFlags = 0; + wavehdr[curr_header].dwLoops = 0; + wavehdr[curr_header].lpNext = NULL; + wavehdr[curr_header].reserved = 0; + + + result = waveOutPrepareHeader( hWaveOut, & wavehdr[curr_header], + sizeof(WAVEHDR)); + + if ( result != MMSYSERR_NOERROR ) + { + wperror ( result ); + error = SL_TRUE; + } + + result = waveOutWrite(hWaveOut, & wavehdr[curr_header], + sizeof(WAVEHDR)); + if ( result != MMSYSERR_NOERROR ) + { + wperror ( result ); + error = SL_TRUE; + } + + counter ++; + + curr_header = ( curr_header + 1 ) % 3; +} + + +float slDSP::secondsRemaining () +{ + if ( error ) + return 0.0f ; + + return 0.0f ; +} + + +float slDSP::secondsUsed () +{ + int samples_used; + MMRESULT result; + float samp_time; + + if ( error ) + return 0.0f ; + + mmt.wType = TIME_BYTES; + + result = waveOutGetPosition( hWaveOut, &mmt, sizeof( mmt )); + + if ( mmt.u.cb == 0 || counter == 0) + return (float)0.0; + + samples_used = ( init_bytes * counter ) - mmt.u.cb; + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + + samp_time = (float) samples_used / (float) rate ; + + //printf("%0.2f position=%ld total written=%ld\n", + // samp_time, mmt.u.cb, init_bytes * counter ); + + return samp_time; +} + + +void slDSP::sync () +{ +} + +void slDSP::stop () +{ + waveOutReset( hWaveOut ); +} + +/* ------------------------------------------------------------ */ +/* OpenBSD 2.3 this should be very close to SUN Audio */ +/* ------------------------------------------------------------ */ + +#elif defined(__OpenBSD__) +void slDSP::open ( char *device, int _rate, int _stereo, int _bps ) +{ + + counter = 0; + + fd = ::open ( device, O_RDWR ) ; + + if ( fd < 0 ) + { + perror ( "slDSP: open" ) ; + error = SL_TRUE ; + } + else + { + + if( ::ioctl( fd, AUDIO_GETINFO, &ainfo) == -1) + { + perror("slDSP: open - getinfo"); + stereo = SL_FALSE ; + bps = 8 ; + rate = 8000 ; + init_bytes = 0 ; + + return; + } + + ainfo.play.sample_rate = _rate; + ainfo.play.precision = _bps; + ainfo.play.channels = _stereo ? 2 : 1; + + ainfo.play.encoding = AUDIO_ENCODING_ULINEAR; + + if( :: ioctl(fd, AUDIO_SETINFO, &ainfo) == -1) + { + perror("slDSP: open - setinfo"); + stereo = SL_FALSE ; + bps = 8 ; + rate = 8000 ; + init_bytes = 0 ; + return; + } + + rate = _rate; + stereo = _stereo; + bps = _bps; + + error = SL_FALSE ; + + getBufferInfo (); + + // I could not change the size, + // so let's try this ... + + init_bytes = 1024 * 8; + } +} + + +void slDSP::close () +{ + if ( fd >= 0 ) + ::close ( fd ) ; +} + + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + getBufferInfo () ; + + // HW buffer is 0xffff on my box + //return ainfo.play.buffer_size; + + return 1024 * 8; +} + +void slDSP::getBufferInfo () +{ + if ( error ) + return ; + + if( ::ioctl( fd, AUDIO_GETINFO, &ainfo) < 0) + { + perror ( "slDSP: getBufferInfo" ) ; + error = SL_TRUE ; + return ; + } + + if( ::ioctl( fd, AUDIO_GETOOFFS, &audio_offset ) < 0) + { + perror ( "slDSP: getBufferInfo" ) ; + error = SL_TRUE ; + return ; + } +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + if ( error || length <= 0 ) + return ; + + int nwritten = ::write ( fd, (const char *) buffer, length ) ; + + if ( nwritten < 0 ) + perror ( "slDSP: write" ) ; + else if ( nwritten != length ) + perror ( "slDSP: short write" ) ; + + counter ++; /* hmmm */ +} + + +float slDSP::secondsRemaining () +{ + return 0.0f ; +} + + +float slDSP::secondsUsed () +{ + /* + * original formula from Steve: + * ----------------------------- + * + * int samples_used = init_bytes - buff_info.bytes ; + * | | + * | +--- current available + * | space in bytes ! + * +---------------- available space + * when empty; + * + * sample_used contains the number of bytes which are + * "used" or in the DSP "pipeline". + */ + + + int samples_used; + + if ( error ) + return 0.0f ; + + getBufferInfo () ; + + //This is wrong: this is the hw queue in the kernel ! + //samples_used = ainfo.play.buffer_size - audio_offset.offset ; + + // This is: all data written minus where we are now in the queue + + if ( counter == 0 ) + return 0.0; + + samples_used = ( counter * init_bytes ) - audio_offset.samples; + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + + return (float) samples_used / (float) rate ; +} + + +void slDSP::sync () +{ + if ( !error) ::ioctl ( fd, AUDIO_FLUSH , 0 ) ; +} + +void slDSP::stop () +{ + // nothing found yet +} + +/* ------------------------------------------------------------ */ +/* SGI IRIX audio */ +/* ------------------------------------------------------------ */ + +#elif defined(sgi) + +void slDSP::open ( char *device, int _rate, int _stereo, int _bps ) +{ + if ( _bps != 8 ) + { + perror ( "slDSP: supports only 8bit audio for sgi" ) ; + error = SL_TRUE; + return; + } + + init_bytes = 1024 * 8; + + config = ALnewconfig(); + + ALsetchannels ( config, _stereo ? AL_STEREO : AL_MONO ); + ALsetwidth ( config, _bps == 8 ? AL_SAMPLE_8 : AL_SAMPLE_16 ); + ALsetqueuesize( config, init_bytes ); + + port = ALopenport( device, "w", config ); + + if ( port == NULL ) + { + perror ( "slDSP: open" ) ; + error = SL_TRUE ; + } + else + { + long params[2] = {AL_OUTPUT_RATE, 0 }; + + params[1] = _rate; + + if ( ALsetparams(AL_DEFAULT_DEVICE, params, 2) != 0 ) + { + perror ( "slDSP: open - ALsetparams" ) ; + error = SL_TRUE ; + return; + } + + rate = _rate; + stereo = _stereo; + bps = _bps; + + error = SL_FALSE ; + + } +} + + +void slDSP::close () +{ + if ( port != NULL ) + { + ALcloseport ( port ); + ALfreeconfig( config ); + port = NULL; + } +} + + +int slDSP::getDriverBufferSize () +{ + if ( error ) + return 0 ; + + return ALgetqueuesize( config ); +} + +void slDSP::getBufferInfo () +{ + if ( error ) + return ; +} + + +void slDSP::write ( void *buffer, size_t length ) +{ + char *buf = (char *)buffer; + int i; + + if ( error || length <= 0 ) + return ; + + // Steve: is this a problem ?? + + for ( i = 0; i < length; i ++ ) + buf[i] = buf[i] >> 1; + + ALwritesamps(port, (void *)buf, length ); +} + + +float slDSP::secondsRemaining () +{ + int samples_remain; + + if ( error ) + return 0.0f ; + + samples_remain = ALgetfillable(port); + + if ( stereo ) samples_remain /= 2 ; + if ( bps == 16 ) samples_remain /= 2 ; + + return (float) samples_remain / (float) rate ; +} + + +float slDSP::secondsUsed () +{ + int samples_used; + + if ( error ) + return 0.0f ; + + samples_used = ALgetfilled(port); + + if ( stereo ) samples_used /= 2 ; + if ( bps == 16 ) samples_used /= 2 ; + + return (float) samples_used / (float) rate ; +} + + +void slDSP::sync () +{ + /* found this in the header file - but no description + * or example for the long parameter. + */ + + // ALflush(ALport, long); +} + +void slDSP::stop () +{ +} + + +#endif + diff --git a/src/slEnvelope.cxx b/src/slEnvelope.cxx new file mode 100644 index 00000000..74cc52b0 --- /dev/null +++ b/src/slEnvelope.cxx @@ -0,0 +1,92 @@ + +#include "sl.h" + +float slEnvelope::getValue ( float _time ) +{ + float delta ; + int step = getStepDelta ( &_time, &delta ) ; + + return delta * (_time - time[step]) + value[step] ; +} + + + +int slEnvelope::getStepDelta ( float *_time, float *delta ) +{ + float tt ; + + if ( replay_mode == SL_SAMPLE_LOOP ) + { + tt = floor ( *_time / time [ nsteps-1 ] ) ; + *_time -= tt * time [ nsteps-1 ] ; + } + + tt = *_time ; + + if ( tt <= time[ 0 ] ) { *delta = 0.0f ; return 0 ; } + if ( tt >= time[nsteps-1] ) { *delta = 0.0f ; return nsteps-1 ; } + + for ( int i = 1 ; i <= nsteps-1 ; i++ ) + if ( tt <= time[i] ) + { + float t1 = time[i-1] ; float v1 = value[i-1] ; + float t2 = time[ i ] ; float v2 = value[ i ] ; + + if ( t1 == t2 ) + { + *delta = 0.0f ; + return i ; + } + + *delta = (v2-v1) / (t2-t1) ; + return i-1 ; + } + + *delta = 0.0f ; + return nsteps - 1 ; +} + +void slEnvelope::applyToVolume ( Uchar *dst, Uchar *src, + int nframes, int start ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + while ( nframes-- ) + { + register int res = (int)( (float)((int)*(src++)-0x80) * _value ) + 0x80 ; + + _value += delta ; + + *(dst++) = ( res > 255 ) ? 255 : ( res < 0 ) ? 0 : res ; + } +} + +void slEnvelope::applyToInvVolume ( Uchar *dst, Uchar *src, + int nframes, int start ) +{ + float delta ; + float _time = slScheduler::getCurrent() -> getElapsedTime ( start ) ; + int step = getStepDelta ( &_time, &delta ) ; + float _value = delta * (_time - time[step]) + value[step] ; + + delta /= (float) slScheduler::getCurrent() -> getRate () ; + + delta = - delta ; + _value = 1.0 - _value ; + + while ( nframes-- ) + { + register int res = (int)( (float)((int)*(src++)-0x80) * _value ) + 0x80 ; + + _value += delta ; + + *(dst++) = ( res > 255 ) ? 255 : ( res < 0 ) ? 0 : res ; + } +} + + diff --git a/src/slPortability.h b/src/slPortability.h new file mode 100644 index 00000000..09953a5f --- /dev/null +++ b/src/slPortability.h @@ -0,0 +1,72 @@ + +#ifndef __SLPORTABILITY_H__ +#define __SLPORTABILITY_H__ 1 + +/* ------------------------------------------------------------- */ +/* OS specific includes and defines ... */ +/* ------------------------------------------------------------- */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#undef VERSION + +#include +#include + +#ifndef WIN32 +#include +#include +#else +#include +#ifdef __CYGWIN32__ +# define NEAR /* */ +# define FAR /* */ +# define WHERE_EVER_YOU_ARE /* Curt: optional, but it reminds me of a song */ +#endif +#include +#endif + +#include +#include +#include +#include + +#ifdef __linux__ +#define SL_USING_OSS_AUDIO 1 +#endif + +#ifdef SL_USING_OSS_AUDIO +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#else +/* + Tom thinks this file may be under some + unixen - but that isn't where the OSS manuals say it + should be. + + If you ever find out the truth, please email me: + Steve Baker +*/ +#include +#endif +#endif + +#ifdef __OpenBSD__ +#include +#endif + +#ifdef WIN32 +#define strcasecmp stricmp /* Yes, Steve really does *HATE* Windoze */ +#endif + +/* Tom */ + +#ifdef sgi +#include +#endif + +#endif + diff --git a/src/slSample.cxx b/src/slSample.cxx new file mode 100644 index 00000000..14c6b537 --- /dev/null +++ b/src/slSample.cxx @@ -0,0 +1,505 @@ + + +#include "sl.h" +#include + +void slSample::autoMatch ( slDSP *dsp ) +{ + if ( dsp == NULL ) return ; + + changeRate ( dsp->getRate () ) ; + changeBps ( dsp->getBps () ) ; + changeStereo ( dsp->getStereo () ) ; +} + +void slSample::adjustVolume ( float vol ) +{ + for ( int i = 0 ; i < length ; i++ ) + { + int s = (int)(((float) buffer[i] - (float) 0x80) * vol) + 0x80 ; + + buffer [ i ] = ( s > 255 ) ? 255 : + ( s < 0 ) ? 0 : s ; + } +} + + +void slSample::changeRate ( int r ) +{ + if ( r == rate ) return ; + + int length1 = length / (getBps ()/8) ; + int length2 = (int) ( (float) length1 * ( (float) r / (float) rate ) ) ; + Uchar *buffer2 = new Uchar [ length2 ] ; + + float step = (float) length1 / (float) length2 ; + + for ( int i = 0 ; i < length2 / (getBps()/8); i++ ) + { + float pos = (float) i * step ; + + int p1 = (int) floor ( pos ) ; + int p2 = (int) ceil ( pos ) ; + + if ( stereo ) + { + if ( ( p1 & 1 ) != ( i & 1 ) ) { pos++ ; p1++ ; p2++ ; } + p2++ ; + } + + float ratio = pos - (float) p1 ; + + float b1 = (getBps()==8) ? + (float) buffer [(p1<0)?0:(p1>=length1)?length1-1:p1] : + (float) ((Ushort*)buffer)[(p1<0)?0:(p1>=length1)?length1-1:p1] ; + float b2 = (getBps()==8) ? + (float) buffer [(p2<0)?0:(p2>=length1)?length1-1:p2] : + (float) ((Ushort*)buffer)[(p2<0)?0:(p2>=length1)?length1-1:p2] ; + + float res = b1 * (1.0-ratio) + b2 * ratio ; + + if ( getBps () == 8 ) + buffer2 [ i ] = (Uchar) ( (res < 0) ? 0 : (res > 255) ? 255 : res ) ; + else + ((Ushort *) buffer2 ) [ i ] = + (Ushort) ( (res < 0) ? 0 : (res > 65535) ? 65535 : res ) ; + } + + rate = r ; + length = length2 ; + delete buffer ; + buffer = buffer2 ; +} + +#ifdef 0 +void slSample::changeToUnsigned () +{ + if ( getBps() == 16 ) + { + int length2 = length / 2 ; + Ushort *buffer2 = (Ushort *) buffer ; + + for ( int i = 0 ; i < length2 ; i++ ) + buffer2 [ i ] = buffer2 [ i ] + 32768 ; + } + else + { + for ( int i = 0 ; i < length ; i++ ) + buffer [ i ] = (buffer [ i ]>0x80) ? (buffer[i]-0x80) : + (0xFF-buffer[i]) ; + } +} +#endif + + +void slSample::changeBps ( int b ) +{ + if ( b == getBps () ) return ; + + if ( b == 8 && getBps() == 16 ) + { + length /= 2 ; + Uchar *buffer2 = new Uchar [ length ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i ] = ((Ushort *)buffer) [ i ] >> 8 ; + + delete buffer ; + buffer = buffer2 ; + setBps ( b ) ; + } + else + if ( b == 16 && getBps() == 8 ) + { + Ushort *buffer2 = new Ushort [ length ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i ] = buffer [ i ] << 8 ; + + delete buffer ; + buffer = (Uchar *) buffer2 ; + length *= 2 ; + setBps ( b ) ; + } +} + +void slSample::changeStereo ( int s ) +{ + if ( s == getStereo () ) + return ; + + if ( s && ! getStereo () ) + { + if ( getBps () == 8 ) + { + Uchar *buffer2 = new Uchar [ length * 2 ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i*2 ] = buffer2 [ i*2+1 ] = buffer [ i ] ; + + delete buffer ; + buffer = buffer2 ; + length *= 2 ; + setStereo ( SL_TRUE ) ; + } + else + { + Ushort *buffer2 = new Ushort [ length ] ; + + for ( int i = 0 ; i < length / 2 ; i++ ) + buffer2 [ i*2 ] = buffer2 [ i*2+1 ] = ((Ushort *) buffer) [ i ] ; + + delete buffer ; + buffer = (Uchar *)buffer2 ; + length *= 2 ; + setStereo ( SL_TRUE ) ; + } + } + else + { + if ( getBps () == 8 ) + { + Uchar *buffer2 = new Uchar [ length / 2 ] ; + + for ( int i = 0 ; i < length ; i++ ) + buffer2 [ i ] = ((int)buffer [ i*2 ] + (int)buffer [ i*2 + 1 ] ) / 2 ; + + delete buffer ; + buffer = buffer2 ; + length /= 2 ; + setStereo ( SL_FALSE ) ; + } + else + { + Ushort *buffer2 = new Ushort [ length / 4 ] ; + + for ( int i = 0 ; i < length / 4 ; i++ ) + buffer2 [ i ] = ((int)((Ushort *)buffer) [ i*2 ] + + (int)((Ushort *)buffer) [ i*2 + 1 ] ) / 2 ; + + delete buffer ; + buffer = (Uchar *)buffer2 ; + length /= 2 ; + setStereo ( SL_FALSE ) ; + } + } +} + + +static void swap_Ushort ( Ushort *i ) +{ + *i = ((*i << 8) & 0xFF00) + + ((*i >> 8) & 0x00FF) ; +} + +static void swap_int ( int *i ) +{ + *i = ((*i << 24) & 0xFF000000) + + ((*i << 8) & 0x00FF0000) + + ((*i >> 8) & 0x0000FF00) + + ((*i >> 24) & 0x000000FF) ; +} + +int slSample::loadFile ( char *fname ) +{ + if ( strcasecmp ( & fname [ strlen ( fname ) - 4 ], ".wav" ) == 0 ) + return loadWavFile ( fname ) ; + + if ( strcasecmp ( & fname [ strlen ( fname ) - 3 ], ".au" ) == 0 ) + return loadAUFile ( fname ) ; + + if ( strcasecmp ( & fname [ strlen ( fname ) - 3 ], ".ub" ) == 0 ) + return loadRawFile ( fname ) ; + + fprintf ( stderr, "slSample:loadFile: Unknown file type for '%s'.\n", + fname ) ; + return SL_FALSE ; +} + + +int slSample::loadWavFile ( char *fname ) +{ + int found_header = SL_FALSE ; + int needs_swabbing = SL_FALSE ; + + delete buffer ; + buffer = NULL ; + length = 0 ; + + FILE *fd = fopen ( fname, "rb" ) ; + + if ( fd == NULL ) + { + fprintf ( stderr, + "slSample: loadWavFile: Cannot open '%s' for reading.\n", + fname ) ; + return SL_FALSE ; + } + + char magic [ 8 ] ; + + if ( fread ( magic, 4, 1, fd ) == -1 || + magic[0] != 'R' || magic[1] != 'I' || + magic[2] != 'F' || magic[3] != 'F' ) + { + fprintf ( stderr, "slWavSample: File '%s' has wrong magic number\n", fname ) ; + fprintf ( stderr, " - it probably isn't in '.wav' format.\n" ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + int leng1 ; + + if ( fread ( & leng1, sizeof(int), 1, fd ) == -1 ) + { + fprintf ( stderr, "slSample: File '%s' has premature EOF in header\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + fread ( magic, 4, 1, fd ) ; + + if ( magic[0] != 'W' || magic[1] != 'A' || + magic[2] != 'V' || magic[3] != 'E' ) + { + fprintf ( stderr, "slSample: File '%s' has no WAVE tag.\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + while ( ! feof ( fd ) ) + { + fread ( magic, 4, 1, fd ) ; + + if ( magic[0] == 'f' && magic[1] == 'm' && + magic[2] == 't' && magic[3] == ' ' ) + { + found_header = SL_TRUE ; + + if ( fread ( & leng1, sizeof(int), 1, fd ) == -1 ) + { + fprintf ( stderr, "slSample: File '%s' has premature EOF in header\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( leng1 > 65536 ) + { + needs_swabbing = SL_TRUE ; + swap_int ( & leng1 ) ; + } + + Ushort header [ 8 ] ; + + if ( leng1 != sizeof ( header ) ) + fprintf ( stderr, + "slSample: File '%s' has unexpectedly long (%d byte) header\n", + fname, leng1 ) ; + + fread ( & header, sizeof(header), 1, fd ) ; + + for ( int junk = sizeof(header) ; junk < leng1 ; junk++ ) + fgetc ( fd ) ; + + if ( needs_swabbing ) + { + swap_Ushort ( & header[0] ) ; + swap_Ushort ( & header[1] ) ; + swap_int ( (int *) & header[2] ) ; + swap_int ( (int *) & header[4] ) ; + swap_Ushort ( & header[6] ) ; + swap_Ushort ( & header[7] ) ; + } + + if ( header [ 0 ] != 0x0001 ) + { + fprintf ( stderr, "slSample: File '%s' is not WAVE_FORMAT_PCM!\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + setStereo ( header[1] > 1 ) ; + setRate ( *((int *) (& header[2])) ) ; + setBps ( header[7] ) ; + } + else + if ( magic[0] == 'd' && magic[1] == 'a' && + magic[2] == 't' && magic[3] == 'a' ) + { + if ( ! found_header ) + { + fprintf ( stderr, "slSample: File '%s' has no data section\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( fread ( & length, sizeof(int), 1, fd ) == -1 ) + { + fprintf ( stderr, "slSample: File '%s' has premature EOF in data\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( needs_swabbing ) + swap_int ( & length ) ; + + buffer = new Uchar [ length ] ; + + fread ( buffer, 1, length, fd ) ; + + if ( getBps () == 16 ) + { + Ushort *b = (Ushort*) buffer ; + + for ( int i = 0 ; i < length/2 ; i++ ) + b [ i ] = (Ushort) ( (int)((short) b [ i ]) + 32768 ) ; + } + + fclose ( fd ) ; + return SL_TRUE ; + } + } + + fclose ( fd ) ; + return SL_FALSE ; +} + +int slSample::loadAUFile ( char *fname ) +{ + delete buffer ; + buffer = NULL ; + length = 0 ; + + FILE *fd = fopen ( fname, "rb" ) ; + + if ( fd == NULL ) + { + fprintf ( stderr, + "slSample: loadAUFile: Cannot open '%s' for reading.\n", + fname ) ; + return SL_FALSE ; + } + + char magic [ 4 ] ; + + if ( fread ( magic, 4, 1, fd ) == -1 || + magic[0] != '.' || magic[1] != 's' || + magic[2] != 'n' || magic[3] != 'd' ) + { + fprintf ( stderr, "slSample: File '%s' has wrong magic number\n", fname ) ; + fprintf ( stderr, " - it probably isn't in '.au' format.\n" ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + int hdr_length ; + int dat_length ; + int nbytes ; + int irate ; + int nchans ; + + if ( fread ( & hdr_length, sizeof(int), 1, fd ) == -1 || + fread ( & dat_length, sizeof(int), 1, fd ) == -1 || + fread ( & nbytes , sizeof(int), 1, fd ) == -1 || + fread ( & irate , sizeof(int), 1, fd ) == -1 || + fread ( & nchans , sizeof(int), 1, fd ) == -1 ) + { + fprintf ( stderr, "slSample: File '%s' has premature EOF in header\n", fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( hdr_length > 65536 ) + { + swap_int ( & hdr_length ) ; + swap_int ( & dat_length ) ; + swap_int ( & nbytes ) ; + swap_int ( & irate ) ; + swap_int ( & nchans ) ; + } + + bps = nbytes * 8 ; + stereo = (nchans>1) ; + rate = irate ; + + if ( nbytes > 2 || nbytes <= 0 || hdr_length > 512 || hdr_length < 24 || + irate > 65526 || irate <= 1000 || nchans < 1 || nchans > 2 ) + { + fprintf ( stderr, "slSample: File '%s' has a very strange header\n", fname ) ; + + fprintf ( stderr, " Header Length = %d\n", hdr_length ) ; + fprintf ( stderr, " Data Length = %d\n", dat_length ) ; + fprintf ( stderr, " Bytes/sample = %d\n", nbytes ) ; + fprintf ( stderr, " Sampling Rate = %dHz\n",irate ) ; + fprintf ( stderr, " Num Channels = %d\n", nchans ) ; + fprintf ( stderr, "\n" ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + if ( hdr_length > 24 ) + { + delete comment ; + comment = new char [ hdr_length - 24 + 1 ] ; + + fread ( comment, 1, hdr_length - 24, fd ) ; + } + + if ( dat_length > 0 ) + { + buffer = new Uchar [ dat_length ] ; + length = fread ( buffer, 1, dat_length, fd ) ; + + if ( length != dat_length ) + fprintf ( stderr, "slAUSample: File '%s' has premature EOF in data.\n", fname ) ; + } + + fclose ( fd ) ; + return SL_TRUE ; +} + + +int slSample::loadRawFile ( char *fname ) +{ + delete buffer ; + buffer = NULL ; + length = 0 ; + + FILE *fd = fopen ( fname, "rb" ) ; + + if ( fd == NULL ) + { + fprintf ( stderr, + "slSample: loadRawFile: Cannot open '%s' for reading.\n", + fname ) ; + return SL_FALSE ; + } + + struct stat stat_buf ; + + if ( fstat ( fileno ( fd ), & stat_buf ) != 0 ) + { + fprintf ( stderr, + "slSample: loadRawFile: Cannot get status for '%s'.\n", + fname ) ; + fclose ( fd ) ; + return SL_FALSE ; + } + + length = stat_buf . st_size ; + + if ( length > 0 ) + { + buffer = new Uchar [ length ] ; + length = fread ( buffer, 1, length, fd ) ; + } + + bps = 8 ; + stereo = SL_FALSE ; + rate = 8000 ; /* Guess */ + + fclose ( fd ) ; + return SL_TRUE ; +} + + diff --git a/src/slSamplePlayer.cxx b/src/slSamplePlayer.cxx new file mode 100644 index 00000000..fa079f4b --- /dev/null +++ b/src/slSamplePlayer.cxx @@ -0,0 +1,150 @@ + +#include "sl.h" + +void slSamplePlayer::addEnvelope ( int i, slEnvelope *_env, slEnvelopeType _type ) +{ + if ( i < 0 || i >= SL_MAX_ENVELOPES ) return ; + + if ( env [ i ] != NULL ) + env [ i ] -> unRef () ; + + env [ i ] = _env ; + + if ( _env != NULL ) + env [ i ] -> ref () ; + + env_type [ i ] = _type ; + env_start_time [ i ] = slScheduler::getCurrent() -> getTimeNow () ; +} + +int slSamplePlayer::preempt ( int delay ) +{ + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_PREEMPTED, magic ) ; + + switch ( preempt_mode ) + { + case SL_SAMPLE_CONTINUE: if ( isRunning() ) + return SL_FALSE ; + /* FALLTHROUGH! */ + case SL_SAMPLE_DELAY : break ; + case SL_SAMPLE_MUTE : skip ( delay ) ; break ; + case SL_SAMPLE_ABORT : stop () ; break ; + case SL_SAMPLE_RESTART : reset () ; break ; + } + + return SL_TRUE ; +} + +slSamplePlayer::~slSamplePlayer () +{ + if ( sample ) + sample -> unRef () ; + + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_COMPLETE, magic ) ; +} + +void slSamplePlayer::skip ( int nframes ) +{ + if ( nframes < lengthRemaining ) + { + lengthRemaining -= nframes ; + bufferPos += nframes ; + } + else + if ( replay_mode == SL_SAMPLE_LOOP ) + { + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_LOOPED, magic ) ; + + nframes -= lengthRemaining ; + + while ( nframes >= sample->getLength () ) + nframes -= sample->getLength () ; + + lengthRemaining = sample->getLength() - nframes ; + bufferPos = & ( sample->getBuffer() [ nframes ] ) ; + } + else + stop () ; +} + + +Uchar *slSamplePlayer::read ( int nframes, Uchar *spare1, Uchar *spare2 ) +{ + if ( isWaiting() ) start () ; + + if ( nframes > lengthRemaining ) /* This is an error */ + { + fprintf ( stderr, "slSamplePlayer: FATAL ERROR - Mixer Requested too much data.\n" ) ; + abort () ; + } + + Uchar *src = bufferPos ; + Uchar *dst = spare1 ; + + for ( int i = 0 ; i < SL_MAX_ENVELOPES ; i++ ) + { + if ( env[i] ) + { + switch ( env_type [ i ] ) + { + case SL_INVERSE_PITCH_ENVELOPE : + case SL_PITCH_ENVELOPE : + memcpy ( dst, src, nframes ) /* Tricky! */ ; + break ; + + case SL_INVERSE_VOLUME_ENVELOPE: + env[i]->applyToInvVolume ( dst,src,nframes,env_start_time[i] ) ; + break ; + + case SL_VOLUME_ENVELOPE : + env[i]->applyToVolume ( dst,src,nframes,env_start_time[i] ) ; + break ; + + case SL_INVERSE_FILTER_ENVELOPE: + case SL_FILTER_ENVELOPE : + memcpy ( dst, src, nframes ) /* Tricky! */ ; + break ; + + case SL_INVERSE_PAN_ENVELOPE : + case SL_PAN_ENVELOPE : + memcpy ( dst, src, nframes ) /* Tricky! */ ; + break ; + + case SL_INVERSE_ECHO_ENVELOPE : + case SL_ECHO_ENVELOPE : + memcpy ( dst, src, nframes ) /* Tricky! */ ; + break ; + } + + if ( dst == spare1 ) + { + src = spare1 ; + dst = spare2 ; + } + else + { + dst = spare1 ; + src = spare2 ; + } + } + } + + if ( nframes < lengthRemaining ) /* Less data than there is left... */ + { + lengthRemaining -= nframes ; + bufferPos += nframes ; + } + else /* Read it all */ + { + if ( replay_mode == SL_SAMPLE_ONE_SHOT ) + stop () ; + else + { + slScheduler::getCurrent() -> addCallBack ( callback, sample, SL_EVENT_LOOPED, magic ) ; + start () ; + } + } + + return src ; +} + diff --git a/src/slScheduler.cxx b/src/slScheduler.cxx new file mode 100644 index 00000000..8edcf03b --- /dev/null +++ b/src/slScheduler.cxx @@ -0,0 +1,370 @@ + +#include "sl.h" + +slScheduler *slScheduler::current = NULL ; + +void slScheduler::init () +{ + current = this ; + + if ( not_working () ) + { + fprintf ( stderr, "slScheduler: soundcard init failed.\n" ) ; + setError () ; + return ; + } + + if ( getBps() != 8 ) + { + fprintf ( stderr, "slScheduler: Needs a sound card that supports 8 bits per sample.\n" ) ; + setError () ; + return ; + } + + if ( getStereo() ) + { + fprintf ( stderr, "slScheduler: Needs a sound card that supports monophonic replay.\n" ) ; + setError () ; + return ; + } + + for ( int i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + samplePlayer [ i ] = NULL ; + + amount_left = 0 ; + now = 0 ; + num_pending_callbacks = 0 ; + safety_margin = 1.0 ; + + mixer = NULL ; + mixer_buffer = NULL ; + spare_buffer1 [ 0 ] = NULL ; + spare_buffer1 [ 1 ] = NULL ; + spare_buffer1 [ 2 ] = NULL ; + spare_buffer2 [ 0 ] = NULL ; + spare_buffer2 [ 1 ] = NULL ; + spare_buffer2 [ 2 ] = NULL ; + + initBuffers () ; +} + +void slScheduler::initBuffers () +{ + if ( not_working () ) return ; + + delete mixer_buffer ; + delete spare_buffer1 [ 0 ] ; + delete spare_buffer1 [ 1 ] ; + delete spare_buffer1 [ 2 ] ; + delete spare_buffer2 [ 0 ] ; + delete spare_buffer2 [ 1 ] ; + delete spare_buffer2 [ 2 ] ; + + mixer_buffer_size = getDriverBufferSize () ; + + mixer_buffer = new Uchar [ mixer_buffer_size ] ; + memset ( mixer_buffer, 0x80, mixer_buffer_size ) ; + + spare_buffer1 [ 0 ] = new Uchar [ mixer_buffer_size ] ; + spare_buffer1 [ 1 ] = new Uchar [ mixer_buffer_size ] ; + spare_buffer1 [ 2 ] = new Uchar [ mixer_buffer_size ] ; + + spare_buffer2 [ 0 ] = new Uchar [ mixer_buffer_size ] ; + spare_buffer2 [ 1 ] = new Uchar [ mixer_buffer_size ] ; + spare_buffer2 [ 2 ] = new Uchar [ mixer_buffer_size ] ; +} + +slScheduler::~slScheduler () +{ + if ( current == this ) + current = NULL ; + + delete mixer_buffer ; + + delete spare_buffer1 [ 0 ] ; + delete spare_buffer1 [ 1 ] ; + delete spare_buffer1 [ 2 ] ; + delete spare_buffer2 [ 0 ] ; + delete spare_buffer2 [ 1 ] ; + delete spare_buffer2 [ 2 ] ; +} + +Uchar *slScheduler::mergeBlock ( Uchar *d ) +{ + register int l = amount_left ; + amount_left = 0 ; + memset ( d, 0x80, l ) ; + + return d + l ; +} + + +Uchar *slScheduler::mergeBlock ( Uchar *d, slSamplePlayer *spa ) +{ + register int l = spa -> getAmountLeft () ; + + if ( l > amount_left ) + l = amount_left ; + + amount_left -= l ; + + memcpy ( d, spa->read(l, spare_buffer1[0], spare_buffer2[0]), l ) ; + + return d + l ; +} + + +Uchar *slScheduler::mergeBlock ( Uchar *d, slSamplePlayer *spa, slSamplePlayer *spb ) +{ + int la = spa -> getAmountLeft () ; + int lb = spb -> getAmountLeft () ; + + register int l = ( la < lb ) ? la : lb ; + + if ( l > amount_left ) + l = amount_left ; + + amount_left -= l ; + + register Uchar *a = spa -> read ( l, spare_buffer1[0], spare_buffer2[0] ) ; + register Uchar *b = spb -> read ( l, spare_buffer1[1], spare_buffer2[1] ) ; + + while ( l-- ) *d++ = mix ( *a++, *b++ ) ; + + return d ; +} + +Uchar *slScheduler::mergeBlock ( Uchar *d, slSamplePlayer *spa, slSamplePlayer *spb, slSamplePlayer *spc ) +{ + int la = spa -> getAmountLeft () ; + int lb = spb -> getAmountLeft () ; + int lc = spc -> getAmountLeft () ; + + register int l = ( la < lb ) ? + (( la < lc ) ? la : lc ) : + (( lb < lc ) ? lb : lc ) ; + + if ( l > amount_left ) + l = amount_left ; + + amount_left -= l ; + + register Uchar *a = spa -> read ( l, spare_buffer1[0], spare_buffer2[0] ) ; + register Uchar *b = spb -> read ( l, spare_buffer1[1], spare_buffer2[1] ) ; + register Uchar *c = spc -> read ( l, spare_buffer1[2], spare_buffer2[2] ) ; + + while ( l-- ) *d++ = mix ( *a++, *b++, *c++ ) ; + + return d ; +} + + +void slScheduler::mixBuffer () +{ + register Uchar *d = mixer_buffer ; + + amount_left = mixer_buffer_size ; + + while ( amount_left > 0 ) + d = mergeBlock ( d ) ; +} + + +void slScheduler::mixBuffer ( slSamplePlayer *spa ) +{ + register Uchar *d = mixer_buffer ; + + amount_left = mixer_buffer_size ; + + while ( amount_left > 0 ) + { + int la = spa -> getAmountLeft () ; + + if ( la > 0 ) /* Buffer has data left... */ + d = mergeBlock ( d, spa ) ; + else /* Buffer is empty */ + d = mergeBlock ( d ) ; + } +} + + +void slScheduler::mixBuffer ( slSamplePlayer *spa, slSamplePlayer *spb ) +{ + register Uchar *d = mixer_buffer ; + amount_left = mixer_buffer_size ; + + while ( amount_left > 0 ) + { + int la = spa -> getAmountLeft () ; + int lb = spb -> getAmountLeft () ; + + if ( la > 0 && lb > 0 ) /* Both buffers have data left... */ + d = mergeBlock ( d, spa, spb ) ; + else + if ( la > 0 && lb <= 0 ) /* Only the A buffer has data left... */ + d = mergeBlock ( d, spa ) ; + else + if ( la <= 0 && lb > 0 ) /* Only the B buffer has data left... */ + d = mergeBlock ( d, spb ) ; + else /* Both buffers are empty */ + d = mergeBlock ( d ) ; + } +} + + + +void slScheduler::mixBuffer ( slSamplePlayer *spa, slSamplePlayer *spb, + slSamplePlayer *spc ) +{ + register Uchar *d = mixer_buffer ; + + amount_left = mixer_buffer_size ; + + while ( amount_left > 0 ) + { + int la = spa -> getAmountLeft () ; + int lb = spb -> getAmountLeft () ; + int lc = spc -> getAmountLeft () ; + + if ( lc > 0 ) /* C buffer has data left... */ + { + if ( la > 0 && lb > 0 ) /* All three buffers have data left... */ + d = mergeBlock ( d, spa, spb, spc ) ; + else + if ( la > 0 && lb <= 0 ) /* Only the A&C buffers have data left... */ + d = mergeBlock ( d, spa, spc ) ; + else + if ( la <= 0 && lb > 0 ) /* Only the B&C buffers have data left... */ + d = mergeBlock ( d, spb, spc ) ; + else /* Only the C buffer has data left */ + d = mergeBlock ( d, spc ) ; + } + else + { + if ( la > 0 && lb > 0 ) /* Only the A&B buffers have data left... */ + d = mergeBlock ( d, spa, spb ) ; + else + if ( la > 0 && lb <= 0 ) /* Only the A buffer has data left... */ + d = mergeBlock ( d, spa ) ; + else + if ( la <= 0 && lb > 0 ) /* Only the B buffer has data left... */ + d = mergeBlock ( d, spb ) ; + else /* All three buffers are empty */ + d = mergeBlock ( d ) ; + } + } +} + + +void slScheduler::realUpdate ( int dump_first ) +{ + int i ; + + if ( not_working () ) + return ; + + while ( secondsUsed() <= safety_margin ) + { + slSamplePlayer *psp [ 3 ] ; + int pri [ 3 ] ; + + pri [ 0 ] = pri [ 1 ] = pri [ 2 ] = -1 ; + + for ( i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + { + if ( samplePlayer [ i ] == NULL ) + continue ; + + /* Clean up dead sample players */ + + if ( samplePlayer [ i ] -> isDone () ) + { + delete samplePlayer [ i ] ; + samplePlayer [ i ] = NULL ; + continue ; + } + + if ( samplePlayer [ i ] -> isPaused () ) + continue ; + + int lowest = ( pri [0] <= pri [2] ) ? + (( pri [0] <= pri [1] ) ? 0 : 1 ) : + (( pri [1] <= pri [2] ) ? 1 : 2 ) ; + + if ( samplePlayer[i]->getPriority() > pri[lowest] ) + { + psp[lowest] = samplePlayer[i] ; + pri[lowest] = samplePlayer[i]->getPriority() ; + } + } + + for ( i = 0 ; i < SL_MAX_SAMPLES ; i++ ) + { + if ( samplePlayer [ i ] == NULL ) + continue ; + + if ( ! samplePlayer [ i ] -> isPaused () && + samplePlayer [ i ] != psp[0] && + samplePlayer [ i ] != psp[1] && + samplePlayer [ i ] != psp[2] ) + { + samplePlayer [ i ] -> preempt ( mixer_buffer_size ) ; + } + } + + if ( pri[0] < 0 ) mixBuffer () ; else + if ( pri[1] < 0 ) mixBuffer ( psp[0] ) ; else + if ( pri[2] < 0 ) mixBuffer ( psp[0], psp[1] ) ; else + mixBuffer ( psp[0], psp[1], psp[2] ) ; + + if ( dump_first ) + { + stop () ; + dump_first = SL_FALSE ; + } + + play ( mixer_buffer, mixer_buffer_size ) ; + + now += mixer_buffer_size ; + } + + flushCallBacks () ; +} + +void slScheduler::addCallBack ( slCallBack c, slSample *s, slEvent e, int m ) +{ + if ( num_pending_callbacks >= SL_MAX_CALLBACKS ) + { + fprintf ( stderr, "slScheduler: Too many pending callback events!\n" ) ; + return ; + } + + slPendingCallBack *p = & ( pending_callback [ num_pending_callbacks++ ] ) ; + + p -> callback = c ; + p -> sample = s ; + p -> event = e ; + p -> magic = m ; +} + +void slScheduler::flushCallBacks () +{ + /* + Execute all the callbacks that we accumulated + in this iteration. + + This is done at the end of 'update' to reduce the risk + of nasty side-effects caused by 'unusual' activities + in the application's callback function. + */ + + while ( num_pending_callbacks > 0 ) + { + slPendingCallBack *p = & ( pending_callback [ --num_pending_callbacks ] ) ; + + if ( p -> callback ) + (*(p->callback))( p->sample, p->event, p->magic ) ; + } +} + + diff --git a/src/sm.h b/src/sm.h new file mode 100644 index 00000000..5e9d50d1 --- /dev/null +++ b/src/sm.h @@ -0,0 +1,88 @@ + +#ifndef __SM_H__ +#define __SM_H__ 1 + +#include "slPortability.h" + +#ifdef SL_USING_OSS_AUDIO +#define SMMIXER_DEFAULT_DEVICE "/dev/mixer" +#elif defined(WIN32) +#define SMMIXER_DEFAULT_DEVICE "mixer" +#else +#endif + + +# define SM_TRUE 1 +# define SM_FALSE 0 + +typedef unsigned char Uchar ; +typedef unsigned short Ushort ; + + +class smMixer +{ +private: + + int devices ; + int error ; + int fd ; + +#ifdef SL_USING_OSS_AUDIO + static char *labels [] = SOUND_DEVICE_LABELS ; + + int ioctl ( int cmd, int param = 0 ) + { + if ( error ) return param ; + + if ( ::ioctl ( fd, cmd, & param ) == -1 ) + { + perror ( "smMixer: ioctl" ) ; + error = SM_TRUE ; + } + + return param ; + } +#endif + void open ( char *device ) ; + void close () ; + +public: + + /* Tom */ + + smMixer (); + smMixer ( char *device ); + ~smMixer (); + + int not_working (); + + /* Volume controls are in integer percentages */ + + int getVolume ( int channel ); + void setVolume ( int channel, int volume ); + + void getVolume ( int channel, int *left, int *right ); + void setVolume ( int channel, int left, int right ); + + void setTreble ( int treble ); + void setBass ( int bass ); + + void setMasterVolume ( int volume ); + void setSynthVolume ( int volume ); + void setPCMVolume ( int volume ); + void setSpeakerVolume( int volume ); + void setLineVolume ( int volume ); + void setMicVolume ( int volume ); + void setCDVolume ( int volume ); + + void setMasterVolume ( int left, int right ); + void setSynthVolume ( int left, int right ); + void setPCMVolume ( int left, int right ); + void setSpeakerVolume( int left, int right ); + void setLineVolume ( int left, int right ); + void setMicVolume ( int left, int right ); + void setCDVolume ( int left, int right ); +} ; + +#endif + diff --git a/src/smMixer.cxx b/src/smMixer.cxx new file mode 100644 index 00000000..4ffaf7e6 --- /dev/null +++ b/src/smMixer.cxx @@ -0,0 +1,258 @@ +#include "sm.h" + +#ifdef SL_USING_OSS_AUDIO +/* ------------------------------------------------------------ */ +/* OSSAUDIO - Linux, FreeBSD */ +/* ------------------------------------------------------------ */ + + +void smMixer::open ( char *device ) +{ + fd = ::open ( device, O_WRONLY ) ; + + if ( fd < 0 ) + { + perror ( "smMixer: open" ) ; + error = SM_TRUE ; + } + else + error = SM_FALSE ; + + devices = ioctl ( SOUND_MIXER_READ_DEVMASK ) ; +} + +void smMixer::close () +{ + if ( fd >= 0 ) + ::close ( fd ) ; +} + + +smMixer::smMixer () +{ + open ( SMMIXER_DEFAULT_DEVICE ) ; +} + +smMixer::smMixer ( char *device ) +{ + open ( device ) ; +} + +smMixer::~smMixer () +{ + close () ; +} + +int smMixer::not_working () +{ + return error ; +} + + /* Volume controls are in integer percentages */ + +int smMixer::getVolume ( int channel ) +{ + return ioctl ( MIXER_READ ( channel ) ) & 0xFF ; +} + + +void smMixer::setVolume ( int channel, int volume ) +{ + ioctl ( MIXER_WRITE ( channel ), (( volume & 255 ) << 8 ) | + ( volume & 255 ) ) ; +} + +void smMixer::getVolume ( int channel, int *left, int *right ) +{ + int vv = ioctl ( MIXER_READ ( channel ) ) ; + + if ( left ) *left = vv & 0xFF ; + if ( right ) *right = (vv>>8) & 0xFF ; +} + +void smMixer::setVolume ( int channel, int left, int right ) +{ + ioctl ( MIXER_WRITE ( channel ), (( right & 255 ) << 8 ) | + ( left & 255 ) ) ; +} + +void smMixer::setTreble ( int treble ) +{ + setVolume ( SOUND_MIXER_TREBLE , treble ) ; +} + +void smMixer::setBass ( int bass ) +{ + setVolume ( SOUND_MIXER_TREBLE , bass ) ; +} + +void smMixer::setMasterVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_VOLUME , volume ) ; +} + +void smMixer::setSynthVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_SYNTH , volume ) ; +} + +void smMixer::setPCMVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_PCM , volume ) ; +} + +void smMixer::setSpeakerVolume( int volume ) +{ + setVolume ( SOUND_MIXER_SPEAKER, volume ) ; +} + +void smMixer::setLineVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_LINE , volume ) ; +} + +void smMixer::setMicVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_MIC , volume ) ; +} + +void smMixer::setCDVolume ( int volume ) +{ + setVolume ( SOUND_MIXER_CD , volume ) ; +} + +void smMixer::setMasterVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_VOLUME , left, right ) ; +} + +void smMixer::setSynthVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_SYNTH , left, right ) ; +} + +void smMixer::setPCMVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_PCM , left, right ) ; +} + +void smMixer::setSpeakerVolume( int left, int right ) +{ + setVolume ( SOUND_MIXER_SPEAKER, left, right ) ; +} + +void smMixer::setLineVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_LINE , left, right ) ; +} + +void smMixer::setMicVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_MIC , left, right ) ; +} + +void smMixer::setCDVolume ( int left, int right ) +{ + setVolume ( SOUND_MIXER_CD , left, right ) ; +} + +#elif defined(__OpenBSD__) + +/* ------------------------------------------------------------ */ +/* OpenBSD 2.3 */ +/* ------------------------------------------------------------ */ + +void smMixer::open ( char *device ) +{ +} + +void smMixer::close (){} + +smMixer::smMixer () { } +smMixer::smMixer ( char *device ) { } +smMixer::~smMixer () {} + +int smMixer::not_working () +{ + return error ; +} + + /* Volume controls are in integer percentages */ + +int smMixer::getVolume ( int channel ) { return 50 ; } +void smMixer::getVolume ( int channel, int *left, int *right ) +{ + if ( left ) *left = 50 ; + if ( right ) *right = 50 ; +} + +void smMixer::setVolume ( int channel, int volume ) {} +void smMixer::setVolume ( int channel, int left, int right ){} +void smMixer::setTreble ( int treble ) {} +void smMixer::setBass ( int bass ) {} +void smMixer::setMasterVolume ( int volume ) {} +void smMixer::setSynthVolume ( int volume ) {} +void smMixer::setPCMVolume ( int volume ) {} +void smMixer::setSpeakerVolume( int volume ) {} +void smMixer::setLineVolume ( int volume ) {} +void smMixer::setMicVolume ( int volume ) {} +void smMixer::setCDVolume ( int volume ) {} +void smMixer::setMasterVolume ( int left, int right ) {} +void smMixer::setSynthVolume ( int left, int right ) {} +void smMixer::setPCMVolume ( int left, int right ) {} +void smMixer::setSpeakerVolume( int left, int right ) {} +void smMixer::setLineVolume ( int left, int right ) {} +void smMixer::setMicVolume ( int left, int right ) {} +void smMixer::setCDVolume ( int left, int right ) {} + + +#else +/* ------------------------------------------------------------ */ +/* win32 */ +/* ------------------------------------------------------------ */ + +void smMixer::open ( char *device ) +{ +} + +void smMixer::close (){} + +smMixer::smMixer () { } +smMixer::smMixer ( char *device ) { } +smMixer::~smMixer () {} + +int smMixer::not_working () +{ + return error ; +} + + /* Volume controls are in integer percentages */ + +int smMixer::getVolume ( int channel ) { return 50 ; } +void smMixer::getVolume ( int channel, int *left, int *right ) +{ + if ( left ) *left = 50 ; + if ( right ) *right = 50 ; +} + +void smMixer::setVolume ( int channel, int volume ) {} +void smMixer::setVolume ( int channel, int left, int right ){} +void smMixer::setTreble ( int treble ) {} +void smMixer::setBass ( int bass ) {} +void smMixer::setMasterVolume ( int volume ) {} +void smMixer::setSynthVolume ( int volume ) {} +void smMixer::setPCMVolume ( int volume ) {} +void smMixer::setSpeakerVolume( int volume ) {} +void smMixer::setLineVolume ( int volume ) {} +void smMixer::setMicVolume ( int volume ) {} +void smMixer::setCDVolume ( int volume ) {} +void smMixer::setMasterVolume ( int left, int right ) {} +void smMixer::setSynthVolume ( int left, int right ) {} +void smMixer::setPCMVolume ( int left, int right ) {} +void smMixer::setSpeakerVolume( int left, int right ) {} +void smMixer::setLineVolume ( int left, int right ) {} +void smMixer::setMicVolume ( int left, int right ) {} +void smMixer::setCDVolume ( int left, int right ) {} + + +#endif -- 2.39.5