/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #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 #include #include #include #include #include #include #include #include #include #include #undef BIG_ENDIAN #include #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 audioStatus; memset( &audioStatus, 0, sizeof(audio_status) ); ioctl(fd, AUDIO_STATUS, &audioStatus); audio_buffer audioBuffer; memset( &audioBuffer, 0, sizeof(audio_buffer) ); ioctl(fd, AUDIO_BUFFER, &audioBuffer); ::close( fd ); /* * default parameters */ param(samplingRate) = audioStatus.srate; param(fragmentSize) = audioStatus.bsize; param(fragmentCount) = audioBuffer.write_buf_cap / audioStatus.bsize; param(channels) = audioStatus.channels; param(direction) = 2; param(format) = ( audioStatus.bits_per_sample==8 ) ? 8 : ( ( audioStatus.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