You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/flow/audioioaix.cc

391 lines
8.9 KiB

/*
Copyright (C) 2001 Carsten Griwodz
griff@ifi.uio.no
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef _AIX
/*
* The audio header files exist even if there is not soundcard the
* the AIX machine. You won't be able to compile this code on AIX3
* which had ACPA support, so /dev/acpa is not checked here.
* I have no idea whether the Ultimedia Audio Adapter is actually
* working or what it is right now.
* For PCI machines including PowerSeries 850, baud or paud should
* work. The DSP (MWave?) of the 850 laptops may need microcode
* download. This is not implemented.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/machine.h>
#undef BIG_ENDIAN
#include <sys/audio.h>
#ifndef AUDIO_BIG_ENDIAN
#define AUDIO_BIG_ENDIAN BIG_ENDIAN
#endif
#include "debug.h"
#include "audioio.h"
namespace Arts {
class AudioIOAIX : public AudioIO {
int openDevice();
protected:
int audio_fd;
public:
AudioIOAIX();
void setParam(AudioParam param, int& value);
int getParam(AudioParam param);
bool open();
void close();
int read(void *buffer, int size);
int write(void *buffer, int size);
};
REGISTER_AUDIO_IO(AudioIOAIX,"paud","Personal Audio Device");
};
using namespace std;
using namespace Arts;
int AudioIOAIX::openDevice()
{
char devname[14];
int fd;
for ( int dev=0; dev<4; dev++ )
{
for ( int chan=1; chan<8; chan++ )
{
sprintf(devname,"/dev/paud%d/%d",dev,chan);
fd = ::open (devname, O_WRONLY, 0);
if ( fd >= 0 )
{
paramStr(deviceName) = devname;
return fd;
}
sprintf(devname,"/dev/baud%d/%d",dev,chan);
fd = ::open (devname, O_WRONLY, 0);
if ( fd >= 0 )
{
paramStr(deviceName) = devname;
return fd;
}
}
}
return -1;
}
AudioIOAIX::AudioIOAIX()
{
int fd = openDevice();
if( fd >= 0 )
{
audio_status audiotqStatus;
memset( &audiotqStatus, 0, sizeof(audio_status) );
ioctl(fd, AUDIO_STATUS, &audiotqStatus);
audio_buffer audioBuffer;
memset( &audioBuffer, 0, sizeof(audio_buffer) );
ioctl(fd, AUDIO_BUFFER, &audioBuffer);
::close( fd );
/*
* default parameters
*/
param(samplingRate) = audiotqStatus.srate;
param(fragmentSize) = audiotqStatus.bsize;
param(fragmentCount) = audioBuffer.write_buf_cap / audiotqStatus.bsize;
param(channels) = audiotqStatus.channels;
param(direction) = 2;
param(format) = ( audiotqStatus.bits_per_sample==8 ) ? 8
: ( ( audiotqStatus.flags & AUDIO_BIG_ENDIAN ) ? 17 : 16 );
}
}
bool AudioIOAIX::open()
{
string& _error = paramStr(lastError);
string& _deviceName = paramStr(deviceName);
int& _channels = param(channels);
int& _fragmentSize = param(fragmentSize);
int& _fragmentCount = param(fragmentCount);
int& _samplingRate = param(samplingRate);
int& _format = param(format);
int mode;
switch( param(direction) )
{
case 1 : mode = O_RDONLY | O_NDELAY; break;
case 2 : mode = O_WRONLY | O_NDELAY; break;
case 3 :
_error = "open device twice to RDWR";
return false;
default :
_error = "invalid direction";
return false;
}
audio_fd = ::open(_deviceName.c_str(), mode, 0);
if(audio_fd == -1)
{
_error = "device ";
_error += _deviceName.c_str();
_error += " can't be opened (";
_error += strerror(errno);
_error += ")";
return false;
}
if( (_channels!=1) && (_channels!=2) )
{
_error = "internal error; set channels to 1 (mono) or 2 (stereo)";
close();
return false;
}
// int requeststereo = stereo;
// int speed = _samplingRate;
audio_init audioInit;
memset( &audioInit, 0, sizeof(audio_init) );
audioInit.srate = _samplingRate;
audioInit.bits_per_sample = ((_format==8)?8:16);
audioInit.bsize = _fragmentSize;
audioInit.mode = PCM;
audioInit.channels = _channels;
audioInit.flags = 0;
audioInit.flags |= (_format==17) ? AUDIO_BIG_ENDIAN : 0;
audioInit.flags |= (_format==8) ? 0 : SIGNED;
audioInit.operation = (param(direction)==1) ? RECORD : PLAY;
if ( ioctl(audio_fd, AUDIO_INIT, &audioInit) < 0 )
{
_error = "AUDIO_INIT failed - ";
_error += strerror(errno);
switch ( audioInit.rc )
{
case 1 :
_error += "Couldn't set audio format: DSP can't do play requests";
break;
case 2 :
_error += "Couldn't set audio format: DSP can't do record requests";
break;
case 4 :
_error += "Couldn't set audio format: request was invalid";
break;
case 5 :
_error += "Couldn't set audio format: conflict with open's flags";
break;
case 6 :
_error += "Couldn't set audio format: out of DSP MIPS or memory";
break;
default :
_error += "Couldn't set audio format: not documented in sys/audio.h";
break;
}
close();
return false;
}
if (audioInit.channels != _channels)
{
_error = "audio device doesn't support number of requested channels";
close();
return false;
}
switch( _format )
{
case 8 :
if (audioInit.flags&AUDIO_BIG_ENDIAN==1)
{
_error = "setting little endian format failed";
close();
return false;
}
if (audioInit.flags&SIGNED==1)
{
_error = "setting unsigned format failed";
close();
return false;
}
break;
case 16 :
if (audioInit.flags&AUDIO_BIG_ENDIAN==1)
{
_error = "setting little endian format failed";
close();
return false;
}
if (audioInit.flags&SIGNED==0)
{
_error = "setting signed format failed";
close();
return false;
}
break;
case 17 :
if (audioInit.flags&AUDIO_BIG_ENDIAN==0)
{
_error = "setting big endian format failed";
close();
return false;
}
if (audioInit.flags&SIGNED==0)
{
_error = "setting signed format failed";
close();
return false;
}
break;
default :
break;
}
/*
* Some soundcards seem to be able to only supply "nearly" the requested
* sampling rate, especially PAS 16 cards seem to quite radical supplying
* something different than the requested sampling rate ;)
*
* So we have a quite large tolerance here (when requesting 44100 Hz, it
* will accept anything between 38690 Hz and 49510 Hz). Most parts of the
* aRts code will do resampling where appropriate, so it shouldn't affect
* sound quality.
*/
int tolerance = _samplingRate/10+1000;
if (abs(audioInit.srate - _samplingRate) > tolerance)
{
_error = "can't set requested samplingrate";
char details[80];
sprintf(details," (requested rate %d, got rate %ld)",
_samplingRate, audioInit.srate);
_error += details;
close();
return false;
}
_samplingRate = audioInit.srate;
_fragmentSize = audioInit.bsize;
_fragmentCount = audioInit.bsize / audioInit.bits_per_sample;
audio_buffer buffer_info;
ioctl(audio_fd, AUDIO_BUFFER, &buffer_info);
_fragmentCount = buffer_info.write_buf_cap / audioInit.bsize;
artsdebug("buffering: %d fragments with %d bytes "
"(audio latency is %1.1f ms)", _fragmentCount, _fragmentSize,
(float)(_fragmentSize*_fragmentCount) /
(float)(2.0 * _samplingRate * _channels)*1000.0);
return true;
}
void AudioIOAIX::close()
{
::close(audio_fd);
}
void AudioIOAIX::setParam(AudioParam p, int& value)
{
param(p) = value;
}
int AudioIOAIX::getParam(AudioParam p)
{
audio_buffer info;
switch(p)
{
case canRead:
ioctl(audio_fd, AUDIO_BUFFER, &info);
return (info.read_buf_cap - info.read_buf_size);
break;
case canWrite:
ioctl(audio_fd, AUDIO_BUFFER, &info);
return (info.write_buf_cap - info.write_buf_size);
break;
case selectReadFD:
return (param(direction) & directionRead)?audio_fd:-1;
break;
case selectWriteFD:
return (param(direction) & directionWrite)?audio_fd:-1;
break;
case autoDetect:
/* You may prefer OSS if it works, e.g. on 43P 240
* or you may prefer UMS, if anyone bothers to write
* a module for it.
*/
return 2;
break;
default:
return param(p);
break;
}
}
int AudioIOAIX::read(void *buffer, int size)
{
arts_assert(audio_fd != 0);
return ::read(audio_fd,buffer,size);
}
int AudioIOAIX::write(void *buffer, int size)
{
arts_assert(audio_fd != 0);
return ::write(audio_fd,buffer,size);
}
#endif