Initial revision.
This commit is contained in:
parent
1175179da1
commit
fca7a8fa47
22 changed files with 3172 additions and 0 deletions
119
Audio/CHANGES
Normal file
119
Audio/CHANGES
Normal file
|
@ -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
|
||||
|
1
Audio/Makefile.am
Normal file
1
Audio/Makefile.am
Normal file
|
@ -0,0 +1 @@
|
|||
SUBDIRS = src example
|
9
Audio/NOTICE
Normal file
9
Audio/NOTICE
Normal file
|
@ -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.
|
||||
|
9
Audio/README
Normal file
9
Audio/README
Normal file
|
@ -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
|
||||
|
13
Audio/README.freebsd
Normal file
13
Audio/README.freebsd
Normal file
|
@ -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
|
||||
|
13
Audio/README.linux
Normal file
13
Audio/README.linux
Normal file
|
@ -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
|
||||
|
13
Audio/README.openbsd
Normal file
13
Audio/README.openbsd
Normal file
|
@ -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
|
||||
|
13
Audio/README.sgi
Normal file
13
Audio/README.sgi
Normal file
|
@ -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
|
||||
|
20
Audio/README.unix
Normal file
20
Audio/README.unix
Normal file
|
@ -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 <sjbaker1@airmail.net>
|
||||
|
15
Audio/README.win
Normal file
15
Audio/README.win
Normal file
|
@ -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;......
|
13
example/Makefile.am
Normal file
13
example/Makefile.am
Normal file
|
@ -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
|
89
example/example.cxx
Normal file
89
example/example.cxx
Normal file
|
@ -0,0 +1,89 @@
|
|||
|
||||
|
||||
#include "sl.h"
|
||||
#include "sm.h"
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
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 () ;
|
||||
}
|
||||
}
|
||||
|
12
src/Makefile.am
Normal file
12
src/Makefile.am
Normal file
|
@ -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.
|
635
src/sl.h
Normal file
635
src/sl.h
Normal file
|
@ -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
|
||||
|
663
src/slDSP.cxx
Normal file
663
src/slDSP.cxx
Normal file
|
@ -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
|
||||
|
92
src/slEnvelope.cxx
Normal file
92
src/slEnvelope.cxx
Normal file
|
@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
|
72
src/slPortability.h
Normal file
72
src/slPortability.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
|
||||
#ifndef __SLPORTABILITY_H__
|
||||
#define __SLPORTABILITY_H__ 1
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
/* OS specific includes and defines ... */
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#undef VERSION
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#ifdef __CYGWIN32__
|
||||
# define NEAR /* */
|
||||
# define FAR /* */
|
||||
# define WHERE_EVER_YOU_ARE /* Curt: optional, but it reminds me of a song */
|
||||
#endif
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#define SL_USING_OSS_AUDIO 1
|
||||
#endif
|
||||
|
||||
#ifdef SL_USING_OSS_AUDIO
|
||||
#if defined(__linux__)
|
||||
#include <linux/soundcard.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <machine/soundcard.h>
|
||||
#else
|
||||
/*
|
||||
Tom thinks this file may be <sys/soundcard.h> 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 <sjbaker1@airmail.net>
|
||||
*/
|
||||
#include <soundcard.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <sys/audioio.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define strcasecmp stricmp /* Yes, Steve really does *HATE* Windoze */
|
||||
#endif
|
||||
|
||||
/* Tom */
|
||||
|
||||
#ifdef sgi
|
||||
#include <audio.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
505
src/slSample.cxx
Normal file
505
src/slSample.cxx
Normal file
|
@ -0,0 +1,505 @@
|
|||
|
||||
|
||||
#include "sl.h"
|
||||
#include <math.h>
|
||||
|
||||
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 ;
|
||||
}
|
||||
|
||||
|
150
src/slSamplePlayer.cxx
Normal file
150
src/slSamplePlayer.cxx
Normal file
|
@ -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 ;
|
||||
}
|
||||
|
370
src/slScheduler.cxx
Normal file
370
src/slScheduler.cxx
Normal file
|
@ -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 ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
88
src/sm.h
Normal file
88
src/sm.h
Normal file
|
@ -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
|
||||
|
258
src/smMixer.cxx
Normal file
258
src/smMixer.cxx
Normal file
|
@ -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
|
Loading…
Add table
Reference in a new issue