664 lines
12 KiB
C++
664 lines
12 KiB
C++
|
|
#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
|
|
|