From 247750abcbf6760bbc52aa5d64fc375d6fbee8a3 Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Sun, 6 Dec 2020 19:28:06 +0900 Subject: Renaming of files in preparation for code style tools. Signed-off-by: Michele Calgaro (cherry picked from commit 00d4f92b717fbcbed6f9eee361975d6ee5380d59) --- flow/CMakeLists.txt | 30 +- flow/asyncschedule.cc | 553 ----------------- flow/asyncschedule.cpp | 553 +++++++++++++++++ flow/audioio.cc | 165 ----- flow/audioio.cpp | 165 +++++ flow/audioioaix.cc | 390 ------------ flow/audioioaix.cpp | 390 ++++++++++++ flow/audioioalsa.cc | 561 ----------------- flow/audioioalsa.cpp | 561 +++++++++++++++++ flow/audioioalsa9.cc | 590 ------------------ flow/audioioalsa9.cpp | 590 ++++++++++++++++++ flow/audioiocsl.cc | 640 -------------------- flow/audioiocsl.cpp | 640 ++++++++++++++++++++ flow/audioioesd.cc | 227 ------- flow/audioioesd.cpp | 227 +++++++ flow/audioiojack.cc | 345 ----------- flow/audioiojack.cpp | 345 +++++++++++ flow/audioiolibaudioio.cc | 236 -------- flow/audioiolibaudioio.cpp | 236 ++++++++ flow/audioiomas.cc | 619 ------------------- flow/audioiomas.cpp | 619 +++++++++++++++++++ flow/audioionas.cc | 260 -------- flow/audioionas.cpp | 260 ++++++++ flow/audioionull.cc | 184 ------ flow/audioionull.cpp | 184 ++++++ flow/audioiooss.cc | 485 --------------- flow/audioiooss.cpp | 485 +++++++++++++++ flow/audioioossthreaded.cc | 681 --------------------- flow/audioioossthreaded.cpp | 681 +++++++++++++++++++++ flow/audioiosgi.cc | 274 --------- flow/audioiosgi.cpp | 274 +++++++++ flow/audioiosndio.cc | 310 ---------- flow/audioiosndio.cpp | 310 ++++++++++ flow/audioiosun.cc | 442 -------------- flow/audioiosun.cpp | 442 ++++++++++++++ flow/audiomanager_impl.cc | 323 ---------- flow/audiomanager_impl.cpp | 323 ++++++++++ flow/audiosubsys.cc | 645 -------------------- flow/audiosubsys.cpp | 645 ++++++++++++++++++++ flow/audiotobytestream_impl.cc | 223 ------- flow/audiotobytestream_impl.cpp | 223 +++++++ flow/bus.cc | 367 ------------ flow/bus.cpp | 367 ++++++++++++ flow/bytestreamtoaudio_impl.cc | 119 ---- flow/bytestreamtoaudio_impl.cpp | 119 ++++ flow/cache.cc | 274 --------- flow/cache.cpp | 274 +++++++++ flow/convert.cc | 418 ------------- flow/convert.cpp | 418 +++++++++++++ flow/cpuinfo.cc | 232 ------- flow/cpuinfo.cpp | 232 +++++++ flow/datahandle_impl.cc | 499 ---------------- flow/datahandle_impl.cpp | 499 ++++++++++++++++ flow/gsl/CMakeLists.txt | 2 +- flow/gsl/dummy.cc | 3 - flow/gsl/dummy.cpp | 3 + flow/gsl/gslartsthreads.cc | 203 ------- flow/gsl/gslartsthreads.cpp | 203 +++++++ flow/gsl/gslglibhash.cc | 149 ----- flow/gsl/gslglibhash.cpp | 149 +++++ flow/gsl/gslglibhashtest.cc | 123 ---- flow/gsl/gslglibhashtest.cpp | 123 ++++ flow/gslschedule.cc | 1195 ------------------------------------- flow/gslschedule.cpp | 1195 +++++++++++++++++++++++++++++++++++++ flow/pipebuffer.cc | 166 ------ flow/pipebuffer.cpp | 166 ++++++ flow/resample.cc | 305 ---------- flow/resample.cpp | 305 ++++++++++ flow/stdsynthmodule.cc | 92 --- flow/stdsynthmodule.cpp | 92 +++ flow/stereoeffectstack_impl.cc | 179 ------ flow/stereoeffectstack_impl.cpp | 179 ++++++ flow/stereofftscope_impl.cc | 131 ---- flow/stereofftscope_impl.cpp | 131 ++++ flow/stereovolumecontrol_impl.cc | 195 ------ flow/stereovolumecontrol_impl.cpp | 195 ++++++ flow/synth_add_impl.cc | 44 -- flow/synth_add_impl.cpp | 44 ++ flow/synth_frequency_impl.cc | 70 --- flow/synth_frequency_impl.cpp | 70 +++ flow/synth_mul_impl.cc | 44 -- flow/synth_mul_impl.cpp | 44 ++ flow/synth_multi_add_impl.cc | 64 -- flow/synth_multi_add_impl.cpp | 64 ++ flow/synth_play_impl.cc | 284 --------- flow/synth_play_impl.cpp | 284 +++++++++ flow/synth_play_wav_impl.cc | 579 ------------------ flow/synth_play_wav_impl.cpp | 579 ++++++++++++++++++ flow/synth_record_impl.cc | 182 ------ flow/synth_record_impl.cpp | 182 ++++++ flow/synth_wave_sin_impl.cc | 44 -- flow/synth_wave_sin_impl.cpp | 44 ++ flow/virtualports.cc | 491 --------------- flow/virtualports.cpp | 491 +++++++++++++++ 94 files changed, 14621 insertions(+), 14621 deletions(-) delete mode 100644 flow/asyncschedule.cc create mode 100644 flow/asyncschedule.cpp delete mode 100644 flow/audioio.cc create mode 100644 flow/audioio.cpp delete mode 100644 flow/audioioaix.cc create mode 100644 flow/audioioaix.cpp delete mode 100644 flow/audioioalsa.cc create mode 100644 flow/audioioalsa.cpp delete mode 100644 flow/audioioalsa9.cc create mode 100644 flow/audioioalsa9.cpp delete mode 100644 flow/audioiocsl.cc create mode 100644 flow/audioiocsl.cpp delete mode 100644 flow/audioioesd.cc create mode 100644 flow/audioioesd.cpp delete mode 100644 flow/audioiojack.cc create mode 100644 flow/audioiojack.cpp delete mode 100644 flow/audioiolibaudioio.cc create mode 100644 flow/audioiolibaudioio.cpp delete mode 100644 flow/audioiomas.cc create mode 100644 flow/audioiomas.cpp delete mode 100644 flow/audioionas.cc create mode 100644 flow/audioionas.cpp delete mode 100644 flow/audioionull.cc create mode 100644 flow/audioionull.cpp delete mode 100644 flow/audioiooss.cc create mode 100644 flow/audioiooss.cpp delete mode 100644 flow/audioioossthreaded.cc create mode 100644 flow/audioioossthreaded.cpp delete mode 100644 flow/audioiosgi.cc create mode 100644 flow/audioiosgi.cpp delete mode 100644 flow/audioiosndio.cc create mode 100644 flow/audioiosndio.cpp delete mode 100644 flow/audioiosun.cc create mode 100644 flow/audioiosun.cpp delete mode 100644 flow/audiomanager_impl.cc create mode 100644 flow/audiomanager_impl.cpp delete mode 100644 flow/audiosubsys.cc create mode 100644 flow/audiosubsys.cpp delete mode 100644 flow/audiotobytestream_impl.cc create mode 100644 flow/audiotobytestream_impl.cpp delete mode 100644 flow/bus.cc create mode 100644 flow/bus.cpp delete mode 100644 flow/bytestreamtoaudio_impl.cc create mode 100644 flow/bytestreamtoaudio_impl.cpp delete mode 100644 flow/cache.cc create mode 100644 flow/cache.cpp delete mode 100644 flow/convert.cc create mode 100644 flow/convert.cpp delete mode 100644 flow/cpuinfo.cc create mode 100644 flow/cpuinfo.cpp delete mode 100644 flow/datahandle_impl.cc create mode 100644 flow/datahandle_impl.cpp delete mode 100644 flow/gsl/dummy.cc create mode 100644 flow/gsl/dummy.cpp delete mode 100644 flow/gsl/gslartsthreads.cc create mode 100644 flow/gsl/gslartsthreads.cpp delete mode 100644 flow/gsl/gslglibhash.cc create mode 100644 flow/gsl/gslglibhash.cpp delete mode 100644 flow/gsl/gslglibhashtest.cc create mode 100644 flow/gsl/gslglibhashtest.cpp delete mode 100644 flow/gslschedule.cc create mode 100644 flow/gslschedule.cpp delete mode 100644 flow/pipebuffer.cc create mode 100644 flow/pipebuffer.cpp delete mode 100644 flow/resample.cc create mode 100644 flow/resample.cpp delete mode 100644 flow/stdsynthmodule.cc create mode 100644 flow/stdsynthmodule.cpp delete mode 100644 flow/stereoeffectstack_impl.cc create mode 100644 flow/stereoeffectstack_impl.cpp delete mode 100644 flow/stereofftscope_impl.cc create mode 100644 flow/stereofftscope_impl.cpp delete mode 100644 flow/stereovolumecontrol_impl.cc create mode 100644 flow/stereovolumecontrol_impl.cpp delete mode 100644 flow/synth_add_impl.cc create mode 100644 flow/synth_add_impl.cpp delete mode 100644 flow/synth_frequency_impl.cc create mode 100644 flow/synth_frequency_impl.cpp delete mode 100644 flow/synth_mul_impl.cc create mode 100644 flow/synth_mul_impl.cpp delete mode 100644 flow/synth_multi_add_impl.cc create mode 100644 flow/synth_multi_add_impl.cpp delete mode 100644 flow/synth_play_impl.cc create mode 100644 flow/synth_play_impl.cpp delete mode 100644 flow/synth_play_wav_impl.cc create mode 100644 flow/synth_play_wav_impl.cpp delete mode 100644 flow/synth_record_impl.cc create mode 100644 flow/synth_record_impl.cpp delete mode 100644 flow/synth_wave_sin_impl.cc create mode 100644 flow/synth_wave_sin_impl.cpp delete mode 100644 flow/virtualports.cc create mode 100644 flow/virtualports.cpp (limited to 'flow') diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index 4a8492d..48fb330 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -48,19 +48,19 @@ install( FILES set( target artsflow ) set( ${target}_SRCS - synth_play_impl.cc gslschedule.cc audiosubsys.cc - pipebuffer.cc convert.cc synth_wave_sin_impl.cc - synth_frequency_impl.cc synth_multi_add_impl.cc - synth_add_impl.cc synth_mul_impl.cc synth_play_wav_impl.cc - stdsynthmodule.cc cache.cc asyncschedule.cc bytestreamtoaudio_impl.cc - stereovolumecontrol_impl.cc stereoeffectstack_impl.cc - fft.c stereofftscope_impl.cc virtualports.cc bus.cc - audiomanager_impl.cc synth_record_impl.cc resample.cc - audioio.cc audioiooss.cc audioioalsa.cc audioioalsa9.cc - audioionull.cc audioiolibaudioio.cc audioioesd.cc audioiosndio.cc - audioiojack.cc audioiosun.cc audioioaix.cc audioionas.cc - cpuinfo.cc audioioossthreaded.cc audiotobytestream_impl.cc - audioiosgi.cc audioiocsl.cc audioiomas.cc datahandle_impl.cc + synth_play_impl.cpp gslschedule.cpp audiosubsys.cpp + pipebuffer.cpp convert.cpp synth_wave_sin_impl.cpp + synth_frequency_impl.cpp synth_multi_add_impl.cpp + synth_add_impl.cpp synth_mul_impl.cpp synth_play_wav_impl.cpp + stdsynthmodule.cpp cache.cpp asyncschedule.cpp bytestreamtoaudio_impl.cpp + stereovolumecontrol_impl.cpp stereoeffectstack_impl.cpp + fft.c stereofftscope_impl.cpp virtualports.cpp bus.cpp + audiomanager_impl.cpp synth_record_impl.cpp resample.cpp + audioio.cpp audioiooss.cpp audioioalsa.cpp audioioalsa9.cpp + audioionull.cpp audioiolibaudioio.cpp audioioesd.cpp audioiosndio.cpp + audioiojack.cpp audioiosun.cpp audioioaix.cpp audioionas.cpp + cpuinfo.cpp audioioossthreaded.cpp audiotobytestream_impl.cpp + audioiosgi.cpp audioiocsl.cpp audioiomas.cpp datahandle_impl.cpp ) tde_add_library( ${target} SHARED @@ -73,13 +73,13 @@ tde_add_library( ${target} SHARED ##### artsflow_idl (shared lib) ################# add_custom_command( - OUTPUT artsflow.cc + OUTPUT artsflow.cpp COMMAND ../mcopidl/mcopidl ARGS -t ${CMAKE_CURRENT_SOURCE_DIR}/artsflow.idl DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/artsflow.idl ) tde_add_library( artsflow_idl SHARED - SOURCES artsflow.cc + SOURCES artsflow.cpp VERSION 1.0.0 LINK mcop-shared ${ALSA_LIBRARIES} DESTINATION ${LIB_INSTALL_DIR} diff --git a/flow/asyncschedule.cc b/flow/asyncschedule.cc deleted file mode 100644 index 66ae48b..0000000 --- a/flow/asyncschedule.cc +++ /dev/null @@ -1,553 +0,0 @@ - /* - - Copyright (C) 2000,2001 Stefan Westerfeld - stefan@space.twc.de - - 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. - - */ - -#include -#include "asyncschedule.h" - -using namespace std; -using namespace Arts; - -#include "debug.h" -#include - -/* Since this file is a tad bit more complex, here is some basic documentation: - -1) ASyncPort: There are asynchronous ports which are parts of the standard- - flowsystem schedule nodes. Their lifetime starts whenever an asynchronous - stream gets created by the flow system, and ends when the schedule node - gets destroyed. Basically, an ASyncPort has two functions: - - * it is a "Port", which means that it gets connect(), disconnect() and - other calls from the flowsystem - - * it is a "GenericDataChannel", which means that DataPackets can interact - with it - - Although there will be ASyncPorts which only send data and ASyncPorts which - only receive data (there are none that do both), there are no distinct - classes for this. - -2) Standard case: a DataPacket that gets transported over a datachannel locally: - - 1. the user allocates himself a datapacket "packet" - 2. the user calls "packet->send()", which in turn calls - ASyncPort::sendPacket(packet) - 3. the ASyncPort sends the DataPacket to every subscriber (incrementing the - useCount) over the NotificationManager - 4. the NotificationManager delivers the DataPackets to the receiver - 5. eventually, the receiver confirms using "packet->processed()" - 6. the packet informs the ASyncPort::processedPacket() - 7. the packet is freed - -variant (pulling): - - 1. the user gets told by the ASyncPort: produce some data, here is a "packet" - 2. the user calls "packet->send()", which in turn calls - ASyncPort::sendPacket(packet) - 3. the ASyncPort sends the DataPacket to every subscriber (incrementing the - useCount) over the NotificationManager - 4. the NotificationManager delivers the DataPackets to the receiver - 5. eventually, the receiver confirms using "packet->processed()" - 6. the packet informs the ASyncPort::processedPacket() - 7. the ASyncPort restarts with 1. - -3) Remote case: the remote case follows from the local case by adding two extra -things: one object that converts packets from their packet form to a message -(ASyncNetSend), and one object that converts packets from the message form -to a packet again. Effectively, the sending of a single packet looks like -this, then: - - 1-S. the user allocates himself a datapacket "packet" - 2-S. the user calls "packet->send()", which in turn calls - ASyncPort::sendPacket(packet) - 3-S. the ASyncPort sends the DataPacket to every subscriber (incrementing the - useCount) over the NotificationManager - 4-S. the NotificationManager delivers the DataPackets to the ASyncNetSend - 5-S. the ASyncNetSend::notify method gets called, which in turn converts - the packet to a network message - - ... network transfer ... - - 6-R. the ASyncNetReceive::receive method gets called - the method creates - a new data packet, and sends it using the NotificationManager again - 7-R. the NotificationManager delivers the DataPacket to the receiver - 8-R. eventually, the receiver confirms using "packet->processed()" - 9-R. the packet informs the ASyncNetReceive::processedPacket() which - frees the packet and tells the (remote) sender that it went all right - - ... network transfer ... - - 10-S. eventually, ASyncNetSend::processed() gets called, and confirms - the packet using "packet->processed()" - 11-S. the packet informs the ASyncPort::processedPacket() - 12-S. the packet is freed - -variant(pulling): - - works the same as in the local case by exchanging steps 1-S and 12-S - -4) ownership: - - * ASyncPort: is owned by the Object which it is a part of, if the object - dies, ASyncPort dies unconditionally - - * DataPacket: is owned by the GenericDataChannel they are propagated over, - that is, the ASyncPort normally - however if the DataPacket is still in - use (i.e. in state 5 of the local case), it will take responsibility to - free itself once all processed() calls have been collected - - * ASyncNetSend, ASyncNetReceive: own each other, so that if the sender dies, - the connection will die as well, and if the receiver dies, the same happens - -*/ - -#undef DEBUG_ASYNC_TRANSFER - -ASyncPort::ASyncPort(const std::string& name, void *ptr, long flags, - StdScheduleNode* parent) : Port(name, ptr, flags, parent), pull(false) -{ - stream = (GenericAsyncStream *)ptr; - stream->channel = this; - stream->_notifyID = notifyID = parent->object()->_mkNotifyID(); -} - -ASyncPort::~ASyncPort() -{ - /* - * tell all outstanding packets that we don't exist any longer, so that - * if they feel like they need to confirm they have been processed now, - * they don't talk to an no longer existing object about it - */ - while(!sent.empty()) - { - sent.front()->channel = 0; - sent.pop_front(); - } - - /* disconnect remote connections (if present): the following things will - * need to be ensured here, since we are being deleted: - * - * - the senders should not talk to us after our destructor - * - all of our connections need to be disconnected - * - every connection needs to be closed exactly once - * - * (closing a connection can cause reentrancy due to mcop communication) - */ - while(!netSenders.empty()) - netSenders.front()->disconnect(); - - FlowSystemReceiver receiver = netReceiver; - if(!receiver.isNull()) - receiver.disconnect(); -} - -//-------------------- GenericDataChannel interface ------------------------- - -void ASyncPort::setPull(int packets, int capacity) -{ - pullNotification.receiver = parent->object(); - pullNotification.ID = notifyID; - pullNotification.internal = 0; - pull = true; - - for(int i=0;icreatePacket(capacity); - packet->useCount = 0; - pullNotification.data = packet; - NotificationManager::the()->send(pullNotification); - } -} - -void ASyncPort::endPull() -{ - pull = false; - // TODO: maybe remove all pending pull packets here -} - -void ASyncPort::processedPacket(GenericDataPacket *packet) -{ - int count = 0; - list::iterator i = sent.begin(); - while(i != sent.end()) - { - if(*i == packet) - { - count++; - i = sent.erase(i); - } - else i++; - } - assert(count == 1); - -#ifdef DEBUG_ASYNC_TRANSFER - cout << "port::processedPacket" << endl; -#endif - assert(packet->useCount == 0); - if(pull) - { - pullNotification.data = packet; - NotificationManager::the()->send(pullNotification); - } - else - { - stream->freePacket(packet); - } -} - -void ASyncPort::sendPacket(GenericDataPacket *packet) -{ - bool sendOk = false; - -#ifdef DEBUG_ASYNC_TRANSFER - cout << "port::sendPacket" << endl; -#endif - - if(packet->size > 0) - { - vector::iterator i; - for(i=subscribers.begin(); i != subscribers.end(); i++) - { - Notification n = *i; - n.data = packet; - packet->useCount++; -#ifdef DEBUG_ASYNC_TRANSFER - cout << "sending notification " << n.ID << endl; -#endif - NotificationManager::the()->send(n); - sendOk = true; - } - } - - if(sendOk) - sent.push_back(packet); - else - stream->freePacket(packet); -} - -//----------------------- Port interface ------------------------------------ - -void ASyncPort::connect(Port *xsource) -{ - arts_debug("port(%s)::connect",_name.c_str()); - - ASyncPort *source = xsource->asyncPort(); - assert(source); - addAutoDisconnect(xsource); - - Notification n; - n.receiver = parent->object(); - n.ID = notifyID; - n.internal = 0; - source->subscribers.push_back(n); -} - -void ASyncPort::disconnect(Port *xsource) -{ - arts_debug("port::disconnect"); - - ASyncPort *source = xsource->asyncPort(); - assert(source); - removeAutoDisconnect(xsource); - - // remove our subscription from the source object - vector::iterator si; - for(si = source->subscribers.begin(); si != source->subscribers.end(); si++) - { - if(si->receiver == parent->object()) - { - source->subscribers.erase(si); - return; - } - } - - // there should have been exactly one, so this shouldn't be reached - assert(false); -} - -ASyncPort *ASyncPort::asyncPort() -{ - return this; -} - -GenericAsyncStream *ASyncPort::receiveNetCreateStream() -{ - return stream->createNewStream(); -} - -NotificationClient *ASyncPort::receiveNetObject() -{ - return parent->object(); -} - -long ASyncPort::receiveNetNotifyID() -{ - return notifyID; -} - -// Network transparency -void ASyncPort::addSendNet(ASyncNetSend *netsend) -{ - Notification n; - n.receiver = netsend; - n.ID = netsend->notifyID(); - n.internal = 0; - subscribers.push_back(n); - netSenders.push_back(netsend); -} - -void ASyncPort::removeSendNet(ASyncNetSend *netsend) -{ - arts_return_if_fail(netsend != 0); - netSenders.remove(netsend); - - vector::iterator si; - for(si = subscribers.begin(); si != subscribers.end(); si++) - { - if(si->receiver == netsend) - { - subscribers.erase(si); - return; - } - } - arts_warning("Failed to remove ASyncNetSend (%p) from ASyncPort", netsend); -} - -void ASyncPort::setNetReceiver(ASyncNetReceive *receiver) -{ - arts_return_if_fail(receiver != 0); - - FlowSystemReceiver r = FlowSystemReceiver::_from_base(receiver->_copy()); - netReceiver = r; -} - -void ASyncPort::disconnectRemote(const string& dest) -{ - list::iterator i; - - for(i = netSenders.begin(); i != netSenders.end(); i++) - { - if((*i)->dest() == dest) - { - (*i)->disconnect(); - return; - } - } - arts_warning("failed to disconnect %s in ASyncPort", dest.c_str()); -} - -ASyncNetSend::ASyncNetSend(ASyncPort *ap, const std::string& dest) : ap(ap) -{ - _dest = dest; - ap->addSendNet(this); -} - -ASyncNetSend::~ASyncNetSend() -{ - while(!pqueue.empty()) - { - pqueue.front()->processed(); - pqueue.pop(); - } - if(ap) - { - ap->removeSendNet(this); - ap = 0; - } -} - -long ASyncNetSend::notifyID() -{ - return 1; -} - -void ASyncNetSend::notify(const Notification& notification) -{ - // got a packet? - assert(notification.ID == notifyID()); - GenericDataPacket *dp = (GenericDataPacket *)notification.data; - pqueue.push(dp); - - /* - * since packets are delivered asynchronously, and since disconnection - * involves communication, it might happen that we get a packet without - * actually being connected any longer - in that case, silently forget it - */ - if(!receiver.isNull()) - { - // put it into a custom data message and send it to the receiver - Buffer *buffer = receiver._allocCustomMessage(receiveHandlerID); - dp->write(*buffer); - receiver._sendCustomMessage(buffer); - } -} - -void ASyncNetSend::processed() -{ - assert(!pqueue.empty()); - pqueue.front()->processed(); - pqueue.pop(); -} - -void ASyncNetSend::setReceiver(FlowSystemReceiver newReceiver) -{ - receiver = newReceiver; - receiveHandlerID = newReceiver.receiveHandlerID(); -} - -void ASyncNetSend::disconnect() -{ - /* since disconnection will cause destruction (most likely immediate), - * we'll reference ourselves ... */ - _copy(); - - if(!receiver.isNull()) - { - FlowSystemReceiver r = receiver; - receiver = FlowSystemReceiver::null(); - r.disconnect(); - } - if(ap) - { - ap->removeSendNet(this); - ap = 0; - } - - _release(); -} - -string ASyncNetSend::dest() -{ - return _dest; -} - -/* dispatching function for custom message */ - -static void _dispatch_ASyncNetReceive_receive(void *object, Buffer *buffer) -{ - ((ASyncNetReceive *)object)->receive(buffer); -} - -ASyncNetReceive::ASyncNetReceive(ASyncPort *port, FlowSystemSender sender) -{ - port->setNetReceiver(this); - stream = port->receiveNetCreateStream(); - stream->channel = this; - this->sender = sender; - /* stream->_notifyID = _mkNotifyID(); */ - - gotPacketNotification.ID = port->receiveNetNotifyID(); - gotPacketNotification.receiver = port->receiveNetObject(); - gotPacketNotification.internal = 0; - _receiveHandlerID = - _addCustomMessageHandler(_dispatch_ASyncNetReceive_receive,this); -} - -ASyncNetReceive::~ASyncNetReceive() -{ - /* tell outstanding packets that we don't exist any longer */ - while(!sent.empty()) - { - sent.front()->channel = 0; - sent.pop_front(); - } - delete stream; -} - -long ASyncNetReceive::receiveHandlerID() -{ - return _receiveHandlerID; -} - -void ASyncNetReceive::receive(Buffer *buffer) -{ - GenericDataPacket *dp = stream->createPacket(512); - dp->read(*buffer); - dp->useCount = 1; - gotPacketNotification.data = dp; - NotificationManager::the()->send(gotPacketNotification); - sent.push_back(dp); -} - -/* - * It will happen that this routine is called in time critical situations, - * such as: while audio calculation is running, and must be finished in - * time. The routine is mostly harmless, because sender->processed() is - * a oneway function, which just queues the buffer for sending and returns - * back, so it should return at once. - * - * However there is an exception upon first call: when sender->processed() - * is called for the first time, the method processed has still to be looked - * up. Thus, a synchronous call to _lookupMethod is made. That means, upon - * first call, the method will send out an MCOP request and block until the - * remote process tells that id. - */ -void ASyncNetReceive::processedPacket(GenericDataPacket *packet) -{ - /* - * HACK! Upon disconnect, strange things will happen. One of them is - * that we might, for the reason of not being referenced any longer, - * cease to exist without warning. Another is that our nice "sender" - * reference will get a null reference without warning, see disconnect - * code (which will cause the attached stub to also disappear). As - * those objects (especially the stub) are not prepared for not - * being there any more in the middle of whatever they do, we here - * explicitly reference us, and them, *again*, so that no evil things - * will happen. A general solution for this would be garbage collection - * in a timer, but until this is implemented (if it ever will become - * implemented), we'll live with this hack. - */ - _copy(); - sent.remove(packet); - stream->freePacket(packet); - if(!sender.isNull()) - { - FlowSystemSender xsender = sender; - xsender.processed(); - } - _release(); -} - -void ASyncNetReceive::disconnect() -{ - if(!sender.isNull()) - { - FlowSystemSender s = sender; - sender = FlowSystemSender::null(); - s.disconnect(); - } -} - -void ASyncNetReceive::sendPacket(GenericDataPacket *) -{ - assert(false); -} - -void ASyncNetReceive::setPull(int, int) -{ - assert(false); -} - -void ASyncNetReceive::endPull() -{ - assert(false); -} diff --git a/flow/asyncschedule.cpp b/flow/asyncschedule.cpp new file mode 100644 index 0000000..66ae48b --- /dev/null +++ b/flow/asyncschedule.cpp @@ -0,0 +1,553 @@ + /* + + Copyright (C) 2000,2001 Stefan Westerfeld + stefan@space.twc.de + + 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. + + */ + +#include +#include "asyncschedule.h" + +using namespace std; +using namespace Arts; + +#include "debug.h" +#include + +/* Since this file is a tad bit more complex, here is some basic documentation: + +1) ASyncPort: There are asynchronous ports which are parts of the standard- + flowsystem schedule nodes. Their lifetime starts whenever an asynchronous + stream gets created by the flow system, and ends when the schedule node + gets destroyed. Basically, an ASyncPort has two functions: + + * it is a "Port", which means that it gets connect(), disconnect() and + other calls from the flowsystem + + * it is a "GenericDataChannel", which means that DataPackets can interact + with it + + Although there will be ASyncPorts which only send data and ASyncPorts which + only receive data (there are none that do both), there are no distinct + classes for this. + +2) Standard case: a DataPacket that gets transported over a datachannel locally: + + 1. the user allocates himself a datapacket "packet" + 2. the user calls "packet->send()", which in turn calls + ASyncPort::sendPacket(packet) + 3. the ASyncPort sends the DataPacket to every subscriber (incrementing the + useCount) over the NotificationManager + 4. the NotificationManager delivers the DataPackets to the receiver + 5. eventually, the receiver confirms using "packet->processed()" + 6. the packet informs the ASyncPort::processedPacket() + 7. the packet is freed + +variant (pulling): + + 1. the user gets told by the ASyncPort: produce some data, here is a "packet" + 2. the user calls "packet->send()", which in turn calls + ASyncPort::sendPacket(packet) + 3. the ASyncPort sends the DataPacket to every subscriber (incrementing the + useCount) over the NotificationManager + 4. the NotificationManager delivers the DataPackets to the receiver + 5. eventually, the receiver confirms using "packet->processed()" + 6. the packet informs the ASyncPort::processedPacket() + 7. the ASyncPort restarts with 1. + +3) Remote case: the remote case follows from the local case by adding two extra +things: one object that converts packets from their packet form to a message +(ASyncNetSend), and one object that converts packets from the message form +to a packet again. Effectively, the sending of a single packet looks like +this, then: + + 1-S. the user allocates himself a datapacket "packet" + 2-S. the user calls "packet->send()", which in turn calls + ASyncPort::sendPacket(packet) + 3-S. the ASyncPort sends the DataPacket to every subscriber (incrementing the + useCount) over the NotificationManager + 4-S. the NotificationManager delivers the DataPackets to the ASyncNetSend + 5-S. the ASyncNetSend::notify method gets called, which in turn converts + the packet to a network message + + ... network transfer ... + + 6-R. the ASyncNetReceive::receive method gets called - the method creates + a new data packet, and sends it using the NotificationManager again + 7-R. the NotificationManager delivers the DataPacket to the receiver + 8-R. eventually, the receiver confirms using "packet->processed()" + 9-R. the packet informs the ASyncNetReceive::processedPacket() which + frees the packet and tells the (remote) sender that it went all right + + ... network transfer ... + + 10-S. eventually, ASyncNetSend::processed() gets called, and confirms + the packet using "packet->processed()" + 11-S. the packet informs the ASyncPort::processedPacket() + 12-S. the packet is freed + +variant(pulling): + + works the same as in the local case by exchanging steps 1-S and 12-S + +4) ownership: + + * ASyncPort: is owned by the Object which it is a part of, if the object + dies, ASyncPort dies unconditionally + + * DataPacket: is owned by the GenericDataChannel they are propagated over, + that is, the ASyncPort normally - however if the DataPacket is still in + use (i.e. in state 5 of the local case), it will take responsibility to + free itself once all processed() calls have been collected + + * ASyncNetSend, ASyncNetReceive: own each other, so that if the sender dies, + the connection will die as well, and if the receiver dies, the same happens + +*/ + +#undef DEBUG_ASYNC_TRANSFER + +ASyncPort::ASyncPort(const std::string& name, void *ptr, long flags, + StdScheduleNode* parent) : Port(name, ptr, flags, parent), pull(false) +{ + stream = (GenericAsyncStream *)ptr; + stream->channel = this; + stream->_notifyID = notifyID = parent->object()->_mkNotifyID(); +} + +ASyncPort::~ASyncPort() +{ + /* + * tell all outstanding packets that we don't exist any longer, so that + * if they feel like they need to confirm they have been processed now, + * they don't talk to an no longer existing object about it + */ + while(!sent.empty()) + { + sent.front()->channel = 0; + sent.pop_front(); + } + + /* disconnect remote connections (if present): the following things will + * need to be ensured here, since we are being deleted: + * + * - the senders should not talk to us after our destructor + * - all of our connections need to be disconnected + * - every connection needs to be closed exactly once + * + * (closing a connection can cause reentrancy due to mcop communication) + */ + while(!netSenders.empty()) + netSenders.front()->disconnect(); + + FlowSystemReceiver receiver = netReceiver; + if(!receiver.isNull()) + receiver.disconnect(); +} + +//-------------------- GenericDataChannel interface ------------------------- + +void ASyncPort::setPull(int packets, int capacity) +{ + pullNotification.receiver = parent->object(); + pullNotification.ID = notifyID; + pullNotification.internal = 0; + pull = true; + + for(int i=0;icreatePacket(capacity); + packet->useCount = 0; + pullNotification.data = packet; + NotificationManager::the()->send(pullNotification); + } +} + +void ASyncPort::endPull() +{ + pull = false; + // TODO: maybe remove all pending pull packets here +} + +void ASyncPort::processedPacket(GenericDataPacket *packet) +{ + int count = 0; + list::iterator i = sent.begin(); + while(i != sent.end()) + { + if(*i == packet) + { + count++; + i = sent.erase(i); + } + else i++; + } + assert(count == 1); + +#ifdef DEBUG_ASYNC_TRANSFER + cout << "port::processedPacket" << endl; +#endif + assert(packet->useCount == 0); + if(pull) + { + pullNotification.data = packet; + NotificationManager::the()->send(pullNotification); + } + else + { + stream->freePacket(packet); + } +} + +void ASyncPort::sendPacket(GenericDataPacket *packet) +{ + bool sendOk = false; + +#ifdef DEBUG_ASYNC_TRANSFER + cout << "port::sendPacket" << endl; +#endif + + if(packet->size > 0) + { + vector::iterator i; + for(i=subscribers.begin(); i != subscribers.end(); i++) + { + Notification n = *i; + n.data = packet; + packet->useCount++; +#ifdef DEBUG_ASYNC_TRANSFER + cout << "sending notification " << n.ID << endl; +#endif + NotificationManager::the()->send(n); + sendOk = true; + } + } + + if(sendOk) + sent.push_back(packet); + else + stream->freePacket(packet); +} + +//----------------------- Port interface ------------------------------------ + +void ASyncPort::connect(Port *xsource) +{ + arts_debug("port(%s)::connect",_name.c_str()); + + ASyncPort *source = xsource->asyncPort(); + assert(source); + addAutoDisconnect(xsource); + + Notification n; + n.receiver = parent->object(); + n.ID = notifyID; + n.internal = 0; + source->subscribers.push_back(n); +} + +void ASyncPort::disconnect(Port *xsource) +{ + arts_debug("port::disconnect"); + + ASyncPort *source = xsource->asyncPort(); + assert(source); + removeAutoDisconnect(xsource); + + // remove our subscription from the source object + vector::iterator si; + for(si = source->subscribers.begin(); si != source->subscribers.end(); si++) + { + if(si->receiver == parent->object()) + { + source->subscribers.erase(si); + return; + } + } + + // there should have been exactly one, so this shouldn't be reached + assert(false); +} + +ASyncPort *ASyncPort::asyncPort() +{ + return this; +} + +GenericAsyncStream *ASyncPort::receiveNetCreateStream() +{ + return stream->createNewStream(); +} + +NotificationClient *ASyncPort::receiveNetObject() +{ + return parent->object(); +} + +long ASyncPort::receiveNetNotifyID() +{ + return notifyID; +} + +// Network transparency +void ASyncPort::addSendNet(ASyncNetSend *netsend) +{ + Notification n; + n.receiver = netsend; + n.ID = netsend->notifyID(); + n.internal = 0; + subscribers.push_back(n); + netSenders.push_back(netsend); +} + +void ASyncPort::removeSendNet(ASyncNetSend *netsend) +{ + arts_return_if_fail(netsend != 0); + netSenders.remove(netsend); + + vector::iterator si; + for(si = subscribers.begin(); si != subscribers.end(); si++) + { + if(si->receiver == netsend) + { + subscribers.erase(si); + return; + } + } + arts_warning("Failed to remove ASyncNetSend (%p) from ASyncPort", netsend); +} + +void ASyncPort::setNetReceiver(ASyncNetReceive *receiver) +{ + arts_return_if_fail(receiver != 0); + + FlowSystemReceiver r = FlowSystemReceiver::_from_base(receiver->_copy()); + netReceiver = r; +} + +void ASyncPort::disconnectRemote(const string& dest) +{ + list::iterator i; + + for(i = netSenders.begin(); i != netSenders.end(); i++) + { + if((*i)->dest() == dest) + { + (*i)->disconnect(); + return; + } + } + arts_warning("failed to disconnect %s in ASyncPort", dest.c_str()); +} + +ASyncNetSend::ASyncNetSend(ASyncPort *ap, const std::string& dest) : ap(ap) +{ + _dest = dest; + ap->addSendNet(this); +} + +ASyncNetSend::~ASyncNetSend() +{ + while(!pqueue.empty()) + { + pqueue.front()->processed(); + pqueue.pop(); + } + if(ap) + { + ap->removeSendNet(this); + ap = 0; + } +} + +long ASyncNetSend::notifyID() +{ + return 1; +} + +void ASyncNetSend::notify(const Notification& notification) +{ + // got a packet? + assert(notification.ID == notifyID()); + GenericDataPacket *dp = (GenericDataPacket *)notification.data; + pqueue.push(dp); + + /* + * since packets are delivered asynchronously, and since disconnection + * involves communication, it might happen that we get a packet without + * actually being connected any longer - in that case, silently forget it + */ + if(!receiver.isNull()) + { + // put it into a custom data message and send it to the receiver + Buffer *buffer = receiver._allocCustomMessage(receiveHandlerID); + dp->write(*buffer); + receiver._sendCustomMessage(buffer); + } +} + +void ASyncNetSend::processed() +{ + assert(!pqueue.empty()); + pqueue.front()->processed(); + pqueue.pop(); +} + +void ASyncNetSend::setReceiver(FlowSystemReceiver newReceiver) +{ + receiver = newReceiver; + receiveHandlerID = newReceiver.receiveHandlerID(); +} + +void ASyncNetSend::disconnect() +{ + /* since disconnection will cause destruction (most likely immediate), + * we'll reference ourselves ... */ + _copy(); + + if(!receiver.isNull()) + { + FlowSystemReceiver r = receiver; + receiver = FlowSystemReceiver::null(); + r.disconnect(); + } + if(ap) + { + ap->removeSendNet(this); + ap = 0; + } + + _release(); +} + +string ASyncNetSend::dest() +{ + return _dest; +} + +/* dispatching function for custom message */ + +static void _dispatch_ASyncNetReceive_receive(void *object, Buffer *buffer) +{ + ((ASyncNetReceive *)object)->receive(buffer); +} + +ASyncNetReceive::ASyncNetReceive(ASyncPort *port, FlowSystemSender sender) +{ + port->setNetReceiver(this); + stream = port->receiveNetCreateStream(); + stream->channel = this; + this->sender = sender; + /* stream->_notifyID = _mkNotifyID(); */ + + gotPacketNotification.ID = port->receiveNetNotifyID(); + gotPacketNotification.receiver = port->receiveNetObject(); + gotPacketNotification.internal = 0; + _receiveHandlerID = + _addCustomMessageHandler(_dispatch_ASyncNetReceive_receive,this); +} + +ASyncNetReceive::~ASyncNetReceive() +{ + /* tell outstanding packets that we don't exist any longer */ + while(!sent.empty()) + { + sent.front()->channel = 0; + sent.pop_front(); + } + delete stream; +} + +long ASyncNetReceive::receiveHandlerID() +{ + return _receiveHandlerID; +} + +void ASyncNetReceive::receive(Buffer *buffer) +{ + GenericDataPacket *dp = stream->createPacket(512); + dp->read(*buffer); + dp->useCount = 1; + gotPacketNotification.data = dp; + NotificationManager::the()->send(gotPacketNotification); + sent.push_back(dp); +} + +/* + * It will happen that this routine is called in time critical situations, + * such as: while audio calculation is running, and must be finished in + * time. The routine is mostly harmless, because sender->processed() is + * a oneway function, which just queues the buffer for sending and returns + * back, so it should return at once. + * + * However there is an exception upon first call: when sender->processed() + * is called for the first time, the method processed has still to be looked + * up. Thus, a synchronous call to _lookupMethod is made. That means, upon + * first call, the method will send out an MCOP request and block until the + * remote process tells that id. + */ +void ASyncNetReceive::processedPacket(GenericDataPacket *packet) +{ + /* + * HACK! Upon disconnect, strange things will happen. One of them is + * that we might, for the reason of not being referenced any longer, + * cease to exist without warning. Another is that our nice "sender" + * reference will get a null reference without warning, see disconnect + * code (which will cause the attached stub to also disappear). As + * those objects (especially the stub) are not prepared for not + * being there any more in the middle of whatever they do, we here + * explicitly reference us, and them, *again*, so that no evil things + * will happen. A general solution for this would be garbage collection + * in a timer, but until this is implemented (if it ever will become + * implemented), we'll live with this hack. + */ + _copy(); + sent.remove(packet); + stream->freePacket(packet); + if(!sender.isNull()) + { + FlowSystemSender xsender = sender; + xsender.processed(); + } + _release(); +} + +void ASyncNetReceive::disconnect() +{ + if(!sender.isNull()) + { + FlowSystemSender s = sender; + sender = FlowSystemSender::null(); + s.disconnect(); + } +} + +void ASyncNetReceive::sendPacket(GenericDataPacket *) +{ + assert(false); +} + +void ASyncNetReceive::setPull(int, int) +{ + assert(false); +} + +void ASyncNetReceive::endPull() +{ + assert(false); +} diff --git a/flow/audioio.cc b/flow/audioio.cc deleted file mode 100644 index eafb197..0000000 --- a/flow/audioio.cc +++ /dev/null @@ -1,165 +0,0 @@ - /* - - Copyright (C) 2000 Stefan Westerfeld - stefan@space.twc.de - - 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. - - */ - -#include "audioio.h" -#include -#include -#include -#include - -using namespace Arts; -using namespace std; - -class Arts::AudioIOPrivate { -public: - map paramMap; - map paramStrMap; -}; - -AudioIO::AudioIO() :d(new AudioIOPrivate) -{ -} - -AudioIO::~AudioIO() -{ - delete d; -} - -int& AudioIO::param(AudioParam param) -{ - /* - * returns d->paramMap[param], initializes new elements with -1 (for those - * parameters not handled explicitly by the derived AudioIO* class). - */ - - map::iterator pi = d->paramMap.find(param); - if(pi == d->paramMap.end()) - { - int& result = d->paramMap[param]; - result = -1; - return result; - } - else return pi->second; -} - -string& AudioIO::paramStr(AudioParam param) -{ - return d->paramStrMap[param]; -} - -void AudioIO::setParamStr(AudioParam p, const char *value) -{ - paramStr(p) = value; -} - -void AudioIO::setParam(AudioParam p, int& value) -{ - param(p) = value; -} - -int AudioIO::getParam(AudioParam p) -{ - return param(p); -} - -const char *AudioIO::getParamStr(AudioParam p) -{ - return paramStr(p).c_str(); -} - -/* ---- factories ---- */ - -static list *audioIOFactories = 0; - -AudioIO *AudioIO::createAudioIO(const char *name) -{ - if(audioIOFactories) - { - list::iterator i; - for(i = audioIOFactories->begin(); i != audioIOFactories->end(); i++) - { - AudioIOFactory *factory = *i; - - if(strcmp(factory->name(),name) == 0) - return factory->createAudioIO(); - } - } - return 0; -} - -int AudioIO::queryAudioIOCount() -{ - return audioIOFactories->size(); -} - -int AudioIO::queryAudioIOParam(int /*audioIO*/, AudioParam /*p*/) -{ - return 0; -} - -const char *AudioIO::queryAudioIOParamStr(int audioIO, AudioParam p) -{ - list::iterator i = audioIOFactories->begin(); - - while(audioIO && i != audioIOFactories->end()) { i++; audioIO--; } - if(i == audioIOFactories->end()) return 0; - - switch(p) - { - case name: - return (*i)->name(); - case fullName: - return (*i)->fullName(); - default: - return 0; - } -} - -void AudioIO::addFactory(AudioIOFactory *factory) -{ - if(!audioIOFactories) - audioIOFactories = new list; - - audioIOFactories->push_back(factory); -} - -void AudioIO::removeFactory(AudioIOFactory *factory) -{ - assert(audioIOFactories); - - audioIOFactories->remove(factory); - if(audioIOFactories->empty()) - { - delete audioIOFactories; - audioIOFactories = 0; - } -} - -void AudioIOFactory::startup() -{ - AudioIO::addFactory(this); -} - -void AudioIOFactory::shutdown() -{ - AudioIO::removeFactory(this); -} diff --git a/flow/audioio.cpp b/flow/audioio.cpp new file mode 100644 index 0000000..eafb197 --- /dev/null +++ b/flow/audioio.cpp @@ -0,0 +1,165 @@ + /* + + Copyright (C) 2000 Stefan Westerfeld + stefan@space.twc.de + + 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. + + */ + +#include "audioio.h" +#include +#include +#include +#include + +using namespace Arts; +using namespace std; + +class Arts::AudioIOPrivate { +public: + map paramMap; + map paramStrMap; +}; + +AudioIO::AudioIO() :d(new AudioIOPrivate) +{ +} + +AudioIO::~AudioIO() +{ + delete d; +} + +int& AudioIO::param(AudioParam param) +{ + /* + * returns d->paramMap[param], initializes new elements with -1 (for those + * parameters not handled explicitly by the derived AudioIO* class). + */ + + map::iterator pi = d->paramMap.find(param); + if(pi == d->paramMap.end()) + { + int& result = d->paramMap[param]; + result = -1; + return result; + } + else return pi->second; +} + +string& AudioIO::paramStr(AudioParam param) +{ + return d->paramStrMap[param]; +} + +void AudioIO::setParamStr(AudioParam p, const char *value) +{ + paramStr(p) = value; +} + +void AudioIO::setParam(AudioParam p, int& value) +{ + param(p) = value; +} + +int AudioIO::getParam(AudioParam p) +{ + return param(p); +} + +const char *AudioIO::getParamStr(AudioParam p) +{ + return paramStr(p).c_str(); +} + +/* ---- factories ---- */ + +static list *audioIOFactories = 0; + +AudioIO *AudioIO::createAudioIO(const char *name) +{ + if(audioIOFactories) + { + list::iterator i; + for(i = audioIOFactories->begin(); i != audioIOFactories->end(); i++) + { + AudioIOFactory *factory = *i; + + if(strcmp(factory->name(),name) == 0) + return factory->createAudioIO(); + } + } + return 0; +} + +int AudioIO::queryAudioIOCount() +{ + return audioIOFactories->size(); +} + +int AudioIO::queryAudioIOParam(int /*audioIO*/, AudioParam /*p*/) +{ + return 0; +} + +const char *AudioIO::queryAudioIOParamStr(int audioIO, AudioParam p) +{ + list::iterator i = audioIOFactories->begin(); + + while(audioIO && i != audioIOFactories->end()) { i++; audioIO--; } + if(i == audioIOFactories->end()) return 0; + + switch(p) + { + case name: + return (*i)->name(); + case fullName: + return (*i)->fullName(); + default: + return 0; + } +} + +void AudioIO::addFactory(AudioIOFactory *factory) +{ + if(!audioIOFactories) + audioIOFactories = new list; + + audioIOFactories->push_back(factory); +} + +void AudioIO::removeFactory(AudioIOFactory *factory) +{ + assert(audioIOFactories); + + audioIOFactories->remove(factory); + if(audioIOFactories->empty()) + { + delete audioIOFactories; + audioIOFactories = 0; + } +} + +void AudioIOFactory::startup() +{ + AudioIO::addFactory(this); +} + +void AudioIOFactory::shutdown() +{ + AudioIO::removeFactory(this); +} diff --git a/flow/audioioaix.cc b/flow/audioioaix.cc deleted file mode 100644 index b5c288b..0000000 --- a/flow/audioioaix.cc +++ /dev/null @@ -1,390 +0,0 @@ -/* - - 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 -#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 - diff --git a/flow/audioioaix.cpp b/flow/audioioaix.cpp new file mode 100644 index 0000000..b5c288b --- /dev/null +++ b/flow/audioioaix.cpp @@ -0,0 +1,390 @@ +/* + + 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 +#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 + diff --git a/flow/audioioalsa.cc b/flow/audioioalsa.cc deleted file mode 100644 index 5b8d485..0000000 --- a/flow/audioioalsa.cc +++ /dev/null @@ -1,561 +0,0 @@ - /* - - Copyright (C) 2000,2001 Jozef Kosoru - jozef.kosoru@pobox.sk - (C) 2000,2001 Stefan Westerfeld - stefan@space.twc.de - - 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 -#endif - -/** - * only compile 'alsa' AudioIO class if configure thinks it is a good idea - */ -#ifdef HAVE_LIBASOUND - -#ifdef HAVE_ALSA_ASOUNDLIB_H -#include -#elif defined(HAVE_SYS_ASOUNDLIB_H) -#include -#endif - -#include -#include -#include -#include - -#ifdef HAVE_SYS_SELECT_H -#include // Needed on some systems. -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "audioio.h" - -namespace Arts { - -class AudioIOALSA : public AudioIO { -protected: - int audio_read_fd; - int audio_write_fd; - int requestedFragmentSize; - int requestedFragmentCount; - - enum BufferMode{block, stream}; - int m_card; - int m_device; - int m_format; - BufferMode m_bufferMode; - - snd_pcm_t *m_pcm_handle; - snd_pcm_channel_info_t m_cinfo; - snd_pcm_format_t m_cformat; - snd_pcm_channel_params_t m_params; - snd_pcm_channel_setup_t m_setup; - - int setPcmParams(const int channel); - void checkCapabilities(); - -public: - AudioIOALSA(); - - 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(AudioIOALSA,"alsa","Advanced Linux Sound Architecture"); -}; - -using namespace std; -using namespace Arts; - -AudioIOALSA::AudioIOALSA() -{ - param(samplingRate) = 44100; - paramStr(deviceName) = "/dev/dsp"; //!! alsa doesn't need this - requestedFragmentSize = param(fragmentSize) = 1024; - requestedFragmentCount = param(fragmentCount) = 7; - param(channels) = 2; - param(direction) = directionWrite; - - /* - * default parameters - */ - m_card = snd_defaults_pcm_card(); //!! need interface !! - m_device = snd_defaults_pcm_device(); //!! -#ifdef WORDS_BIGENDIAN - m_format = SND_PCM_SFMT_S16_BE; -#else - m_format = SND_PCM_SFMT_S16_LE; -#endif - m_bufferMode = block; //block/stream (stream mode doesn't work yet) - - if(m_card >= 0) { - char* cardname = 0; - - if(snd_card_get_name(m_card, &cardname) == 0 && cardname != 0) - { - //!! thats not what devicename is intended to do - //!! devicename is an input information into - //!! the "driver", to select which card to use - //!! not an output information - paramStr(deviceName) = cardname; - free(cardname); - } - } -} - -bool AudioIOALSA::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& _direction = param(direction); - int& _format = param(format); - - /* - * initialize format - TODO: implement fallback (i.e. if no format given, - * it should try 16bit first, then fall back to 8bit) - */ - switch(_format) - { - default: _format = 16; - - case 16: // 16bit, signed little endian - m_format = SND_PCM_SFMT_S16_LE; - break; - - case 17: // 16bit, signed big endian - m_format = SND_PCM_SFMT_S16_BE; - break; - - case 8: // 8bit, unsigned - m_format = SND_PCM_SFMT_U8; - break; - } - - /* open pcm device */ - int mode = SND_PCM_OPEN_NONBLOCK; - - if(_direction == directionReadWrite) - mode |= SND_PCM_OPEN_DUPLEX; - else if(_direction == directionWrite) - mode |= SND_PCM_OPEN_PLAYBACK; - else - { - _error = "invalid direction"; - return false; - } - - int err; - if((err = snd_pcm_open(&m_pcm_handle, m_card, m_device, mode)) < 0) { - _error = "device: "; - _error += _deviceName.c_str(); - _error += " can't be opened ("; - _error += snd_strerror(err); - _error += ")"; - return false; - } - else { - artsdebug("ALSA driver: %s", _deviceName.c_str()); - } - - snd_pcm_nonblock_mode(m_pcm_handle, 0); - - /* flush buffers */ - (void)snd_pcm_capture_flush(m_pcm_handle); - if(_direction & directionRead) - (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE); - if(_direction & directionWrite) - (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK); - - /* check device capabilities */ - checkCapabilities(); - - /* set the fragment settings to what the user requested */ - _fragmentSize = requestedFragmentSize; - _fragmentCount = requestedFragmentCount; - - /* set PCM communication parameters */ - if((_direction & directionRead) && setPcmParams(SND_PCM_CHANNEL_CAPTURE)) - return false; - if((_direction & directionWrite) && setPcmParams(SND_PCM_CHANNEL_PLAYBACK)) - return false; - - /* prepare channel */ - if((_direction & directionRead) && - snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE) < 0) - { - _error = "Unable to prepare capture channel!"; - return false; - } - if((_direction & directionWrite) && - snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0) - { - _error = "Unable to prepare playback channel!"; - return false; - } - - /* obtain current PCM setup (may differ from requested one) */ - (void)memset(&m_setup, 0, sizeof(m_setup)); - - m_setup.channel = SND_PCM_CHANNEL_PLAYBACK; - if(snd_pcm_channel_setup(m_pcm_handle, &m_setup) < 0) { - _error = "Unable to obtain channel setup!"; - return false; - } - - /* check samplerate */ - const int tolerance = _samplingRate/10+1000; - if(abs(m_setup.format.rate-_samplingRate) > tolerance) - { - _error = "Can't set requested sampling rate!"; - char details[80]; - sprintf(details," (requested rate %d, got rate %d)", - _samplingRate, m_setup.format.rate); - _error += details; - return false; - } - _samplingRate = m_setup.format.rate; - - /* check format */ - if(m_setup.format.format != m_format) { - _error = "Can't set requested format:"; - _error += snd_pcm_get_format_name(m_format); - return false; - } - - /* check voices */ - if(m_setup.format.voices != _channels) { - _error = "Audio device doesn't support number of requested channels!"; - return false; - } - - /* update fragment settings with what we got */ - switch(m_bufferMode) { - case block: - _fragmentSize = m_setup.buf.block.frag_size; - _fragmentCount = m_setup.buf.block.frags_max-1; - break; - case stream: - _fragmentSize = m_setup.buf.stream.queue_size; - _fragmentCount = 1; - break; - } - - 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); - - /* obtain PCM file descriptor(s) */ - audio_read_fd = audio_write_fd = -1; - - if(_direction & directionRead) - audio_read_fd = snd_pcm_file_descriptor(m_pcm_handle, - SND_PCM_CHANNEL_CAPTURE); - if(_direction & directionWrite) - audio_write_fd = snd_pcm_file_descriptor(m_pcm_handle, - SND_PCM_CHANNEL_PLAYBACK); - - /* start recording */ - if((_direction & directionRead) && snd_pcm_capture_go(m_pcm_handle)) { - _error = "Can't start recording!"; - return false; - } - - return true; -} - -void AudioIOALSA::close() -{ - int& _direction = param(direction); - if(_direction & directionRead) - (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE); - if(_direction & directionWrite) - (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK); - (void)snd_pcm_close(m_pcm_handle); -} - -void AudioIOALSA::setParam(AudioParam p, int& value) -{ - switch(p) - { - case fragmentSize: - param(p) = requestedFragmentSize = value; - break; - case fragmentCount: - param(p) = requestedFragmentCount = value; - break; - default: - param(p) = value; - break; - } -} - -int AudioIOALSA::getParam(AudioParam p) -{ - snd_pcm_channel_status_t status; - (void)memset(&status, 0, sizeof(status)); - - switch(p) - { - case canRead: - status.channel = SND_PCM_CHANNEL_CAPTURE; - if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { - arts_warning("Capture channel status error!"); - return -1; - } - return status.free; - break; - - case canWrite: - status.channel = SND_PCM_CHANNEL_PLAYBACK; - if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { - arts_warning("Playback channel status error!"); - return -1; - } - return status.free; - break; - - case selectReadFD: - return audio_read_fd; - break; - - case selectWriteFD: - return audio_write_fd; - break; - - case autoDetect: - /* - * that the ALSA driver could be compiled doesn't say anything - * about whether it will work (the user might be using an OSS - * kernel driver) so we'll use a value less than the OSS one - * here, because OSS will most certainly work (ALSA's OSS emu) - */ - return 5; - break; - - default: - return param(p); - break; - } -} - -int AudioIOALSA::read(void *buffer, int size) -{ - int length; - do { - length = snd_pcm_read(m_pcm_handle, buffer, size); - } while (length == -EINTR); - if(length == -EPIPE) { - snd_pcm_channel_status_t status; - (void)memset(&status, 0, sizeof(status)); - status.channel = SND_PCM_CHANNEL_CAPTURE; - if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { - arts_info("Capture channel status error!"); - return -1; - } - else if(status.status == SND_PCM_STATUS_RUNNING) { - length = 0; - } - else if(status.status == SND_PCM_STATUS_OVERRUN) { - artsdebug("Overrun at position: %d" ,status.scount); - if(snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE)<0) - { - arts_info("Overrun: capture prepare error!"); - return -1; - } - length = 0; - } - else { - arts_info("Unknown capture error!"); - return -1; - } - } - else if(length < 0) { - arts_info("Capture error: %s", snd_strerror(length)); - return -1; - } - return length; -} - -int AudioIOALSA::write(void *buffer, int size) -{ - int length; - while((length = snd_pcm_write(m_pcm_handle, buffer, size)) != size) { - if (length == -EINTR) - continue; // Try again - snd_pcm_channel_status_t status; - (void)memset(&status, 0, sizeof(status)); - status.channel = SND_PCM_CHANNEL_PLAYBACK; - - if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { - arts_warning("Playback channel status error!"); - return -1; - } - else if(status.status == SND_PCM_STATUS_UNDERRUN) { - artsdebug("Underrun at position: %d", status.scount); - if(snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK) - < 0) { - arts_warning("Underrun: playback prepare error!"); - return -1; - } - } - else { - arts_warning("Unknown playback error!"); - return -1; - } - } - return size; -} - -int AudioIOALSA::setPcmParams(const int channel) -{ - int &_samplingRate = param(samplingRate); - int &_channels = param(channels); - int &_fragmentSize = param(fragmentSize); - int &_fragmentCount = param(fragmentCount); - - (void)memset(&m_cformat, 0, sizeof(m_cformat)); - m_cformat.interleave = 1; - m_cformat.format = m_format; - m_cformat.rate = _samplingRate; - m_cformat.voices = _channels; - - (void)memset(&m_params, 0, sizeof(m_params)); - switch(m_bufferMode){ - case stream: - m_params.mode=SND_PCM_MODE_STREAM; - break; - case block: - m_params.mode=SND_PCM_MODE_BLOCK; - break; - } - m_params.channel=channel; - (void)memcpy(&m_params.format, &m_cformat, sizeof(m_cformat)); - if(channel==SND_PCM_CHANNEL_CAPTURE){ - m_params.start_mode=SND_PCM_START_GO; - m_params.stop_mode=SND_PCM_STOP_ROLLOVER; - } - else{ //SND_PCM_CHANNEL_PLAYBACK - m_params.start_mode= (m_bufferMode==block) ? SND_PCM_START_FULL : SND_PCM_START_DATA; - m_params.stop_mode=SND_PCM_STOP_ROLLOVER; // SND_PCM_STOP_STOP - //use this ^^^ if you want to track underruns - } - - switch(m_bufferMode){ - case stream: - m_params.buf.stream.queue_size=1024*1024; //_fragmentSize*_fragmentCount; - m_params.buf.stream.fill=SND_PCM_FILL_SILENCE_WHOLE; - m_params.buf.stream.max_fill=1024; - break; - case block: - m_params.buf.block.frag_size=_fragmentSize; - if(channel==SND_PCM_CHANNEL_CAPTURE){ - m_params.buf.block.frags_max=1; - m_params.buf.block.frags_min=1; - } - else{ //SND_PCM_CHANNEL_PLAYBACK - m_params.buf.block.frags_max=_fragmentCount+1; - m_params.buf.block.frags_min=1; - } - } - if(snd_pcm_channel_params(m_pcm_handle, &m_params)<0){ - paramStr(lastError) = "Unable to set channel params!"; - return 1; - } - else { - return 0; - } -} - -void AudioIOALSA::checkCapabilities() -{ - snd_pcm_info_t info; - (void)memset(&info, 0, sizeof(info)); - if(!snd_pcm_info(m_pcm_handle, &info)) { - string flags = ""; - if(info.flags & SND_PCM_INFO_PLAYBACK) flags += "playback "; - if(info.flags & SND_PCM_INFO_CAPTURE) flags += "capture "; - if(info.flags & SND_PCM_INFO_DUPLEX) flags += "duplex "; - if(info.flags & SND_PCM_INFO_DUPLEX_RATE) flags += "duplex_rate "; - artsdebug(" type:%d id:%s\n" - " flags:%s\n" - " playback_subdevices:%d capture_subdevices:%d", - info.type, info.id, - flags.c_str(), - info.playback+1, info.capture+1); - } - else { - arts_warning("Can't get device info!"); //not fatal error - } - - (void)memset(&m_cinfo, 0, sizeof(m_cinfo)); - m_cinfo.channel = SND_PCM_CHANNEL_PLAYBACK; - if(!snd_pcm_channel_info(m_pcm_handle, &m_cinfo)) { - string flags = ""; - if(m_cinfo.flags & SND_PCM_CHNINFO_MMAP) flags += "mmap "; - if(m_cinfo.flags & SND_PCM_CHNINFO_STREAM) flags += "stream "; - if(m_cinfo.flags & SND_PCM_CHNINFO_BLOCK) flags += "block "; - if(m_cinfo.flags & SND_PCM_CHNINFO_BATCH) flags += "batch "; - if(m_cinfo.flags & SND_PCM_CHNINFO_INTERLEAVE) flags += "interleave "; - if(m_cinfo.flags & SND_PCM_CHNINFO_NONINTERLEAVE) flags += "noninterleave "; - if(m_cinfo.flags & SND_PCM_CHNINFO_BLOCK_TRANSFER) flags += "block_transfer "; - if(m_cinfo.flags & SND_PCM_CHNINFO_OVERRANGE) flags += "overrange "; - if(m_cinfo.flags & SND_PCM_CHNINFO_MMAP_VALID) flags += "mmap_valid "; - if(m_cinfo.flags & SND_PCM_CHNINFO_PAUSE) flags += "pause "; - - artsdebug(" subdevice:%d\n" - " flags:%s\n" - " min_rate:%d max_rate:%d\n" - " buffer_size:%d min_fragment_size:%d max_fragment_size:%d\n" - " fragment_align:%d fifo_size:%d transfer_block_size:%d\n" - " mmap_size:%d", - m_cinfo.subdevice, - flags.c_str(), - m_cinfo.min_rate, m_cinfo.max_rate, - m_cinfo.buffer_size, m_cinfo.min_fragment_size, m_cinfo.max_fragment_size, - m_cinfo.fragment_align, m_cinfo.fifo_size, m_cinfo.transfer_block_size, - m_cinfo.mmap_size); - } - else { - arts_warning("Can't get channel info!"); //not fatal error - } -} - -#endif /* HAVE_LIBASOUND */ diff --git a/flow/audioioalsa.cpp b/flow/audioioalsa.cpp new file mode 100644 index 0000000..5b8d485 --- /dev/null +++ b/flow/audioioalsa.cpp @@ -0,0 +1,561 @@ + /* + + Copyright (C) 2000,2001 Jozef Kosoru + jozef.kosoru@pobox.sk + (C) 2000,2001 Stefan Westerfeld + stefan@space.twc.de + + 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 +#endif + +/** + * only compile 'alsa' AudioIO class if configure thinks it is a good idea + */ +#ifdef HAVE_LIBASOUND + +#ifdef HAVE_ALSA_ASOUNDLIB_H +#include +#elif defined(HAVE_SYS_ASOUNDLIB_H) +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_SYS_SELECT_H +#include // Needed on some systems. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "audioio.h" + +namespace Arts { + +class AudioIOALSA : public AudioIO { +protected: + int audio_read_fd; + int audio_write_fd; + int requestedFragmentSize; + int requestedFragmentCount; + + enum BufferMode{block, stream}; + int m_card; + int m_device; + int m_format; + BufferMode m_bufferMode; + + snd_pcm_t *m_pcm_handle; + snd_pcm_channel_info_t m_cinfo; + snd_pcm_format_t m_cformat; + snd_pcm_channel_params_t m_params; + snd_pcm_channel_setup_t m_setup; + + int setPcmParams(const int channel); + void checkCapabilities(); + +public: + AudioIOALSA(); + + 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(AudioIOALSA,"alsa","Advanced Linux Sound Architecture"); +}; + +using namespace std; +using namespace Arts; + +AudioIOALSA::AudioIOALSA() +{ + param(samplingRate) = 44100; + paramStr(deviceName) = "/dev/dsp"; //!! alsa doesn't need this + requestedFragmentSize = param(fragmentSize) = 1024; + requestedFragmentCount = param(fragmentCount) = 7; + param(channels) = 2; + param(direction) = directionWrite; + + /* + * default parameters + */ + m_card = snd_defaults_pcm_card(); //!! need interface !! + m_device = snd_defaults_pcm_device(); //!! +#ifdef WORDS_BIGENDIAN + m_format = SND_PCM_SFMT_S16_BE; +#else + m_format = SND_PCM_SFMT_S16_LE; +#endif + m_bufferMode = block; //block/stream (stream mode doesn't work yet) + + if(m_card >= 0) { + char* cardname = 0; + + if(snd_card_get_name(m_card, &cardname) == 0 && cardname != 0) + { + //!! thats not what devicename is intended to do + //!! devicename is an input information into + //!! the "driver", to select which card to use + //!! not an output information + paramStr(deviceName) = cardname; + free(cardname); + } + } +} + +bool AudioIOALSA::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& _direction = param(direction); + int& _format = param(format); + + /* + * initialize format - TODO: implement fallback (i.e. if no format given, + * it should try 16bit first, then fall back to 8bit) + */ + switch(_format) + { + default: _format = 16; + + case 16: // 16bit, signed little endian + m_format = SND_PCM_SFMT_S16_LE; + break; + + case 17: // 16bit, signed big endian + m_format = SND_PCM_SFMT_S16_BE; + break; + + case 8: // 8bit, unsigned + m_format = SND_PCM_SFMT_U8; + break; + } + + /* open pcm device */ + int mode = SND_PCM_OPEN_NONBLOCK; + + if(_direction == directionReadWrite) + mode |= SND_PCM_OPEN_DUPLEX; + else if(_direction == directionWrite) + mode |= SND_PCM_OPEN_PLAYBACK; + else + { + _error = "invalid direction"; + return false; + } + + int err; + if((err = snd_pcm_open(&m_pcm_handle, m_card, m_device, mode)) < 0) { + _error = "device: "; + _error += _deviceName.c_str(); + _error += " can't be opened ("; + _error += snd_strerror(err); + _error += ")"; + return false; + } + else { + artsdebug("ALSA driver: %s", _deviceName.c_str()); + } + + snd_pcm_nonblock_mode(m_pcm_handle, 0); + + /* flush buffers */ + (void)snd_pcm_capture_flush(m_pcm_handle); + if(_direction & directionRead) + (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE); + if(_direction & directionWrite) + (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + + /* check device capabilities */ + checkCapabilities(); + + /* set the fragment settings to what the user requested */ + _fragmentSize = requestedFragmentSize; + _fragmentCount = requestedFragmentCount; + + /* set PCM communication parameters */ + if((_direction & directionRead) && setPcmParams(SND_PCM_CHANNEL_CAPTURE)) + return false; + if((_direction & directionWrite) && setPcmParams(SND_PCM_CHANNEL_PLAYBACK)) + return false; + + /* prepare channel */ + if((_direction & directionRead) && + snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE) < 0) + { + _error = "Unable to prepare capture channel!"; + return false; + } + if((_direction & directionWrite) && + snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0) + { + _error = "Unable to prepare playback channel!"; + return false; + } + + /* obtain current PCM setup (may differ from requested one) */ + (void)memset(&m_setup, 0, sizeof(m_setup)); + + m_setup.channel = SND_PCM_CHANNEL_PLAYBACK; + if(snd_pcm_channel_setup(m_pcm_handle, &m_setup) < 0) { + _error = "Unable to obtain channel setup!"; + return false; + } + + /* check samplerate */ + const int tolerance = _samplingRate/10+1000; + if(abs(m_setup.format.rate-_samplingRate) > tolerance) + { + _error = "Can't set requested sampling rate!"; + char details[80]; + sprintf(details," (requested rate %d, got rate %d)", + _samplingRate, m_setup.format.rate); + _error += details; + return false; + } + _samplingRate = m_setup.format.rate; + + /* check format */ + if(m_setup.format.format != m_format) { + _error = "Can't set requested format:"; + _error += snd_pcm_get_format_name(m_format); + return false; + } + + /* check voices */ + if(m_setup.format.voices != _channels) { + _error = "Audio device doesn't support number of requested channels!"; + return false; + } + + /* update fragment settings with what we got */ + switch(m_bufferMode) { + case block: + _fragmentSize = m_setup.buf.block.frag_size; + _fragmentCount = m_setup.buf.block.frags_max-1; + break; + case stream: + _fragmentSize = m_setup.buf.stream.queue_size; + _fragmentCount = 1; + break; + } + + 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); + + /* obtain PCM file descriptor(s) */ + audio_read_fd = audio_write_fd = -1; + + if(_direction & directionRead) + audio_read_fd = snd_pcm_file_descriptor(m_pcm_handle, + SND_PCM_CHANNEL_CAPTURE); + if(_direction & directionWrite) + audio_write_fd = snd_pcm_file_descriptor(m_pcm_handle, + SND_PCM_CHANNEL_PLAYBACK); + + /* start recording */ + if((_direction & directionRead) && snd_pcm_capture_go(m_pcm_handle)) { + _error = "Can't start recording!"; + return false; + } + + return true; +} + +void AudioIOALSA::close() +{ + int& _direction = param(direction); + if(_direction & directionRead) + (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE); + if(_direction & directionWrite) + (void)snd_pcm_channel_flush(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + (void)snd_pcm_close(m_pcm_handle); +} + +void AudioIOALSA::setParam(AudioParam p, int& value) +{ + switch(p) + { + case fragmentSize: + param(p) = requestedFragmentSize = value; + break; + case fragmentCount: + param(p) = requestedFragmentCount = value; + break; + default: + param(p) = value; + break; + } +} + +int AudioIOALSA::getParam(AudioParam p) +{ + snd_pcm_channel_status_t status; + (void)memset(&status, 0, sizeof(status)); + + switch(p) + { + case canRead: + status.channel = SND_PCM_CHANNEL_CAPTURE; + if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { + arts_warning("Capture channel status error!"); + return -1; + } + return status.free; + break; + + case canWrite: + status.channel = SND_PCM_CHANNEL_PLAYBACK; + if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { + arts_warning("Playback channel status error!"); + return -1; + } + return status.free; + break; + + case selectReadFD: + return audio_read_fd; + break; + + case selectWriteFD: + return audio_write_fd; + break; + + case autoDetect: + /* + * that the ALSA driver could be compiled doesn't say anything + * about whether it will work (the user might be using an OSS + * kernel driver) so we'll use a value less than the OSS one + * here, because OSS will most certainly work (ALSA's OSS emu) + */ + return 5; + break; + + default: + return param(p); + break; + } +} + +int AudioIOALSA::read(void *buffer, int size) +{ + int length; + do { + length = snd_pcm_read(m_pcm_handle, buffer, size); + } while (length == -EINTR); + if(length == -EPIPE) { + snd_pcm_channel_status_t status; + (void)memset(&status, 0, sizeof(status)); + status.channel = SND_PCM_CHANNEL_CAPTURE; + if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { + arts_info("Capture channel status error!"); + return -1; + } + else if(status.status == SND_PCM_STATUS_RUNNING) { + length = 0; + } + else if(status.status == SND_PCM_STATUS_OVERRUN) { + artsdebug("Overrun at position: %d" ,status.scount); + if(snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_CAPTURE)<0) + { + arts_info("Overrun: capture prepare error!"); + return -1; + } + length = 0; + } + else { + arts_info("Unknown capture error!"); + return -1; + } + } + else if(length < 0) { + arts_info("Capture error: %s", snd_strerror(length)); + return -1; + } + return length; +} + +int AudioIOALSA::write(void *buffer, int size) +{ + int length; + while((length = snd_pcm_write(m_pcm_handle, buffer, size)) != size) { + if (length == -EINTR) + continue; // Try again + snd_pcm_channel_status_t status; + (void)memset(&status, 0, sizeof(status)); + status.channel = SND_PCM_CHANNEL_PLAYBACK; + + if(snd_pcm_channel_status(m_pcm_handle, &status) < 0) { + arts_warning("Playback channel status error!"); + return -1; + } + else if(status.status == SND_PCM_STATUS_UNDERRUN) { + artsdebug("Underrun at position: %d", status.scount); + if(snd_pcm_channel_prepare(m_pcm_handle, SND_PCM_CHANNEL_PLAYBACK) + < 0) { + arts_warning("Underrun: playback prepare error!"); + return -1; + } + } + else { + arts_warning("Unknown playback error!"); + return -1; + } + } + return size; +} + +int AudioIOALSA::setPcmParams(const int channel) +{ + int &_samplingRate = param(samplingRate); + int &_channels = param(channels); + int &_fragmentSize = param(fragmentSize); + int &_fragmentCount = param(fragmentCount); + + (void)memset(&m_cformat, 0, sizeof(m_cformat)); + m_cformat.interleave = 1; + m_cformat.format = m_format; + m_cformat.rate = _samplingRate; + m_cformat.voices = _channels; + + (void)memset(&m_params, 0, sizeof(m_params)); + switch(m_bufferMode){ + case stream: + m_params.mode=SND_PCM_MODE_STREAM; + break; + case block: + m_params.mode=SND_PCM_MODE_BLOCK; + break; + } + m_params.channel=channel; + (void)memcpy(&m_params.format, &m_cformat, sizeof(m_cformat)); + if(channel==SND_PCM_CHANNEL_CAPTURE){ + m_params.start_mode=SND_PCM_START_GO; + m_params.stop_mode=SND_PCM_STOP_ROLLOVER; + } + else{ //SND_PCM_CHANNEL_PLAYBACK + m_params.start_mode= (m_bufferMode==block) ? SND_PCM_START_FULL : SND_PCM_START_DATA; + m_params.stop_mode=SND_PCM_STOP_ROLLOVER; // SND_PCM_STOP_STOP + //use this ^^^ if you want to track underruns + } + + switch(m_bufferMode){ + case stream: + m_params.buf.stream.queue_size=1024*1024; //_fragmentSize*_fragmentCount; + m_params.buf.stream.fill=SND_PCM_FILL_SILENCE_WHOLE; + m_params.buf.stream.max_fill=1024; + break; + case block: + m_params.buf.block.frag_size=_fragmentSize; + if(channel==SND_PCM_CHANNEL_CAPTURE){ + m_params.buf.block.frags_max=1; + m_params.buf.block.frags_min=1; + } + else{ //SND_PCM_CHANNEL_PLAYBACK + m_params.buf.block.frags_max=_fragmentCount+1; + m_params.buf.block.frags_min=1; + } + } + if(snd_pcm_channel_params(m_pcm_handle, &m_params)<0){ + paramStr(lastError) = "Unable to set channel params!"; + return 1; + } + else { + return 0; + } +} + +void AudioIOALSA::checkCapabilities() +{ + snd_pcm_info_t info; + (void)memset(&info, 0, sizeof(info)); + if(!snd_pcm_info(m_pcm_handle, &info)) { + string flags = ""; + if(info.flags & SND_PCM_INFO_PLAYBACK) flags += "playback "; + if(info.flags & SND_PCM_INFO_CAPTURE) flags += "capture "; + if(info.flags & SND_PCM_INFO_DUPLEX) flags += "duplex "; + if(info.flags & SND_PCM_INFO_DUPLEX_RATE) flags += "duplex_rate "; + artsdebug(" type:%d id:%s\n" + " flags:%s\n" + " playback_subdevices:%d capture_subdevices:%d", + info.type, info.id, + flags.c_str(), + info.playback+1, info.capture+1); + } + else { + arts_warning("Can't get device info!"); //not fatal error + } + + (void)memset(&m_cinfo, 0, sizeof(m_cinfo)); + m_cinfo.channel = SND_PCM_CHANNEL_PLAYBACK; + if(!snd_pcm_channel_info(m_pcm_handle, &m_cinfo)) { + string flags = ""; + if(m_cinfo.flags & SND_PCM_CHNINFO_MMAP) flags += "mmap "; + if(m_cinfo.flags & SND_PCM_CHNINFO_STREAM) flags += "stream "; + if(m_cinfo.flags & SND_PCM_CHNINFO_BLOCK) flags += "block "; + if(m_cinfo.flags & SND_PCM_CHNINFO_BATCH) flags += "batch "; + if(m_cinfo.flags & SND_PCM_CHNINFO_INTERLEAVE) flags += "interleave "; + if(m_cinfo.flags & SND_PCM_CHNINFO_NONINTERLEAVE) flags += "noninterleave "; + if(m_cinfo.flags & SND_PCM_CHNINFO_BLOCK_TRANSFER) flags += "block_transfer "; + if(m_cinfo.flags & SND_PCM_CHNINFO_OVERRANGE) flags += "overrange "; + if(m_cinfo.flags & SND_PCM_CHNINFO_MMAP_VALID) flags += "mmap_valid "; + if(m_cinfo.flags & SND_PCM_CHNINFO_PAUSE) flags += "pause "; + + artsdebug(" subdevice:%d\n" + " flags:%s\n" + " min_rate:%d max_rate:%d\n" + " buffer_size:%d min_fragment_size:%d max_fragment_size:%d\n" + " fragment_align:%d fifo_size:%d transfer_block_size:%d\n" + " mmap_size:%d", + m_cinfo.subdevice, + flags.c_str(), + m_cinfo.min_rate, m_cinfo.max_rate, + m_cinfo.buffer_size, m_cinfo.min_fragment_size, m_cinfo.max_fragment_size, + m_cinfo.fragment_align, m_cinfo.fifo_size, m_cinfo.transfer_block_size, + m_cinfo.mmap_size); + } + else { + arts_warning("Can't get channel info!"); //not fatal error + } +} + +#endif /* HAVE_LIBASOUND */ diff --git a/flow/audioioalsa9.cc b/flow/audioioalsa9.cc deleted file mode 100644 index 94c72ef..0000000 --- a/flow/audioioalsa9.cc +++ /dev/null @@ -1,590 +0,0 @@ - /* - - Copyright (C) 2001 Takashi Iwai - Copyright (C) 2004 Allan Sandfeld Jensen - - based on audioalsa.cc: - Copyright (C) 2000,2001 Jozef Kosoru - jozef.kosoru@pobox.sk - (C) 2000,2001 Stefan Westerfeld - stefan@space.twc.de - - 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 -#endif - -/** - * only compile 'alsa' AudioIO class if configure thinks it is a good idea - */ -#ifdef HAVE_LIBASOUND2 - -#ifdef HAVE_ALSA_ASOUNDLIB_H -#include -#elif defined(HAVE_SYS_ASOUNDLIB_H) -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "audioio.h" -#include "audiosubsys.h" -#include "dispatcher.h" -#include "iomanager.h" - -namespace Arts { - -class AudioIOALSA : public AudioIO, public IONotify { -protected: - // List of file descriptors - struct poll_descriptors { - poll_descriptors() : nfds(0), pfds(0) {}; - int nfds; - struct pollfd *pfds; - } audio_write_pds, audio_read_pds; - - snd_pcm_t *m_pcm_playback; - snd_pcm_t *m_pcm_capture; - snd_pcm_format_t m_format; - int m_period_size, m_periods; - - void startIO(); - int setPcmParams(snd_pcm_t *pcm); - static int poll2iomanager(int pollTypes); - static int iomanager2poll(int ioTypes); - void getDescriptors(snd_pcm_t *pcm, poll_descriptors *pds); - void watchDescriptors(poll_descriptors *pds); - - void notifyIO(int fd, int types); - - int xrun(snd_pcm_t *pcm); -#ifdef HAVE_SND_PCM_RESUME - int resume(snd_pcm_t *pcm); -#endif - -public: - AudioIOALSA(); - - 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(AudioIOALSA,"alsa","Advanced Linux Sound Architecture"); -} - -using namespace std; -using namespace Arts; - -AudioIOALSA::AudioIOALSA() -{ - param(samplingRate) = 44100; - paramStr(deviceName) = "default"; // ALSA pcm device name - not file name - param(fragmentSize) = 1024; - param(fragmentCount) = 7; - param(channels) = 2; - param(direction) = directionWrite; - param(format) = 16; - /* - * default parameters - */ - m_format = SND_PCM_FORMAT_S16_LE; - m_pcm_playback = NULL; - m_pcm_capture = NULL; -} - -bool AudioIOALSA::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& _direction = param(direction); - int& _format = param(format); - - m_pcm_playback = NULL; - m_pcm_capture = NULL; - - /* initialize format */ - switch(_format) { - case 16: // 16bit, signed little endian - m_format = SND_PCM_FORMAT_S16_LE; - break; - case 17: // 16bit, signed big endian - m_format = SND_PCM_FORMAT_S16_BE; - break; - case 8: // 8bit, unsigned - m_format = SND_PCM_FORMAT_U8; - break; - default: // test later - m_format = SND_PCM_FORMAT_UNKNOWN; - break; - } - - /* open pcm device */ - int err; - if (_direction & directionWrite) { - if ((err = snd_pcm_open(&m_pcm_playback, _deviceName.c_str(), - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { - _error = "device: "; - _error += _deviceName.c_str(); - _error += " can't be opened for playback ("; - _error += snd_strerror(err); - _error += ")"; - return false; - } - snd_pcm_nonblock(m_pcm_playback, 0); - } - if (_direction & directionRead) { - if ((err = snd_pcm_open(&m_pcm_capture, _deviceName.c_str(), - SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { - _error = "device: "; - _error += _deviceName.c_str(); - _error += " can't be opened for capture ("; - _error += snd_strerror(err); - _error += ")"; - snd_pcm_close(m_pcm_playback); - return false; - } - snd_pcm_nonblock(m_pcm_capture, 0); - } - - artsdebug("ALSA driver: %s", _deviceName.c_str()); - - /* check device capabilities */ - // checkCapabilities(); - - /* set PCM communication parameters */ - if (((_direction & directionWrite) && setPcmParams(m_pcm_playback)) || - ((_direction & directionRead) && setPcmParams(m_pcm_capture))) { - snd_pcm_close(m_pcm_playback); - snd_pcm_close(m_pcm_capture); - return false; - } - - 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); - - - startIO(); - /* restore the format value */ - switch (m_format) { - case SND_PCM_FORMAT_S16_LE: - _format = 16; - break; - case SND_PCM_FORMAT_S16_BE: - _format = 17; - break; - case SND_PCM_FORMAT_U8: - _format = 8; - break; - default: - _error = "Unknown PCM format"; - return false; - } - - /* start recording */ - if (_direction & directionRead) - snd_pcm_start(m_pcm_capture); - - return true; -} - -void AudioIOALSA::close() -{ - arts_debug("Closing ALSA-driver"); - int& _direction = param(direction); - if ((_direction & directionRead) && m_pcm_capture) { - (void)snd_pcm_drop(m_pcm_capture); - (void)snd_pcm_close(m_pcm_capture); - m_pcm_capture = NULL; - } - if ((_direction & directionWrite) && m_pcm_playback) { - (void)snd_pcm_drop(m_pcm_playback); - (void)snd_pcm_close(m_pcm_playback); - m_pcm_playback = NULL; - } - Dispatcher::the()->ioManager()->remove(this, IOType::all); - - delete[] audio_read_pds.pfds; - delete[] audio_write_pds.pfds; - audio_read_pds.pfds = NULL; audio_write_pds.pfds = NULL; - audio_read_pds.nfds = 0; audio_write_pds.nfds = 0; -} - -void AudioIOALSA::setParam(AudioParam p, int& value) -{ - param(p) = value; - if (m_pcm_playback != NULL) { - setPcmParams(m_pcm_playback); - } - if (m_pcm_capture != NULL) { - setPcmParams(m_pcm_capture); - } -} - -int AudioIOALSA::getParam(AudioParam p) -{ - snd_pcm_sframes_t avail; - switch(p) { - - case canRead: - if (! m_pcm_capture) return -1; - while ((avail = snd_pcm_avail_update(m_pcm_capture)) < 0) { - if (avail == -EPIPE) - avail = xrun(m_pcm_capture); -#ifdef HAVE_SND_PCM_RESUME - else if (avail == -ESTRPIPE) - avail = resume(m_pcm_capture); -#endif - if (avail < 0) { - arts_info("Capture error: %s", snd_strerror(avail)); - return -1; - } - } - return snd_pcm_frames_to_bytes(m_pcm_capture, avail); - - case canWrite: - if (! m_pcm_playback) return -1; - while ((avail = snd_pcm_avail_update(m_pcm_playback)) < 0) { - if (avail == -EPIPE) - avail = xrun(m_pcm_playback); -#ifdef HAVE_SND_PCM_RESUME - else if (avail == -ESTRPIPE) - avail = resume(m_pcm_playback); -#endif - if (avail < 0) { - arts_info("Playback error: %s", snd_strerror(avail)); - return -1; - } - } - return snd_pcm_frames_to_bytes(m_pcm_playback, avail); - - case selectReadFD: - return -1; - - case selectWriteFD: - return -1; - - case autoDetect: - { - /* - * that the ALSA driver could be compiled doesn't say anything - * about whether it will work (the user might be using an OSS - * kernel driver). - * If we can open the device, it'll work - and we'll have to use - * a higher number than OSS to avoid buggy OSS emulation being used. - */ - int card = -1; - if (snd_card_next(&card) < 0 || card < 0) { - // No ALSA drivers in use... - return 0; - } - return 15; - } - - default: - return param(p); - } -} - -void AudioIOALSA::startIO() -{ - /* get & watch PCM file descriptor(s) */ - if (m_pcm_playback) { - getDescriptors(m_pcm_playback, &audio_write_pds); - watchDescriptors(&audio_write_pds); - } - if (m_pcm_capture) { - getDescriptors(m_pcm_capture, &audio_read_pds); - watchDescriptors(&audio_read_pds); - } - -} - -int AudioIOALSA::poll2iomanager(int pollTypes) -{ - int types = 0; - - if(pollTypes & POLLIN) - types |= IOType::read; - if(pollTypes & POLLOUT) - types |= IOType::write; - if(pollTypes & POLLERR) - types |= IOType::except; - - return types; -} - -int AudioIOALSA::iomanager2poll(int ioTypes) -{ - int types = 0; - - if(ioTypes & IOType::read) - types |= POLLIN; - if(ioTypes & IOType::write) - types |= POLLOUT; - if(ioTypes & IOType::except) - types |= POLLERR; - - return types; -} - -void AudioIOALSA::getDescriptors(snd_pcm_t *pcm, poll_descriptors *pds) -{ - pds->nfds = snd_pcm_poll_descriptors_count(pcm); - pds->pfds = new struct pollfd[pds->nfds]; - - if (snd_pcm_poll_descriptors(pcm, pds->pfds, pds->nfds) != pds->nfds) { - arts_info("Cannot get poll descriptor(s)\n"); - } - -} - -void AudioIOALSA::watchDescriptors(poll_descriptors *pds) -{ - for(int i=0; infds; i++) { - // Check in which direction this handle is supposed to be watched - int types = poll2iomanager(pds->pfds[i].events); - Dispatcher::the()->ioManager()->watchFD(pds->pfds[i].fd, types, this); - } -} - -int AudioIOALSA::xrun(snd_pcm_t *pcm) -{ - int err; - artsdebug("xrun!!\n"); - if ((err = snd_pcm_prepare(pcm)) < 0) - return err; - if (pcm == m_pcm_capture) - snd_pcm_start(pcm); // ignore error here.. - return 0; -} - -#ifdef HAVE_SND_PCM_RESUME -int AudioIOALSA::resume(snd_pcm_t *pcm) -{ - int err; - artsdebug("resume!\n"); - while ((err = snd_pcm_resume(pcm)) == -EAGAIN) - sleep(1); /* wait until suspend flag is not released */ - if (err < 0) { - if ((err = snd_pcm_prepare(pcm)) < 0) - return err; - if (pcm == m_pcm_capture) - snd_pcm_start(pcm); // ignore error here.. - } - return 0; -} -#endif - -int AudioIOALSA::read(void *buffer, int size) -{ - int frames = snd_pcm_bytes_to_frames(m_pcm_capture, size); - int length; - while ((length = snd_pcm_readi(m_pcm_capture, buffer, frames)) < 0) { - if (length == -EINTR) - continue; // Try again - else if (length == -EPIPE) - length = xrun(m_pcm_capture); -#ifdef HAVE_SND_PCM_RESUME - else if (length == -ESTRPIPE) - length = resume(m_pcm_capture); -#endif - if (length < 0) { - arts_info("Capture error: %s", snd_strerror(length)); - return -1; - } - } - return snd_pcm_frames_to_bytes(m_pcm_capture, length); -} - -int AudioIOALSA::write(void *buffer, int size) -{ - int frames = snd_pcm_bytes_to_frames(m_pcm_playback, size); - int length; - while ((length = snd_pcm_writei(m_pcm_playback, buffer, frames)) < 0) { - if (length == -EINTR) - continue; // Try again - else if (length == -EPIPE) - length = xrun(m_pcm_playback); -#ifdef HAVE_SND_PCM_RESUME - else if (length == -ESTRPIPE) - length = resume(m_pcm_playback); -#endif - if (length < 0) { - arts_info("Playback error: %s", snd_strerror(length)); - return -1; - } - } - - // Start the sink if it needs it - if (snd_pcm_state( m_pcm_playback ) == SND_PCM_STATE_PREPARED) - snd_pcm_start(m_pcm_playback); - - if (length == frames) // Sometimes the fragments are "odd" in alsa - return size; - else - return snd_pcm_frames_to_bytes(m_pcm_playback, length); -} - -void AudioIOALSA::notifyIO(int fd, int type) -{ - int todo = 0; - - // Translate from iomanager-types to poll-types, - // inorder to fake a snd_pcm_poll_descriptors_revents call. - if(m_pcm_playback) { - for(int i=0; i < audio_write_pds.nfds; i++) { - if(fd == audio_write_pds.pfds[i].fd) { - audio_write_pds.pfds[i].revents = iomanager2poll(type); - todo |= AudioSubSystem::ioWrite; - } - } - if (todo & AudioSubSystem::ioWrite) { - unsigned short revents; - snd_pcm_poll_descriptors_revents(m_pcm_playback, - audio_write_pds.pfds, - audio_write_pds.nfds, - &revents); - if (! (revents & POLLOUT)) todo &= ~AudioSubSystem::ioWrite; - } - } - if(m_pcm_capture) { - for(int i=0; i < audio_read_pds.nfds; i++) { - if(fd == audio_read_pds.pfds[i].fd) { - audio_read_pds.pfds[i].revents = iomanager2poll(type); - todo |= AudioSubSystem::ioRead; - } - } - if (todo & AudioSubSystem::ioRead) { - unsigned short revents; - snd_pcm_poll_descriptors_revents(m_pcm_capture, - audio_read_pds.pfds, - audio_read_pds.nfds, - &revents); - if (! (revents & POLLIN)) todo &= ~AudioSubSystem::ioRead; - } - } - - if (type & IOType::except) todo |= AudioSubSystem::ioExcept; - - if (todo != 0) AudioSubSystem::the()->handleIO(todo); -} - -int AudioIOALSA::setPcmParams(snd_pcm_t *pcm) -{ - int &_samplingRate = param(samplingRate); - int &_channels = param(channels); - int &_fragmentSize = param(fragmentSize); - int &_fragmentCount = param(fragmentCount); - string& _error = paramStr(lastError); - - snd_pcm_hw_params_t *hw; - snd_pcm_hw_params_alloca(&hw); - snd_pcm_hw_params_any(pcm, hw); - - if (snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { - _error = "Unable to set interleaved!"; - return 1; - } - if (m_format == SND_PCM_FORMAT_UNKNOWN) { - // test the available formats - // try 16bit first, then fall back to 8bit - if (! snd_pcm_hw_params_test_format(pcm, hw, SND_PCM_FORMAT_S16_LE)) - m_format = SND_PCM_FORMAT_S16_LE; - else if (! snd_pcm_hw_params_test_format(pcm, hw, SND_PCM_FORMAT_S16_BE)) - m_format = SND_PCM_FORMAT_S16_BE; - else if (! snd_pcm_hw_params_test_format(pcm, hw, SND_PCM_FORMAT_U8)) - m_format = SND_PCM_FORMAT_U8; - else - m_format = SND_PCM_FORMAT_UNKNOWN; - } - if (snd_pcm_hw_params_set_format(pcm, hw, m_format) < 0) { - _error = "Unable to set format!"; - return 1; - } - - unsigned int rate = snd_pcm_hw_params_set_rate_near(pcm, hw, _samplingRate, 0); - const unsigned int tolerance = _samplingRate/10+1000; - if (abs((int)rate - (int)_samplingRate) > (int)tolerance) { - _error = "Can't set requested sampling rate!"; - char details[80]; - sprintf(details," (requested rate %d, got rate %d)", - _samplingRate, rate); - _error += details; - return 1; - } - _samplingRate = rate; - - if (snd_pcm_hw_params_set_channels(pcm, hw, _channels) < 0) { - _error = "Unable to set channels!"; - return 1; - } - - m_period_size = _fragmentSize; - if (m_format != SND_PCM_FORMAT_U8) - m_period_size <<= 1; - if (_channels > 1) - m_period_size /= _channels; - if ((m_period_size = snd_pcm_hw_params_set_period_size_near(pcm, hw, m_period_size, 0)) < 0) { - _error = "Unable to set period size!"; - return 1; - } - m_periods = _fragmentCount; - if ((m_periods = snd_pcm_hw_params_set_periods_near(pcm, hw, m_periods, 0)) < 0) { - _error = "Unable to set periods!"; - return 1; - } - - if (snd_pcm_hw_params(pcm, hw) < 0) { - _error = "Unable to set hw params!"; - return 1; - } - - _fragmentSize = m_period_size; - _fragmentCount = m_periods; - if (m_format != SND_PCM_FORMAT_U8) - _fragmentSize >>= 1; - if (_channels > 1) - _fragmentSize *= _channels; - - return 0; // ok, we're ready.. -} - -#endif /* HAVE_LIBASOUND2 */ diff --git a/flow/audioioalsa9.cpp b/flow/audioioalsa9.cpp new file mode 100644 index 0000000..1c8b3a5 --- /dev/null +++ b/flow/audioioalsa9.cpp @@ -0,0 +1,590 @@ + /* + + Copyright (C) 2001 Takashi Iwai + Copyright (C) 2004 Allan Sandfeld Jensen + + based on audioalsa.cpp: + Copyright (C) 2000,2001 Jozef Kosoru + jozef.kosoru@pobox.sk + (C) 2000,2001 Stefan Westerfeld + stefan@space.twc.de + + 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 +#endif + +/** + * only compile 'alsa' AudioIO class if configure thinks it is a good idea + */ +#ifdef HAVE_LIBASOUND2 + +#ifdef HAVE_ALSA_ASOUNDLIB_H +#include +#elif defined(HAVE_SYS_ASOUNDLIB_H) +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "audioio.h" +#include "audiosubsys.h" +#include "dispatcher.h" +#include "iomanager.h" + +namespace Arts { + +class AudioIOALSA : public AudioIO, public IONotify { +protected: + // List of file descriptors + struct poll_descriptors { + poll_descriptors() : nfds(0), pfds(0) {}; + int nfds; + struct pollfd *pfds; + } audio_write_pds, audio_read_pds; + + snd_pcm_t *m_pcm_playback; + snd_pcm_t *m_pcm_capture; + snd_pcm_format_t m_format; + int m_period_size, m_periods; + + void startIO(); + int setPcmParams(snd_pcm_t *pcm); + static int poll2iomanager(int pollTypes); + static int iomanager2poll(int ioTypes); + void getDescriptors(snd_pcm_t *pcm, poll_descriptors *pds); + void watchDescriptors(poll_descriptors *pds); + + void notifyIO(int fd, int types); + + int xrun(snd_pcm_t *pcm); +#ifdef HAVE_SND_PCM_RESUME + int resume(snd_pcm_t *pcm); +#endif + +public: + AudioIOALSA(); + + 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(AudioIOALSA,"alsa","Advanced Linux Sound Architecture"); +} + +using namespace std; +using namespace Arts; + +AudioIOALSA::AudioIOALSA() +{ + param(samplingRate) = 44100; + paramStr(deviceName) = "default"; // ALSA pcm device name - not file name + param(fragmentSize) = 1024; + param(fragmentCount) = 7; + param(channels) = 2; + param(direction) = directionWrite; + param(format) = 16; + /* + * default parameters + */ + m_format = SND_PCM_FORMAT_S16_LE; + m_pcm_playback = NULL; + m_pcm_capture = NULL; +} + +bool AudioIOALSA::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& _direction = param(direction); + int& _format = param(format); + + m_pcm_playback = NULL; + m_pcm_capture = NULL; + + /* initialize format */ + switch(_format) { + case 16: // 16bit, signed little endian + m_format = SND_PCM_FORMAT_S16_LE; + break; + case 17: // 16bit, signed big endian + m_format = SND_PCM_FORMAT_S16_BE; + break; + case 8: // 8bit, unsigned + m_format = SND_PCM_FORMAT_U8; + break; + default: // test later + m_format = SND_PCM_FORMAT_UNKNOWN; + break; + } + + /* open pcm device */ + int err; + if (_direction & directionWrite) { + if ((err = snd_pcm_open(&m_pcm_playback, _deviceName.c_str(), + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { + _error = "device: "; + _error += _deviceName.c_str(); + _error += " can't be opened for playback ("; + _error += snd_strerror(err); + _error += ")"; + return false; + } + snd_pcm_nonblock(m_pcm_playback, 0); + } + if (_direction & directionRead) { + if ((err = snd_pcm_open(&m_pcm_capture, _deviceName.c_str(), + SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { + _error = "device: "; + _error += _deviceName.c_str(); + _error += " can't be opened for capture ("; + _error += snd_strerror(err); + _error += ")"; + snd_pcm_close(m_pcm_playback); + return false; + } + snd_pcm_nonblock(m_pcm_capture, 0); + } + + artsdebug("ALSA driver: %s", _deviceName.c_str()); + + /* check device capabilities */ + // checkCapabilities(); + + /* set PCM communication parameters */ + if (((_direction & directionWrite) && setPcmParams(m_pcm_playback)) || + ((_direction & directionRead) && setPcmParams(m_pcm_capture))) { + snd_pcm_close(m_pcm_playback); + snd_pcm_close(m_pcm_capture); + return false; + } + + 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); + + + startIO(); + /* restore the format value */ + switch (m_format) { + case SND_PCM_FORMAT_S16_LE: + _format = 16; + break; + case SND_PCM_FORMAT_S16_BE: + _format = 17; + break; + case SND_PCM_FORMAT_U8: + _format = 8; + break; + default: + _error = "Unknown PCM format"; + return false; + } + + /* start recording */ + if (_direction & directionRead) + snd_pcm_start(m_pcm_capture); + + return true; +} + +void AudioIOALSA::close() +{ + arts_debug("Closing ALSA-driver"); + int& _direction = param(direction); + if ((_direction & directionRead) && m_pcm_capture) { + (void)snd_pcm_drop(m_pcm_capture); + (void)snd_pcm_close(m_pcm_capture); + m_pcm_capture = NULL; + } + if ((_direction & directionWrite) && m_pcm_playback) { + (void)snd_pcm_drop(m_pcm_playback); + (void)snd_pcm_close(m_pcm_playback); + m_pcm_playback = NULL; + } + Dispatcher::the()->ioManager()->remove(this, IOType::all); + + delete[] audio_read_pds.pfds; + delete[] audio_write_pds.pfds; + audio_read_pds.pfds = NULL; audio_write_pds.pfds = NULL; + audio_read_pds.nfds = 0; audio_write_pds.nfds = 0; +} + +void AudioIOALSA::setParam(AudioParam p, int& value) +{ + param(p) = value; + if (m_pcm_playback != NULL) { + setPcmParams(m_pcm_playback); + } + if (m_pcm_capture != NULL) { + setPcmParams(m_pcm_capture); + } +} + +int AudioIOALSA::getParam(AudioParam p) +{ + snd_pcm_sframes_t avail; + switch(p) { + + case canRead: + if (! m_pcm_capture) return -1; + while ((avail = snd_pcm_avail_update(m_pcm_capture)) < 0) { + if (avail == -EPIPE) + avail = xrun(m_pcm_capture); +#ifdef HAVE_SND_PCM_RESUME + else if (avail == -ESTRPIPE) + avail = resume(m_pcm_capture); +#endif + if (avail < 0) { + arts_info("Capture error: %s", snd_strerror(avail)); + return -1; + } + } + return snd_pcm_frames_to_bytes(m_pcm_capture, avail); + + case canWrite: + if (! m_pcm_playback) return -1; + while ((avail = snd_pcm_avail_update(m_pcm_playback)) < 0) { + if (avail == -EPIPE) + avail = xrun(m_pcm_playback); +#ifdef HAVE_SND_PCM_RESUME + else if (avail == -ESTRPIPE) + avail = resume(m_pcm_playback); +#endif + if (avail < 0) { + arts_info("Playback error: %s", snd_strerror(avail)); + return -1; + } + } + return snd_pcm_frames_to_bytes(m_pcm_playback, avail); + + case selectReadFD: + return -1; + + case selectWriteFD: + return -1; + + case autoDetect: + { + /* + * that the ALSA driver could be compiled doesn't say anything + * about whether it will work (the user might be using an OSS + * kernel driver). + * If we can open the device, it'll work - and we'll have to use + * a higher number than OSS to avoid buggy OSS emulation being used. + */ + int card = -1; + if (snd_card_next(&card) < 0 || card < 0) { + // No ALSA drivers in use... + return 0; + } + return 15; + } + + default: + return param(p); + } +} + +void AudioIOALSA::startIO() +{ + /* get & watch PCM file descriptor(s) */ + if (m_pcm_playback) { + getDescriptors(m_pcm_playback, &audio_write_pds); + watchDescriptors(&audio_write_pds); + } + if (m_pcm_capture) { + getDescriptors(m_pcm_capture, &audio_read_pds); + watchDescriptors(&audio_read_pds); + } + +} + +int AudioIOALSA::poll2iomanager(int pollTypes) +{ + int types = 0; + + if(pollTypes & POLLIN) + types |= IOType::read; + if(pollTypes & POLLOUT) + types |= IOType::write; + if(pollTypes & POLLERR) + types |= IOType::except; + + return types; +} + +int AudioIOALSA::iomanager2poll(int ioTypes) +{ + int types = 0; + + if(ioTypes & IOType::read) + types |= POLLIN; + if(ioTypes & IOType::write) + types |= POLLOUT; + if(ioTypes & IOType::except) + types |= POLLERR; + + return types; +} + +void AudioIOALSA::getDescriptors(snd_pcm_t *pcm, poll_descriptors *pds) +{ + pds->nfds = snd_pcm_poll_descriptors_count(pcm); + pds->pfds = new struct pollfd[pds->nfds]; + + if (snd_pcm_poll_descriptors(pcm, pds->pfds, pds->nfds) != pds->nfds) { + arts_info("Cannot get poll descriptor(s)\n"); + } + +} + +void AudioIOALSA::watchDescriptors(poll_descriptors *pds) +{ + for(int i=0; infds; i++) { + // Check in which direction this handle is supposed to be watched + int types = poll2iomanager(pds->pfds[i].events); + Dispatcher::the()->ioManager()->watchFD(pds->pfds[i].fd, types, this); + } +} + +int AudioIOALSA::xrun(snd_pcm_t *pcm) +{ + int err; + artsdebug("xrun!!\n"); + if ((err = snd_pcm_prepare(pcm)) < 0) + return err; + if (pcm == m_pcm_capture) + snd_pcm_start(pcm); // ignore error here.. + return 0; +} + +#ifdef HAVE_SND_PCM_RESUME +int AudioIOALSA::resume(snd_pcm_t *pcm) +{ + int err; + artsdebug("resume!\n"); + while ((err = snd_pcm_resume(pcm)) == -EAGAIN) + sleep(1); /* wait until suspend flag is not released */ + if (err < 0) { + if ((err = snd_pcm_prepare(pcm)) < 0) + return err; + if (pcm == m_pcm_capture) + snd_pcm_start(pcm); // ignore error here.. + } + return 0; +} +#endif + +int AudioIOALSA::read(void *buffer, int size) +{ + int frames = snd_pcm_bytes_to_frames(m_pcm_capture, size); + int length; + while ((length = snd_pcm_readi(m_pcm_capture, buffer, frames)) < 0) { + if (length == -EINTR) + continue; // Try again + else if (length == -EPIPE) + length = xrun(m_pcm_capture); +#ifdef HAVE_SND_PCM_RESUME + else if (length == -ESTRPIPE) + length = resume(m_pcm_capture); +#endif + if (length < 0) { + arts_info("Capture error: %s", snd_strerror(length)); + return -1; + } + } + return snd_pcm_frames_to_bytes(m_pcm_capture, length); +} + +int AudioIOALSA::write(void *buffer, int size) +{ + int frames = snd_pcm_bytes_to_frames(m_pcm_playback, size); + int length; + while ((length = snd_pcm_writei(m_pcm_playback, buffer, frames)) < 0) { + if (length == -EINTR) + continue; // Try again + else if (length == -EPIPE) + length = xrun(m_pcm_playback); +#ifdef HAVE_SND_PCM_RESUME + else if (length == -ESTRPIPE) + length = resume(m_pcm_playback); +#endif + if (length < 0) { + arts_info("Playback error: %s", snd_strerror(length)); + return -1; + } + } + + // Start the sink if it needs it + if (snd_pcm_state( m_pcm_playback ) == SND_PCM_STATE_PREPARED) + snd_pcm_start(m_pcm_playback); + + if (length == frames) // Sometimes the fragments are "odd" in alsa + return size; + else + return snd_pcm_frames_to_bytes(m_pcm_playback, length); +} + +void AudioIOALSA::notifyIO(int fd, int type) +{ + int todo = 0; + + // Translate from iomanager-types to poll-types, + // inorder to fake a snd_pcm_poll_descriptors_revents call. + if(m_pcm_playback) { + for(int i=0; i < audio_write_pds.nfds; i++) { + if(fd == audio_write_pds.pfds[i].fd) { + audio_write_pds.pfds[i].revents = iomanager2poll(type); + todo |= AudioSubSystem::ioWrite; + } + } + if (todo & AudioSubSystem::ioWrite) { + unsigned short revents; + snd_pcm_poll_descriptors_revents(m_pcm_playback, + audio_write_pds.pfds, + audio_write_pds.nfds, + &revents); + if (! (revents & POLLOUT)) todo &= ~AudioSubSystem::ioWrite; + } + } + if(m_pcm_capture) { + for(int i=0; i < audio_read_pds.nfds; i++) { + if(fd == audio_read_pds.pfds[i].fd) { + audio_read_pds.pfds[i].revents = iomanager2poll(type); + todo |= AudioSubSystem::ioRead; + } + } + if (todo & AudioSubSystem::ioRead) { + unsigned short revents; + snd_pcm_poll_descriptors_revents(m_pcm_capture, + audio_read_pds.pfds, + audio_read_pds.nfds, + &revents); + if (! (revents & POLLIN)) todo &= ~AudioSubSystem::ioRead; + } + } + + if (type & IOType::except) todo |= AudioSubSystem::ioExcept; + + if (todo != 0) AudioSubSystem::the()->handleIO(todo); +} + +int AudioIOALSA::setPcmParams(snd_pcm_t *pcm) +{ + int &_samplingRate = param(samplingRate); + int &_channels = param(channels); + int &_fragmentSize = param(fragmentSize); + int &_fragmentCount = param(fragmentCount); + string& _error = paramStr(lastError); + + snd_pcm_hw_params_t *hw; + snd_pcm_hw_params_alloca(&hw); + snd_pcm_hw_params_any(pcm, hw); + + if (snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { + _error = "Unable to set interleaved!"; + return 1; + } + if (m_format == SND_PCM_FORMAT_UNKNOWN) { + // test the available formats + // try 16bit first, then fall back to 8bit + if (! snd_pcm_hw_params_test_format(pcm, hw, SND_PCM_FORMAT_S16_LE)) + m_format = SND_PCM_FORMAT_S16_LE; + else if (! snd_pcm_hw_params_test_format(pcm, hw, SND_PCM_FORMAT_S16_BE)) + m_format = SND_PCM_FORMAT_S16_BE; + else if (! snd_pcm_hw_params_test_format(pcm, hw, SND_PCM_FORMAT_U8)) + m_format = SND_PCM_FORMAT_U8; + else + m_format = SND_PCM_FORMAT_UNKNOWN; + } + if (snd_pcm_hw_params_set_format(pcm, hw, m_format) < 0) { + _error = "Unable to set format!"; + return 1; + } + + unsigned int rate = snd_pcm_hw_params_set_rate_near(pcm, hw, _samplingRate, 0); + const unsigned int tolerance = _samplingRate/10+1000; + if (abs((int)rate - (int)_samplingRate) > (int)tolerance) { + _error = "Can't set requested sampling rate!"; + char details[80]; + sprintf(details," (requested rate %d, got rate %d)", + _samplingRate, rate); + _error += details; + return 1; + } + _samplingRate = rate; + + if (snd_pcm_hw_params_set_channels(pcm, hw, _channels) < 0) { + _error = "Unable to set channels!"; + return 1; + } + + m_period_size = _fragmentSize; + if (m_format != SND_PCM_FORMAT_U8) + m_period_size <<= 1; + if (_channels > 1) + m_period_size /= _channels; + if ((m_period_size = snd_pcm_hw_params_set_period_size_near(pcm, hw, m_period_size, 0)) < 0) { + _error = "Unable to set period size!"; + return 1; + } + m_periods = _fragmentCount; + if ((m_periods = snd_pcm_hw_params_set_periods_near(pcm, hw, m_periods, 0)) < 0) { + _error = "Unable to set periods!"; + return 1; + } + + if (snd_pcm_hw_params(pcm, hw) < 0) { + _error = "Unable to set hw params!"; + return 1; + } + + _fragmentSize = m_period_size; + _fragmentCount = m_periods; + if (m_format != SND_PCM_FORMAT_U8) + _fragmentSize >>= 1; + if (_channels > 1) + _fragmentSize *= _channels; + + return 0; // ok, we're ready.. +} + +#endif /* HAVE_LIBASOUND2 */ diff --git a/flow/audioiocsl.cc b/flow/audioiocsl.cc deleted file mode 100644 index 7f3d5dd..0000000 --- a/flow/audioiocsl.cc +++ /dev/null @@ -1,640 +0,0 @@ - /* - - Copyright (C) 2000-2002 Stefan Westerfeld - stefan@space.twc.de - - 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 -#endif - -/** - * only compile 'csl' AudioIO class if libcsl is present - */ -#ifdef HAVE_LIBCSL -#include - -/* g_newa */ -#include - -#include - -#include "audioio.h" -#include "audiosubsys.h" -#include "debug.h" -#include "dispatcher.h" -#include "iomanager.h" - -namespace Arts { - -class AudioIOCSL : public AudioIO, - public IONotify -{ -protected: - CslPcmStream *inputStream, *outputStream; - CslDriver *cslDriver; - int requestedFragmentSize; - int requestedFragmentCount; - const char *cslDriverName; - - std::vector cslFds, cslOldFds; - int csl2iomanager(int cslTypes); - void updateFds(); - - void notifyIO(int fd, int types); - static void handleRead(void *user_data, CslPcmStream *stream); - static void handleWrite(void *user_data, CslPcmStream *stream); - -public: - AudioIOCSL(const char *driverName = 0); - - 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(AudioIOCSL,"csl","Common Sound Layer"); - -class AudioIOCSLFactory : public AudioIOFactory { -protected: - const char *driverName; - std::string _name; - std::string _fullName; - -public: - AudioIOCSLFactory(const char *driverName) - : driverName(driverName) - { - _name = "csl-"; - _name += driverName; - - _fullName = "Common Sound Layer ("; - _fullName += driverName; - _fullName += ")"; - } - virtual ~AudioIOCSLFactory() - { - } - AudioIO *createAudioIO() { return new AudioIOCSL(driverName); } - virtual const char *name() { return _name.c_str(); } - virtual const char *fullName() { return _fullName.c_str(); } -}; - -static class AudioIOCSLInit { -protected: - std::list factories; - -public: - AudioIOCSLInit() - { - unsigned int i,n; - const char **drivers = csl_list_drivers(&n); - - for(i = 0; i < n; i++) - factories.push_back(new AudioIOCSLFactory(drivers[i])); - } - ~AudioIOCSLInit() - { - std::list::iterator i; - for(i = factories.begin(); i != factories.end(); i++) - delete (*i); - - factories.clear(); - } -} aci; - -}; - -using namespace std; -using namespace Arts; - -AudioIOCSL::AudioIOCSL(const char *driverName) - : cslDriverName(driverName) -{ - /* - * default parameters - */ - param(samplingRate) = 44100; - paramStr(deviceName) = "todo"; - requestedFragmentSize = param(fragmentSize) = 1024; - requestedFragmentCount = param(fragmentCount) = 7; - param(channels) = 2; - param(direction) = 2; - -#ifdef WORDS_BIGENDIAN - param(format) = 17; -#else - param(format) = 16; -#endif -} - -bool AudioIOCSL::open() -{ - string& errorMsg = 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 fmt = 0; - char *env = 0; - - if(cslDriverName && strcmp(cslDriverName, "arts") == 0) - env = arts_strdup_printf("ARTS_SERVER=%s",_deviceName.c_str()); - - if(env) - { - putenv(env); - arts_debug("AudioIOCsl: set %s\n",env); - } - - CslErrorType error; - error = csl_driver_init(cslDriverName, &cslDriver); /* choose backend */ - - if(env) - { - putenv("ARTS_SERVER"); - free(env); - } - - if (error) - { - errorMsg = "unable to initialize CSL driver: "; - errorMsg += csl_strerror(error); - return false; - } - - if(_format == 8) - fmt = CSL_PCM_FORMAT_U8; - else if(_format == 16) - fmt = CSL_PCM_FORMAT_S16_LE; - else if(_format == 17) - fmt = CSL_PCM_FORMAT_S16_BE; - - inputStream = outputStream = 0; - if(param(direction) & directionRead) - { - /* open PCM output stream */ - error = csl_pcm_open_output(cslDriver, - "artsd output", - _samplingRate, - _channels, - fmt, &inputStream); - if (error) - { - errorMsg = "failed to open CSL input stream: "; - errorMsg += csl_strerror(error); - return false; - } - csl_pcm_set_callback(inputStream, handleRead, 0, 0); - } - if(param(direction) & directionWrite) - { - /* open PCM output stream */ - error = csl_pcm_open_output(cslDriver, - "artsd output", - _samplingRate, - _channels, - fmt, &outputStream); - if (error) - { - close(); - - errorMsg = "failed to open CSL output stream: "; - errorMsg += csl_strerror(error); - return false; - } - csl_pcm_set_callback(outputStream, handleWrite, 0, 0); - } -#if 0 - if (_format && (ossBits(gotFormat) != ossBits(requestedFormat))) - { - char details[80]; - sprintf(details," (_format = %d, asked driver to give %d, got %d)", - _format, requestedFormat, gotFormat); - - _error = "Can't set playback format"; - _error += details; - - close(); - return false; - } - - if(gotFormat == AFMT_U8) - _format = 8; - else if(gotFormat == AFMT_S16_LE) - _format = 16; - else if(gotFormat == AFMT_S16_BE) - _format = 17; - else - { - char details[80]; - sprintf(details," (_format = %d, asked driver to give %d, got %d)", - _format, requestedFormat, gotFormat); - - _error = "unknown format given by driver"; - _error += details; - - close(); - return false; - } - - - int stereo=-1; /* 0=mono, 1=stereo */ - - if(_channels == 1) - { - stereo = 0; - } - if(_channels == 2) - { - stereo = 1; - } - - if(stereo == -1) - { - _error = "internal error; set channels to 1 (mono) or 2 (stereo)"; - - close(); - return false; - } - - int requeststereo = stereo; - - if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1) - { - _error = "SNDCTL_DSP_STEREO failed - "; - _error += strerror(errno); - - close(); - return false; - } - - if (requeststereo != stereo) - { - _error = "audio device doesn't support number of requested channels"; - - close(); - return false; - } - - int speed = _samplingRate; - - if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) - { - _error = "SNDCTL_DSP_SPEED failed - "; - _error += strerror(errno); - - close(); - return false; - } - - /* - * 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(speed-_samplingRate) > tolerance) - { - _error = "can't set requested samplingrate"; - - char details[80]; - sprintf(details," (requested rate %d, got rate %d)", - _samplingRate, speed); - _error += details; - - close(); - return false; - } - _samplingRate = speed; - - /* - * set the fragment settings to what the user requested - */ - - _fragmentSize = requestedFragmentSize; - _fragmentCount = requestedFragmentCount; - - /* - * lower 16 bits are the fragment size (as 2^S) - * higher 16 bits are the number of fragments - */ - int frag_arg = 0; - - int size = _fragmentSize; - while(size > 1) { size /= 2; frag_arg++; } - frag_arg += (_fragmentCount << 16); - if(ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_arg) == -1) - { - char buffer[1024]; - _error = "can't set requested fragments settings"; - sprintf(buffer,"size%d:count%d\n",_fragmentSize,_fragmentCount); - close(); - return false; - } - - /* - * now see what we really got as cards aren't required to supply what - * we asked for - */ - audio_buf_info info; - if(ioctl(audio_fd,SNDCTL_DSP_GETOSPACE, &info) == -1) - { - _error = "can't retrieve fragment settings"; - close(); - return false; - } - - // update fragment settings with what we got - _fragmentSize = info.fragsize; - _fragmentCount = info.fragstotal; - - 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); - - /* - * Workaround for broken kernel drivers: usually filling up the audio - * buffer is _only_ required if _fullDuplex is true. However, there - * are kernel drivers around (especially everything related to ES1370/1371) - * which will not trigger select()ing the file descriptor unless we have - * written something first. - */ - char *zbuffer = (char *)calloc(sizeof(char), _fragmentSize); - if(_format == 8) - for(int zpos = 0; zpos < _fragmentSize; zpos++) - zbuffer[zpos] |= 0x80; - - for(int fill = 0; fill < _fragmentCount; fill++) - { - int len = ::write(audio_fd,zbuffer,_fragmentSize); - if(len != _fragmentSize) - { - arts_debug("AudioIOCSL: failed prefilling audio buffer (might cause synchronization problems in conjunction with full duplex)"); - fill = _fragmentCount+1; - } - } - free(zbuffer); - - /* - * Triggering - the original aRts code did this for full duplex: - * - * - stop audio i/o using SETTRIGGER(~(PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT)) - * - fill buffer (see zbuffer code two lines above) - * - start audio i/o using SETTRIGGER(PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT) - * - * this should guarantee synchronous start of input/output. Today, it - * seems there are too many broken drivers around for this. - */ - - if(device_caps & DSP_CAP_TRIGGER) - { - int enable_bits = 0; - - if(param(direction) & 1) enable_bits |= PCM_ENABLE_INPUT; - if(param(direction) & 2) enable_bits |= PCM_ENABLE_OUTPUT; - - if(ioctl(audio_fd,SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1) - { - _error = "can't start sound i/o"; - - close(); - return false; - } - } -#endif - updateFds(); - return true; -} - -void AudioIOCSL::close() -{ - if(inputStream) - { - csl_pcm_close(inputStream); - inputStream = 0; - } - if(outputStream) - { - csl_pcm_close(outputStream); - outputStream = 0; - } - updateFds(); -} - -void AudioIOCSL::setParam(AudioParam p, int& value) -{ - switch(p) - { - case fragmentSize: - param(p) = requestedFragmentSize = value; - break; - case fragmentCount: - param(p) = requestedFragmentCount = value; - break; - default: - param(p) = value; - break; - } -} - -int AudioIOCSL::getParam(AudioParam p) -{ - CslErrorType error; - CslPcmStatus status; - - switch(p) - { - case canRead: - error = csl_pcm_get_status (inputStream, &status); - if (error) /* FIXME */ - arts_fatal("unable to obtain csl stream status: %s", - csl_strerror (error)); - - updateFds(); - return status.n_bytes_available; - break; - - case canWrite: - error = csl_pcm_get_status(outputStream, &status); - if (error) /* FIXME */ - arts_fatal("unable to obtain csl stream status: %s", - csl_strerror (error)); - - updateFds(); - return status.n_bytes_available; - break; - - case autoDetect: - /* CSL is pretty experimental currently */ - return 1; - break; - - default: - return param(p); - break; - } -} - -int AudioIOCSL::read(void *buffer, int size) -{ - arts_assert(inputStream != 0); - - int result = csl_pcm_read(inputStream, size, buffer); - updateFds(); - - return result; -} - -void AudioIOCSL::handleRead(void *, CslPcmStream *) -{ - AudioSubSystem::the()->handleIO(AudioSubSystem::ioRead); -} - -int AudioIOCSL::write(void *buffer, int size) -{ - arts_assert(outputStream != 0); - - int result = csl_pcm_write(outputStream, size, buffer); - updateFds(); - - return result; -} - -void AudioIOCSL::handleWrite(void *, CslPcmStream *) -{ - AudioSubSystem::the()->handleIO(AudioSubSystem::ioWrite); -} - -/* mainloop integration: make CSL callbacks work inside the aRts mainloop */ - -int AudioIOCSL::csl2iomanager(int cslTypes) -{ - /* FIXME: doublecheck this list */ - int types = 0; - - if(cslTypes & CSL_POLLIN) - types |= IOType::read; - if(cslTypes & CSL_POLLOUT) - types |= IOType::write; - if(cslTypes & CSL_POLLERR) - types |= IOType::except; - - return types; -} - -void AudioIOCSL::updateFds() -{ - unsigned int n_fds = csl_poll_count_fds(cslDriver); - CslPollFD *newFds = g_newa(CslPollFD, n_fds); - - unsigned int have_fds = csl_poll_get_fds(cslDriver, n_fds, newFds); - arts_assert(have_fds == n_fds); - - cslFds.clear(); - - unsigned int i; - for(i = 0; i < have_fds; i++) - cslFds.push_back(newFds[i]); - - /* FIXME: if csl provided a flag for this, we could save some work here */ - bool fdsChanged; - if(cslFds.size() == cslOldFds.size()) - { - fdsChanged = false; - for(i = 0; i < have_fds; i++) - { - if(cslFds[i].events != cslOldFds[i].events) - fdsChanged = true; - if(cslFds[i].fd != cslOldFds[i].fd) - fdsChanged = true; - } - } - else - { - fdsChanged = true; - } - if(!fdsChanged) - return; - - vector::iterator ci; - - /* remove old watches */ - /* - * UGLY! due to broken API, we can only remove all watches here, and not - * do anything selectively - its not a problem for the code here, but it - * might be a problem elsewhere. Unfortunately, it can't be fixed without - * breaking BC. - */ - Dispatcher::the()->ioManager()->remove(this, IOType::all); - arts_debug("AudioIOCSL::updateFds(): removing watches"); - - /* add new watches */ - for(ci = cslFds.begin(); ci < cslFds.end(); ci++) - { - int types = csl2iomanager(ci->events); - if(types) - { - Dispatcher::the()->ioManager()->watchFD(ci->fd, types, this); - arts_debug("AudioIOCSL::updateFds(): adding watch on %d", ci->fd); - } - } - - cslOldFds = cslFds; -} - -void AudioIOCSL::notifyIO(int fd, int type) -{ - vector::iterator fi; - - for(fi = cslFds.begin(); fi != cslFds.end(); fi++) - { - if(fi->fd == fd) - { - int ftype = csl2iomanager(fi->events); - fi->revents = 0; - - if(type & ftype & IOType::read) - fi->revents |= CSL_POLLIN; - if(type & ftype & IOType::write) - fi->revents |= CSL_POLLOUT; - if(type & ftype & IOType::except) - fi->revents |= CSL_POLLERR; - - if(fi->revents) - csl_poll_handle_fds(cslDriver, 1, &(*fi)); - } - } - updateFds(); -} - -#endif diff --git a/flow/audioiocsl.cpp b/flow/audioiocsl.cpp new file mode 100644 index 0000000..7f3d5dd --- /dev/null +++ b/flow/audioiocsl.cpp @@ -0,0 +1,640 @@ + /* + + Copyright (C) 2000-2002 Stefan Westerfeld + stefan@space.twc.de + + 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 +#endif + +/** + * only compile 'csl' AudioIO class if libcsl is present + */ +#ifdef HAVE_LIBCSL +#include + +/* g_newa */ +#include + +#include + +#include "audioio.h" +#include "audiosubsys.h" +#include "debug.h" +#include "dispatcher.h" +#include "iomanager.h" + +namespace Arts { + +class AudioIOCSL : public AudioIO, + public IONotify +{ +protected: + CslPcmStream *inputStream, *outputStream; + CslDriver *cslDriver; + int requestedFragmentSize; + int requestedFragmentCount; + const char *cslDriverName; + + std::vector cslFds, cslOldFds; + int csl2iomanager(int cslTypes); + void updateFds(); + + void notifyIO(int fd, int types); + static void handleRead(void *user_data, CslPcmStream *stream); + static void handleWrite(void *user_data, CslPcmStream *stream); + +public: + AudioIOCSL(const char *driverName = 0); + + 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(AudioIOCSL,"csl","Common Sound Layer"); + +class AudioIOCSLFactory : public AudioIOFactory { +protected: + const char *driverName; + std::string _name; + std::string _fullName; + +public: + AudioIOCSLFactory(const char *driverName) + : driverName(driverName) + { + _name = "csl-"; + _name += driverName; + + _fullName = "Common Sound Layer ("; + _fullName += driverName; + _fullName += ")"; + } + virtual ~AudioIOCSLFactory() + { + } + AudioIO *createAudioIO() { return new AudioIOCSL(driverName); } + virtual const char *name() { return _name.c_str(); } + virtual const char *fullName() { return _fullName.c_str(); } +}; + +static class AudioIOCSLInit { +protected: + std::list factories; + +public: + AudioIOCSLInit() + { + unsigned int i,n; + const char **drivers = csl_list_drivers(&n); + + for(i = 0; i < n; i++) + factories.push_back(new AudioIOCSLFactory(drivers[i])); + } + ~AudioIOCSLInit() + { + std::list::iterator i; + for(i = factories.begin(); i != factories.end(); i++) + delete (*i); + + factories.clear(); + } +} aci; + +}; + +using namespace std; +using namespace Arts; + +AudioIOCSL::AudioIOCSL(const char *driverName) + : cslDriverName(driverName) +{ + /* + * default parameters + */ + param(samplingRate) = 44100; + paramStr(deviceName) = "todo"; + requestedFragmentSize = param(fragmentSize) = 1024; + requestedFragmentCount = param(fragmentCount) = 7; + param(channels) = 2; + param(direction) = 2; + +#ifdef WORDS_BIGENDIAN + param(format) = 17; +#else + param(format) = 16; +#endif +} + +bool AudioIOCSL::open() +{ + string& errorMsg = 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 fmt = 0; + char *env = 0; + + if(cslDriverName && strcmp(cslDriverName, "arts") == 0) + env = arts_strdup_printf("ARTS_SERVER=%s",_deviceName.c_str()); + + if(env) + { + putenv(env); + arts_debug("AudioIOCsl: set %s\n",env); + } + + CslErrorType error; + error = csl_driver_init(cslDriverName, &cslDriver); /* choose backend */ + + if(env) + { + putenv("ARTS_SERVER"); + free(env); + } + + if (error) + { + errorMsg = "unable to initialize CSL driver: "; + errorMsg += csl_strerror(error); + return false; + } + + if(_format == 8) + fmt = CSL_PCM_FORMAT_U8; + else if(_format == 16) + fmt = CSL_PCM_FORMAT_S16_LE; + else if(_format == 17) + fmt = CSL_PCM_FORMAT_S16_BE; + + inputStream = outputStream = 0; + if(param(direction) & directionRead) + { + /* open PCM output stream */ + error = csl_pcm_open_output(cslDriver, + "artsd output", + _samplingRate, + _channels, + fmt, &inputStream); + if (error) + { + errorMsg = "failed to open CSL input stream: "; + errorMsg += csl_strerror(error); + return false; + } + csl_pcm_set_callback(inputStream, handleRead, 0, 0); + } + if(param(direction) & directionWrite) + { + /* open PCM output stream */ + error = csl_pcm_open_output(cslDriver, + "artsd output", + _samplingRate, + _channels, + fmt, &outputStream); + if (error) + { + close(); + + errorMsg = "failed to open CSL output stream: "; + errorMsg += csl_strerror(error); + return false; + } + csl_pcm_set_callback(outputStream, handleWrite, 0, 0); + } +#if 0 + if (_format && (ossBits(gotFormat) != ossBits(requestedFormat))) + { + char details[80]; + sprintf(details," (_format = %d, asked driver to give %d, got %d)", + _format, requestedFormat, gotFormat); + + _error = "Can't set playback format"; + _error += details; + + close(); + return false; + } + + if(gotFormat == AFMT_U8) + _format = 8; + else if(gotFormat == AFMT_S16_LE) + _format = 16; + else if(gotFormat == AFMT_S16_BE) + _format = 17; + else + { + char details[80]; + sprintf(details," (_format = %d, asked driver to give %d, got %d)", + _format, requestedFormat, gotFormat); + + _error = "unknown format given by driver"; + _error += details; + + close(); + return false; + } + + + int stereo=-1; /* 0=mono, 1=stereo */ + + if(_channels == 1) + { + stereo = 0; + } + if(_channels == 2) + { + stereo = 1; + } + + if(stereo == -1) + { + _error = "internal error; set channels to 1 (mono) or 2 (stereo)"; + + close(); + return false; + } + + int requeststereo = stereo; + + if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1) + { + _error = "SNDCTL_DSP_STEREO failed - "; + _error += strerror(errno); + + close(); + return false; + } + + if (requeststereo != stereo) + { + _error = "audio device doesn't support number of requested channels"; + + close(); + return false; + } + + int speed = _samplingRate; + + if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) + { + _error = "SNDCTL_DSP_SPEED failed - "; + _error += strerror(errno); + + close(); + return false; + } + + /* + * 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(speed-_samplingRate) > tolerance) + { + _error = "can't set requested samplingrate"; + + char details[80]; + sprintf(details," (requested rate %d, got rate %d)", + _samplingRate, speed); + _error += details; + + close(); + return false; + } + _samplingRate = speed; + + /* + * set the fragment settings to what the user requested + */ + + _fragmentSize = requestedFragmentSize; + _fragmentCount = requestedFragmentCount; + + /* + * lower 16 bits are the fragment size (as 2^S) + * higher 16 bits are the number of fragments + */ + int frag_arg = 0; + + int size = _fragmentSize; + while(size > 1) { size /= 2; frag_arg++; } + frag_arg += (_fragmentCount << 16); + if(ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_arg) == -1) + { + char buffer[1024]; + _error = "can't set requested fragments settings"; + sprintf(buffer,"size%d:count%d\n",_fragmentSize,_fragmentCount); + close(); + return false; + } + + /* + * now see what we really got as cards aren't required to supply what + * we asked for + */ + audio_buf_info info; + if(ioctl(audio_fd,SNDCTL_DSP_GETOSPACE, &info) == -1) + { + _error = "can't retrieve fragment settings"; + close(); + return false; + } + + // update fragment settings with what we got + _fragmentSize = info.fragsize; + _fragmentCount = info.fragstotal; + + 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); + + /* + * Workaround for broken kernel drivers: usually filling up the audio + * buffer is _only_ required if _fullDuplex is true. However, there + * are kernel drivers around (especially everything related to ES1370/1371) + * which will not trigger select()ing the file descriptor unless we have + * written something first. + */ + char *zbuffer = (char *)calloc(sizeof(char), _fragmentSize); + if(_format == 8) + for(int zpos = 0; zpos < _fragmentSize; zpos++) + zbuffer[zpos] |= 0x80; + + for(int fill = 0; fill < _fragmentCount; fill++) + { + int len = ::write(audio_fd,zbuffer,_fragmentSize); + if(len != _fragmentSize) + { + arts_debug("AudioIOCSL: failed prefilling audio buffer (might cause synchronization problems in conjunction with full duplex)"); + fill = _fragmentCount+1; + } + } + free(zbuffer); + + /* + * Triggering - the original aRts code did this for full duplex: + * + * - stop audio i/o using SETTRIGGER(~(PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT)) + * - fill buffer (see zbuffer code two lines above) + * - start audio i/o using SETTRIGGER(PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT) + * + * this should guarantee synchronous start of input/output. Today, it + * seems there are too many broken drivers around for this. + */ + + if(device_caps & DSP_CAP_TRIGGER) + { + int enable_bits = 0; + + if(param(direction) & 1) enable_bits |= PCM_ENABLE_INPUT; + if(param(direction) & 2) enable_bits |= PCM_ENABLE_OUTPUT; + + if(ioctl(audio_fd,SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1) + { + _error = "can't start sound i/o"; + + close(); + return false; + } + } +#endif + updateFds(); + return true; +} + +void AudioIOCSL::close() +{ + if(inputStream) + { + csl_pcm_close(inputStream); + inputStream = 0; + } + if(outputStream) + { + csl_pcm_close(outputStream); + outputStream = 0; + } + updateFds(); +} + +void AudioIOCSL::setParam(AudioParam p, int& value) +{ + switch(p) + { + case fragmentSize: + param(p) = requestedFragmentSize = value; + break; + case fragmentCount: + param(p) = requestedFragmentCount = value; + break; + default: + param(p) = value; + break; + } +} + +int AudioIOCSL::getParam(AudioParam p) +{ + CslErrorType error; + CslPcmStatus status; + + switch(p) + { + case canRead: + error = csl_pcm_get_status (inputStream, &status); + if (error) /* FIXME */ + arts_fatal("unable to obtain csl stream status: %s", + csl_strerror (error)); + + updateFds(); + return status.n_bytes_available; + break; + + case canWrite: + error = csl_pcm_get_status(outputStream, &status); + if (error) /* FIXME */ + arts_fatal("unable to obtain csl stream status: %s", + csl_strerror (error)); + + updateFds(); + return status.n_bytes_available; + break; + + case autoDetect: + /* CSL is pretty experimental currently */ + return 1; + break; + + default: + return param(p); + break; + } +} + +int AudioIOCSL::read(void *buffer, int size) +{ + arts_assert(inputStream != 0); + + int result = csl_pcm_read(inputStream, size, buffer); + updateFds(); + + return result; +} + +void AudioIOCSL::handleRead(void *, CslPcmStream *) +{ + AudioSubSystem::the()->handleIO(AudioSubSystem::ioRead); +} + +int AudioIOCSL::write(void *buffer, int size) +{ + arts_assert(outputStream != 0); + + int result = csl_pcm_write(outputStream, size, buffer); + updateFds(); + + return result; +} + +void AudioIOCSL::handleWrite(void *, CslPcmStream *) +{ + AudioSubSystem::the()->handleIO(AudioSubSystem::ioWrite); +} + +/* mainloop integration: make CSL callbacks work inside the aRts mainloop */ + +int AudioIOCSL::csl2iomanager(int cslTypes) +{ + /* FIXME: doublecheck this list */ + int types = 0; + + if(cslTypes & CSL_POLLIN) + types |= IOType::read; + if(cslTypes & CSL_POLLOUT) + types |= IOType::write; + if(cslTypes & CSL_POLLERR) + types |= IOType::except; + + return types; +} + +void AudioIOCSL::updateFds() +{ + unsigned int n_fds = csl_poll_count_fds(cslDriver); + CslPollFD *newFds = g_newa(CslPollFD, n_fds); + + unsigned int have_fds = csl_poll_get_fds(cslDriver, n_fds, newFds); + arts_assert(have_fds == n_fds); + + cslFds.clear(); + + unsigned int i; + for(i = 0; i < have_fds; i++) + cslFds.push_back(newFds[i]); + + /* FIXME: if csl provided a flag for this, we could save some work here */ + bool fdsChanged; + if(cslFds.size() == cslOldFds.size()) + { + fdsChanged = false; + for(i = 0; i < have_fds; i++) + { + if(cslFds[i].events != cslOldFds[i].events) + fdsChanged = true; + if(cslFds[i].fd != cslOldFds[i].fd) + fdsChanged = true; + } + } + else + { + fdsChanged = true; + } + if(!fdsChanged) + return; + + vector::iterator ci; + + /* remove old watches */ + /* + * UGLY! due to broken API, we can only remove all watches here, and not + * do anything selectively - its not a problem for the code here, but it + * might be a problem elsewhere. Unfortunately, it can't be fixed without + * breaking BC. + */ + Dispatcher::the()->ioManager()->remove(this, IOType::all); + arts_debug("AudioIOCSL::updateFds(): removing watches"); + + /* add new watches */ + for(ci = cslFds.begin(); ci < cslFds.end(); ci++) + { + int types = csl2iomanager(ci->events); + if(types) + { + Dispatcher::the()->ioManager()->watchFD(ci->fd, types, this); + arts_debug("AudioIOCSL::updateFds(): adding watch on %d", ci->fd); + } + } + + cslOldFds = cslFds; +} + +void AudioIOCSL::notifyIO(int fd, int type) +{ + vector::iterator fi; + + for(fi = cslFds.begin(); fi != cslFds.end(); fi++) + { + if(fi->fd == fd) + { + int ftype = csl2iomanager(fi->events); + fi->revents = 0; + + if(type & ftype & IOType::read) + fi->revents |= CSL_POLLIN; + if(type & ftype & IOType::write) + fi->revents |= CSL_POLLOUT; + if(type & ftype & IOType::except) + fi->revents |= CSL_POLLERR; + + if(fi->revents) + csl_poll_handle_fds(cslDriver, 1, &(*fi)); + } + } + updateFds(); +} + +#endif diff --git a/flow/audioioesd.cc b/flow/audioioesd.cc deleted file mode 100644 index 90b074d..0000000 --- a/flow/audioioesd.cc +++ /dev/null @@ -1,227 +0,0 @@ - /* - - Copyright (C) 2001 Jochen Hoenicke - jochen@gnu.org - - Copyright (C) 2003 Ian Chiew - ian@snork.net - - 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 -#endif - -/** - * only compile esound AudioIO class if libesd was detected during - * configure - */ -#ifdef HAVE_LIBESD - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "audioio.h" -#include "audiosubsys.h" -#include "iomanager.h" -#include "dispatcher.h" - -#include - -#ifdef __BIG_ENDIAN__ -#define _format_bits 17 -#else -#define _format_bits 16 -#endif - -namespace Arts { - -class AudioIOESD : public AudioIO, public IONotify { -protected: - int server_fd, read_fd, write_fd; - -public: - AudioIOESD(); - - void setParam(AudioParam param, int& value); - int getParam(AudioParam param); - void notifyIO(int fd, int type); - - bool open(); - void close(); - void run(); - int read(void *buffer, int size); - int write(void *buffer, int size); -}; - -REGISTER_AUDIO_IO(AudioIOESD,"esd","Enlightened Sound Daemon"); -} - -using namespace std; -using namespace Arts; - -AudioIOESD::AudioIOESD() -{ - /* - * default parameters - */ - param(samplingRate) = 44100; - paramStr(deviceName) = "null"; - param(fragmentSize) = 1024; - param(fragmentCount) = 7; - param(format) = _format_bits; - param(channels) = 2; - param(direction) = 2; - - server_fd = -1; - read_fd = -1; - write_fd = -1; -} - -bool AudioIOESD::open() -{ - int& _channels = param(channels); - int& _direction = param(direction); - int& _samplingRate = param(samplingRate); - int& _format = param(format); - string& _error = paramStr(lastError); - - if ((server_fd = esd_open_sound(NULL)) == -1) - { - _error = "Couldn't connect to server"; - return false; - } - - esd_server_info_t *server_info = esd_get_server_info(server_fd); - - if (server_info == NULL) - { - _error = "Unable to query EsounD server"; - return false; - } - - esd_format_t server_format = server_info->format; - - _samplingRate = server_info->rate; - _channels = (server_format & ESD_STEREO) ? 2 : 1; - _format = (server_format & ESD_BITS16) ? _format_bits : 8; - - esd_free_server_info(server_info); - - if (_direction & directionRead) - { - read_fd = esd_record_stream(server_format, _samplingRate, - NULL, "aRts"); - if (read_fd == -1) - { - _error = "Couldn't create read uflow"; - return false; - } - } - - if (_direction & directionWrite) - { - write_fd = esd_play_stream(server_format, _samplingRate, - NULL, "aRts"); - if (write_fd == -1) - { - _error = "Couldn't create write flow"; - return false; - } - } - - return true; -} - -void AudioIOESD::notifyIO(int, int) -{ -} - -void AudioIOESD::close() -{ - if (write_fd != -1) - esd_close(write_fd); - - if (read_fd != -1) - esd_close(read_fd); - - if (server_fd != -1) - esd_close(server_fd); -} - -void AudioIOESD::setParam(AudioParam p, int& value) -{ - switch(p) - { - case channels: - case format: - case direction: - case samplingRate: - param(p) = value; - close(); - open(); - return; - - default: - param(p) = value; - return; - } -} - -int AudioIOESD::getParam(AudioParam p) -{ - switch(p) - { - case selectReadFD: - return read_fd; - - case selectWriteFD: - return write_fd; - - case canRead: - return param(fragmentSize); - - case canWrite: - return param(fragmentSize); - - default: - return param(p); - } -} - -int AudioIOESD::read(void *buffer, int size) -{ - return ::read(read_fd, buffer, size); -} - -int AudioIOESD::write(void *buffer, int size) -{ - return ::write(write_fd, buffer, size); -} - -#endif diff --git a/flow/audioioesd.cpp b/flow/audioioesd.cpp new file mode 100644 index 0000000..90b074d --- /dev/null +++ b/flow/audioioesd.cpp @@ -0,0 +1,227 @@ + /* + + Copyright (C) 2001 Jochen Hoenicke + jochen@gnu.org + + Copyright (C) 2003 Ian Chiew + ian@snork.net + + 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 +#endif + +/** + * only compile esound AudioIO class if libesd was detected during + * configure + */ +#ifdef HAVE_LIBESD + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "audioio.h" +#include "audiosubsys.h" +#include "iomanager.h" +#include "dispatcher.h" + +#include + +#ifdef __BIG_ENDIAN__ +#define _format_bits 17 +#else +#define _format_bits 16 +#endif + +namespace Arts { + +class AudioIOESD : public AudioIO, public IONotify { +protected: + int server_fd, read_fd, write_fd; + +public: + AudioIOESD(); + + void setParam(AudioParam param, int& value); + int getParam(AudioParam param); + void notifyIO(int fd, int type); + + bool open(); + void close(); + void run(); + int read(void *buffer, int size); + int write(void *buffer, int size); +}; + +REGISTER_AUDIO_IO(AudioIOESD,"esd","Enlightened Sound Daemon"); +} + +using namespace std; +using namespace Arts; + +AudioIOESD::AudioIOESD() +{ + /* + * default parameters + */ + param(samplingRate) = 44100; + paramStr(deviceName) = "null"; + param(fragmentSize) = 1024; + param(fragmentCount) = 7; + param(format) = _format_bits; + param(channels) = 2; + param(direction) = 2; + + server_fd = -1; + read_fd = -1; + write_fd = -1; +} + +bool AudioIOESD::open() +{ + int& _channels = param(channels); + int& _direction = param(direction); + int& _samplingRate = param(samplingRate); + int& _format = param(format); + string& _error = paramStr(lastError); + + if ((server_fd = esd_open_sound(NULL)) == -1) + { + _error = "Couldn't connect to server"; + return false; + } + + esd_server_info_t *server_info = esd_get_server_info(server_fd); + + if (server_info == NULL) + { + _error = "Unable to query EsounD server"; + return false; + } + + esd_format_t server_format = server_info->format; + + _samplingRate = server_info->rate; + _channels = (server_format & ESD_STEREO) ? 2 : 1; + _format = (server_format & ESD_BITS16) ? _format_bits : 8; + + esd_free_server_info(server_info); + + if (_direction & directionRead) + { + read_fd = esd_record_stream(server_format, _samplingRate, + NULL, "aRts"); + if (read_fd == -1) + { + _error = "Couldn't create read uflow"; + return false; + } + } + + if (_direction & directionWrite) + { + write_fd = esd_play_stream(server_format, _samplingRate, + NULL, "aRts"); + if (write_fd == -1) + { + _error = "Couldn't create write flow"; + return false; + } + } + + return true; +} + +void AudioIOESD::notifyIO(int, int) +{ +} + +void AudioIOESD::close() +{ + if (write_fd != -1) + esd_close(write_fd); + + if (read_fd != -1) + esd_close(read_fd); + + if (server_fd != -1) + esd_close(server_fd); +} + +void AudioIOESD::setParam(AudioParam p, int& value) +{ + switch(p) + { + case channels: + case format: + case direction: + case samplingRate: + param(p) = value; + close(); + open(); + return; + + default: + param(p) = value; + return; + } +} + +int AudioIOESD::getParam(AudioParam p) +{ + switch(p) + { + case selectReadFD: + return read_fd; + + case selectWriteFD: + return write_fd; + + case canRead: + return param(fragmentSize); + + case canWrite: + return param(fragmentSize); + + default: + return param(p); + } +} + +int AudioIOESD::read(void *buffer, int size) +{ + return ::read(read_fd, buffer, size); +} + +int AudioIOESD::write(void *buffer, int size) +{ + return ::write(write_fd, buffer, size); +} + +#endif diff --git a/flow/audioiojack.cc b/flow/audioiojack.cc deleted file mode 100644 index ca8110a..0000000 --- a/flow/audioiojack.cc +++ /dev/null @@ -1,345 +0,0 @@ -/* - Copyright (C) 2004 Matthias Kretz - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - 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 -#endif - -#ifdef HAVE_LIBJACK - -#include -#include - -#include "debug.h" -#include "audioio.h" -#include "audiosubsys.h" -#include "iomanager.h" -#include "dispatcher.h" - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#undef DEBUG_WAVEFORM -#ifdef DEBUG_WAVEFORM -#include -#endif - -#include -#include - -namespace Arts { - -class AudioIOJack : public AudioIO, public TimeNotify { -private: -#ifdef DEBUG_WAVEFORM - std::ofstream plotfile; -#endif - char * processBuffer; - size_t buffersize; - -protected: - jack_client_t *jack; - jack_port_t *outleft, *outright; - jack_port_t *inleft, *inright; - jack_ringbuffer_t *olb, *orb, *ilb, *irb; - -public: - AudioIOJack(); - - void notifyTime(); - - void setParam( AudioParam p, int & val ); - int getParam(AudioParam param); - - static int jackCallback( jack_nframes_t, void * ); - - bool open(); - void close(); - int read(void *buffer, int size); - int write(void *buffer, int size); -}; - -REGISTER_AUDIO_IO(AudioIOJack,"jack","Jack Audio Connection Kit"); -} - -using namespace std; -using namespace Arts; - -AudioIOJack::AudioIOJack() - : -#ifdef DEBUG_WAVEFORM - plotfile( "/dev/shm/audioiojack.plot" ), -#endif - jack( 0 ) - , outleft( 0 ) - , outright( 0 ) - , inleft( 0 ) - , inright( 0 ) -{ - /* - * default parameters - */ - param( samplingRate ) = 44100; - paramStr( deviceName ) = "jack"; - param( fragmentSize ) = 512; - param( fragmentCount ) = 2; - param( channels ) = 2; - param( direction ) = 2; - param( format ) = 32; -} - -bool AudioIOJack::open() -{ - string& _error = paramStr( lastError ); - jack = jack_client_new( "artsd" ); - if( jack == 0 ) - { - _error = "Couldn't connect to jackd"; - return false; - } - - int& _sampleRate = param(samplingRate); - _sampleRate = jack_get_sample_rate( jack ); - int& _fragmentSize = param(fragmentSize); - int& _fragmentCount = param(fragmentCount); - - /* - * don't allow unreasonable large fragmentSize/Count combinations, - * because "real" hardware also doesn't - */ - - if(_fragmentSize > 1024*8) _fragmentSize = 1024*8; - - while(_fragmentSize * _fragmentCount > 1024*128) - _fragmentCount--; - - jack_set_process_callback( jack, Arts::AudioIOJack::jackCallback, this ); - - if( param( direction ) & directionWrite ) - { - outleft = jack_port_register( jack, "out_1", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - outright = jack_port_register( jack, "out_2", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - olb = jack_ringbuffer_create( - sizeof( jack_default_audio_sample_t ) * - _fragmentSize * _fragmentCount ); - orb = jack_ringbuffer_create( - sizeof( jack_default_audio_sample_t ) * - _fragmentSize * _fragmentCount ); - } - if( param( direction ) & directionRead ) - { - inleft = jack_port_register( jack, "in_1", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); - inright = jack_port_register( jack, "in_2", - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); - ilb = jack_ringbuffer_create( - sizeof( jack_default_audio_sample_t ) * - 1024 * 64 ); - irb = jack_ringbuffer_create( - sizeof( jack_default_audio_sample_t ) * - 1024 * 64 ); - } - - if( jack_activate( jack ) ) - { - _error = "Activating as jack client failed."; - return false; - } - - const char **ports; - if( param( direction ) & directionRead ) - { - ports = jack_get_ports( jack, 0, 0, JackPortIsPhysical - | JackPortIsOutput ); - if( ports == 0 ) - { - arts_warning( "Cannot find any capture ports to" - " connect to. You need to manually connect" - " the capture ports in jack" ); - } - else - { - if( ports[ 0 ] != 0 ) - jack_connect( jack, ports[ 0 ], - jack_port_name( inleft ) ); - if( ports[ 1 ] != 0 ) - jack_connect( jack, ports[ 1 ], - jack_port_name( inright ) ); - free( ports ); - } - } - if( param( direction ) & directionWrite ) - { - ports = jack_get_ports( jack, 0, 0, JackPortIsPhysical - | JackPortIsInput ); - if( ports == 0 ) - { - arts_warning( "Cannot find any playback ports to" - " connect to. You need to manually connect" - " the playback ports in jack" ); - } - else - { - if( ports[ 0 ] != 0 ) - jack_connect( jack, jack_port_name( outleft ), - ports[ 0 ] ); - if( ports[ 1 ] != 0 ) - jack_connect( jack, jack_port_name( outright ), - ports[ 1 ] ); - free( ports ); - } - } - - // Install the timer - Dispatcher::the()->ioManager()->addTimer(10, this); - - return true; -} - -void AudioIOJack::close() -{ - jack_client_close( jack ); - Dispatcher::the()->ioManager()->removeTimer(this); -} - -int AudioIOJack::jackCallback( jack_nframes_t nframes, void * args ) -{ - AudioIOJack * that = static_cast( args ); - - that->buffersize = nframes * sizeof( jack_default_audio_sample_t ); - if( that->outleft ) - { - if( jack_ringbuffer_read_space( that->olb ) < that->buffersize ) - { - that->processBuffer = static_cast( - jack_port_get_buffer( that->outleft, nframes ) ); - memset( that->processBuffer, 0, that->buffersize ); - that->processBuffer = static_cast( - jack_port_get_buffer( that->outright, nframes ) ); - memset( that->processBuffer, 0, that->buffersize ); - } - else - { - that->processBuffer = static_cast( - jack_port_get_buffer( that->outleft, nframes ) ); - jack_ringbuffer_read( that->olb, that->processBuffer, that->buffersize ); - that->processBuffer = static_cast( - jack_port_get_buffer( that->outright, nframes ) ); - jack_ringbuffer_read( that->orb, that->processBuffer, that->buffersize ); - } - } - if( that->inleft ) - { - that->processBuffer = static_cast( - jack_port_get_buffer( that->inleft, nframes ) ); - jack_ringbuffer_write( that->ilb, that->processBuffer, that->buffersize ); - that->processBuffer = static_cast( - jack_port_get_buffer( that->inright, nframes ) ); - jack_ringbuffer_write( that->irb, that->processBuffer, that->buffersize ); - } - return 0; -} - -void AudioIOJack::setParam( AudioParam p, int& val ) -{ - // don't change the format - jack only supports 32 bit float - if( p == format ) - return; - AudioIO::setParam( p, val ); -} - -int AudioIOJack::getParam(AudioParam p) -{ - switch(p) - { - case canRead: - return MIN( jack_ringbuffer_read_space( ilb ), jack_ringbuffer_read_space( irb ) ) * param( channels ); - case canWrite: - return MIN( jack_ringbuffer_write_space( olb ), jack_ringbuffer_write_space( orb ) ) * param( channels ); - default: - return AudioIO::getParam( p ); - } -} - -int AudioIOJack::read(void *buffer, int size) -{ - float * floatbuffer = static_cast( buffer ); - if( param( channels ) == 2 ) - { - float * end = ( float * )( static_cast( buffer ) + size ); - while( floatbuffer < end ) - { - jack_ringbuffer_read( ilb, ( char* )( floatbuffer++ ), sizeof( float ) ); -#ifdef DEBUG_WAVEFORM - plotfile << *( floatbuffer - 1 ) << "\n"; -#endif - jack_ringbuffer_read( irb, ( char* )( floatbuffer++ ), sizeof( float ) ); - } - } - else if( param( channels ) == 1 ) - { - jack_ringbuffer_read( ilb, ( char* )( floatbuffer ), size ); - } - return size; -} - -int AudioIOJack::write(void *buffer, int size) -{ - float * floatbuffer = static_cast( buffer ); - if( param( channels ) == 2 ) - { - float * end = ( float * )( static_cast( buffer ) + size ); - while( floatbuffer < end ) - { - jack_ringbuffer_write( olb, ( char* )( floatbuffer++ ), sizeof( float ) ); - jack_ringbuffer_write( orb, ( char* )( floatbuffer++ ), sizeof( float ) ); - } - } - else if( param( channels ) == 1 ) - { - jack_ringbuffer_write( olb, ( char* )( floatbuffer ), size ); - } - return size; -} - -void AudioIOJack::notifyTime() -{ - int& _direction = param(direction); - int& _fragmentSize = param(fragmentSize); - - for( ;; ) - { - int todo = 0; - if( ( _direction & directionRead ) && ( getParam( canRead ) >= _fragmentSize ) ) - todo |= AudioSubSystem::ioRead; - - if( ( _direction & directionWrite ) && ( getParam( canWrite ) >= _fragmentSize ) ) - todo |= AudioSubSystem::ioWrite; - - if( ! todo ) - return; - - AudioSubSystem::the()->handleIO( todo ); - } -} - -#endif diff --git a/flow/audioiojack.cpp b/flow/audioiojack.cpp new file mode 100644 index 0000000..ca8110a --- /dev/null +++ b/flow/audioiojack.cpp @@ -0,0 +1,345 @@ +/* + Copyright (C) 2004 Matthias Kretz + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 +#endif + +#ifdef HAVE_LIBJACK + +#include +#include + +#include "debug.h" +#include "audioio.h" +#include "audiosubsys.h" +#include "iomanager.h" +#include "dispatcher.h" + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#undef DEBUG_WAVEFORM +#ifdef DEBUG_WAVEFORM +#include +#endif + +#include +#include + +namespace Arts { + +class AudioIOJack : public AudioIO, public TimeNotify { +private: +#ifdef DEBUG_WAVEFORM + std::ofstream plotfile; +#endif + char * processBuffer; + size_t buffersize; + +protected: + jack_client_t *jack; + jack_port_t *outleft, *outright; + jack_port_t *inleft, *inright; + jack_ringbuffer_t *olb, *orb, *ilb, *irb; + +public: + AudioIOJack(); + + void notifyTime(); + + void setParam( AudioParam p, int & val ); + int getParam(AudioParam param); + + static int jackCallback( jack_nframes_t, void * ); + + bool open(); + void close(); + int read(void *buffer, int size); + int write(void *buffer, int size); +}; + +REGISTER_AUDIO_IO(AudioIOJack,"jack","Jack Audio Connection Kit"); +} + +using namespace std; +using namespace Arts; + +AudioIOJack::AudioIOJack() + : +#ifdef DEBUG_WAVEFORM + plotfile( "/dev/shm/audioiojack.plot" ), +#endif + jack( 0 ) + , outleft( 0 ) + , outright( 0 ) + , inleft( 0 ) + , inright( 0 ) +{ + /* + * default parameters + */ + param( samplingRate ) = 44100; + paramStr( deviceName ) = "jack"; + param( fragmentSize ) = 512; + param( fragmentCount ) = 2; + param( channels ) = 2; + param( direction ) = 2; + param( format ) = 32; +} + +bool AudioIOJack::open() +{ + string& _error = paramStr( lastError ); + jack = jack_client_new( "artsd" ); + if( jack == 0 ) + { + _error = "Couldn't connect to jackd"; + return false; + } + + int& _sampleRate = param(samplingRate); + _sampleRate = jack_get_sample_rate( jack ); + int& _fragmentSize = param(fragmentSize); + int& _fragmentCount = param(fragmentCount); + + /* + * don't allow unreasonable large fragmentSize/Count combinations, + * because "real" hardware also doesn't + */ + + if(_fragmentSize > 1024*8) _fragmentSize = 1024*8; + + while(_fragmentSize * _fragmentCount > 1024*128) + _fragmentCount--; + + jack_set_process_callback( jack, Arts::AudioIOJack::jackCallback, this ); + + if( param( direction ) & directionWrite ) + { + outleft = jack_port_register( jack, "out_1", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + outright = jack_port_register( jack, "out_2", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + olb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + _fragmentSize * _fragmentCount ); + orb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + _fragmentSize * _fragmentCount ); + } + if( param( direction ) & directionRead ) + { + inleft = jack_port_register( jack, "in_1", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + inright = jack_port_register( jack, "in_2", + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + ilb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + 1024 * 64 ); + irb = jack_ringbuffer_create( + sizeof( jack_default_audio_sample_t ) * + 1024 * 64 ); + } + + if( jack_activate( jack ) ) + { + _error = "Activating as jack client failed."; + return false; + } + + const char **ports; + if( param( direction ) & directionRead ) + { + ports = jack_get_ports( jack, 0, 0, JackPortIsPhysical + | JackPortIsOutput ); + if( ports == 0 ) + { + arts_warning( "Cannot find any capture ports to" + " connect to. You need to manually connect" + " the capture ports in jack" ); + } + else + { + if( ports[ 0 ] != 0 ) + jack_connect( jack, ports[ 0 ], + jack_port_name( inleft ) ); + if( ports[ 1 ] != 0 ) + jack_connect( jack, ports[ 1 ], + jack_port_name( inright ) ); + free( ports ); + } + } + if( param( direction ) & directionWrite ) + { + ports = jack_get_ports( jack, 0, 0, JackPortIsPhysical + | JackPortIsInput ); + if( ports == 0 ) + { + arts_warning( "Cannot find any playback ports to" + " connect to. You need to manually connect" + " the playback ports in jack" ); + } + else + { + if( ports[ 0 ] != 0 ) + jack_connect( jack, jack_port_name( outleft ), + ports[ 0 ] ); + if( ports[ 1 ] != 0 ) + jack_connect( jack, jack_port_name( outright ), + ports[ 1 ] ); + free( ports ); + } + } + + // Install the timer + Dispatcher::the()->ioManager()->addTimer(10, this); + + return true; +} + +void AudioIOJack::close() +{ + jack_client_close( jack ); + Dispatcher::the()->ioManager()->removeTimer(this); +} + +int AudioIOJack::jackCallback( jack_nframes_t nframes, void * args ) +{ + AudioIOJack * that = static_cast( args ); + + that->buffersize = nframes * sizeof( jack_default_audio_sample_t ); + if( that->outleft ) + { + if( jack_ringbuffer_read_space( that->olb ) < that->buffersize ) + { + that->processBuffer = static_cast( + jack_port_get_buffer( that->outleft, nframes ) ); + memset( that->processBuffer, 0, that->buffersize ); + that->processBuffer = static_cast( + jack_port_get_buffer( that->outright, nframes ) ); + memset( that->processBuffer, 0, that->buffersize ); + } + else + { + that->processBuffer = static_cast( + jack_port_get_buffer( that->outleft, nframes ) ); + jack_ringbuffer_read( that->olb, that->processBuffer, that->buffersize ); + that->processBuffer = static_cast( + jack_port_get_buffer( that->outright, nframes ) ); + jack_ringbuffer_read( that->orb, that->processBuffer, that->buffersize ); + } + } + if( that->inleft ) + { + that->processBuffer = static_cast( + jack_port_get_buffer( that->inleft, nframes ) ); + jack_ringbuffer_write( that->ilb, that->processBuffer, that->buffersize ); + that->processBuffer = static_cast( + jack_port_get_buffer( that->inright, nframes ) ); + jack_ringbuffer_write( that->irb, that->processBuffer, that->buffersize ); + } + return 0; +} + +void AudioIOJack::setParam( AudioParam p, int& val ) +{ + // don't change the format - jack only supports 32 bit float + if( p == format ) + return; + AudioIO::setParam( p, val ); +} + +int AudioIOJack::getParam(AudioParam p) +{ + switch(p) + { + case canRead: + return MIN( jack_ringbuffer_read_space( ilb ), jack_ringbuffer_read_space( irb ) ) * param( channels ); + case canWrite: + return MIN( jack_ringbuffer_write_space( olb ), jack_ringbuffer_write_space( orb ) ) * param( channels ); + default: + return AudioIO::getParam( p ); + } +} + +int AudioIOJack::read(void *buffer, int size) +{ + float * floatbuffer = static_cast( buffer ); + if( param( channels ) == 2 ) + { + float * end = ( float * )( static_cast( buffer ) + size ); + while( floatbuffer < end ) + { + jack_ringbuffer_read( ilb, ( char* )( floatbuffer++ ), sizeof( float ) ); +#ifdef DEBUG_WAVEFORM + plotfile << *( floatbuffer - 1 ) << "\n"; +#endif + jack_ringbuffer_read( irb, ( char* )( floatbuffer++ ), sizeof( float ) ); + } + } + else if( param( channels ) == 1 ) + { + jack_ringbuffer_read( ilb, ( char* )( floatbuffer ), size ); + } + return size; +} + +int AudioIOJack::write(void *buffer, int size) +{ + float * floatbuffer = static_cast( buffer ); + if( param( channels ) == 2 ) + { + float * end = ( float * )( static_cast( buffer ) + size ); + while( floatbuffer < end ) + { + jack_ringbuffer_write( olb, ( char* )( floatbuffer++ ), sizeof( float ) ); + jack_ringbuffer_write( orb, ( char* )( floatbuffer++ ), sizeof( float ) ); + } + } + else if( param( channels ) == 1 ) + { + jack_ringbuffer_write( olb, ( char* )( floatbuffer ), size ); + } + return size; +} + +void AudioIOJack::notifyTime() +{ + int& _direction = param(direction); + int& _fragmentSize = param(fragmentSize); + + for( ;; ) + { + int todo = 0; + if( ( _direction & directionRead ) && ( getParam( canRead ) >= _fragmentSize ) ) + todo |= AudioSubSystem::ioRead; + + if( ( _direction & directionWrite ) && ( getParam( canWrite ) >= _fragmentSize ) ) + todo |= AudioSubSystem::ioWrite; + + if( ! todo ) + return; + + AudioSubSystem::the()->handleIO( todo ); + } +} + +#endif diff --git a/flow/audioiolibaudioio.cc b/flow/audioiolibaudioio.cc deleted file mode 100644 index c3a887b..0000000 --- a/flow/audioiolibaudioio.cc +++ /dev/null @@ -1,236 +0,0 @@ - /* - - Copyright (C) 2001 Robert Lunnon - bob@yarrabee.net.au - Copyright (C) 2001 Stefan Westerfeld - stefan@space.twc.de - - 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 -#endif - -/** - * only compile 'LibAudioIO' AudioIO class if libaudio was detected during - * configure - */ -#ifdef HAVE_LIBAUDIOIO - -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_SELECT_H -#include // Needed on some systems. -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "audioio.h" - -namespace Arts { - -class AudioIOLibAudioIO : public AudioIO { -protected: - int audio_fd; - int requestedFragmentSize; - int requestedFragmentCount; - SampleSpec_t spec; - -public: - AudioIOLibAudioIO(); - - 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(AudioIOLibAudioIO,"libaudioio"," Portable Audio Library"); -}; - -using namespace std; -using namespace Arts; - -AudioIOLibAudioIO::AudioIOLibAudioIO() -{ - /* - * default parameters - */ - param(samplingRate) = spec.rate=44100; - paramStr(deviceName) = "/dev/Audio"; - requestedFragmentSize = param(fragmentSize) = 4096; - requestedFragmentCount =spec.max_blocks= param(fragmentCount) = 7; - param(channels) = spec.channels=2; - param(direction) = 2; -} - -bool AudioIOLibAudioIO::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; - spec.channels=_channels; - spec.max_blocks= param(fragmentCount); - spec.rate= _samplingRate; - spec.encoding= ENCODE_PCM; - spec.precision=16 ; - spec.endian=ENDIAN_NATURAL; - spec.disable_threads=1; - - if(param(direction) == 3) - mode = O_RDWR|O_NDELAY; - else if(param(direction) == 2) - mode = O_WRONLY|O_NDELAY; - else - { - _error = "invalid direction"; - return false; - } - - audio_fd = ::AudioIOOpenX( mode, &spec,&spec ); - if(audio_fd == -1) - { - _error = "device "; - _error += _deviceName.c_str(); - _error += " can't be opened ("; - _error += strerror(errno); - _error += ")"; - return false; - } - - - /* - * since we use spec.endian=ENDIAN_NATURAL we'll have little endian audio - * on little endian machines and big endian audio on big endian machines: - */ -#ifdef WORDS_BIGENDIAN - _format = 17; -#else - _format = 16; -#endif - - spec.channels=_channels; - - spec.rate = _samplingRate; - - - _fragmentSize = requestedFragmentSize; - spec.max_blocks=_fragmentCount = requestedFragmentCount; - - - 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 AudioIOLibAudioIO::close() -{ - ::AudioIOClose(); -} - -void AudioIOLibAudioIO::setParam(AudioParam p, int& value) -{ - switch(p) - { - case fragmentSize: - param(p) = requestedFragmentSize = value; - break; - case fragmentCount: - param(p) = requestedFragmentCount = value; - break; - default: - param(p) = value; - break; - } -} - -int AudioIOLibAudioIO::getParam(AudioParam p) -{ - switch(p) - { - case canRead: - return AudioIOCheckRead(); - break; - - case canWrite: - return (AudioIOCheckWriteReady()) ? (16*1024) : 0; - // Assume if writable can write 16K - // Arts Really doesn't care - break; - - case selectReadFD: - return (param(direction) & directionRead)?audio_fd:-1; - break; - - case selectWriteFD: - return (param(direction) & directionWrite)?audio_fd:-1; - break; - - case autoDetect: - /* - * if there is a "native" aRts driver, we'll rather use this - * than the generic libaudioio one, because the native one - * is likely to be better optimized to the needs of aRts than - * a generic driver, so keep the value small - */ - return 3; - break; - - default: - return param(p); - break; - } -} - -int AudioIOLibAudioIO::read(void *buffer, int size) -{ - arts_assert(audio_fd != 0); - return ::AudioIORead(buffer,size); -} - -int AudioIOLibAudioIO::write(void *buffer, int size) -{ - arts_assert(audio_fd != 0); - return ::AudioIOWrite(buffer,size); -} - -#endif diff --git a/flow/audioiolibaudioio.cpp b/flow/audioiolibaudioio.cpp new file mode 100644 index 0000000..c3a887b --- /dev/null +++ b/flow/audioiolibaudioio.cpp @@ -0,0 +1,236 @@ + /* + + Copyright (C) 2001 Robert Lunnon + bob@yarrabee.net.au + Copyright (C) 2001 Stefan Westerfeld + stefan@space.twc.de + + 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 +#endif + +/** + * only compile 'LibAudioIO' AudioIO class if libaudio was detected during + * configure + */ +#ifdef HAVE_LIBAUDIOIO + +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_SELECT_H +#include // Needed on some systems. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "audioio.h" + +namespace Arts { + +class AudioIOLibAudioIO : public AudioIO { +protected: + int audio_fd; + int requestedFragmentSize; + int requestedFragmentCount; + SampleSpec_t spec; + +public: + AudioIOLibAudioIO(); + + 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(AudioIOLibAudioIO,"libaudioio"," Portable Audio Library"); +}; + +using namespace std; +using namespace Arts; + +AudioIOLibAudioIO::AudioIOLibAudioIO() +{ + /* + * default parameters + */ + param(samplingRate) = spec.rate=44100; + paramStr(deviceName) = "/dev/Audio"; + requestedFragmentSize = param(fragmentSize) = 4096; + requestedFragmentCount =spec.max_blocks= param(fragmentCount) = 7; + param(channels) = spec.channels=2; + param(direction) = 2; +} + +bool AudioIOLibAudioIO::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; + spec.channels=_channels; + spec.max_blocks= param(fragmentCount); + spec.rate= _samplingRate; + spec.encoding= ENCODE_PCM; + spec.precision=16 ; + spec.endian=ENDIAN_NATURAL; + spec.disable_threads=1; + + if(param(direction) == 3) + mode = O_RDWR|O_NDELAY; + else if(param(direction) == 2) + mode = O_WRONLY|O_NDELAY; + else + { + _error = "invalid direction"; + return false; + } + + audio_fd = ::AudioIOOpenX( mode, &spec,&spec ); + if(audio_fd == -1) + { + _error = "device "; + _error += _deviceName.c_str(); + _error += " can't be opened ("; + _error += strerror(errno); + _error += ")"; + return false; + } + + + /* + * since we use spec.endian=ENDIAN_NATURAL we'll have little endian audio + * on little endian machines and big endian audio on big endian machines: + */ +#ifdef WORDS_BIGENDIAN + _format = 17; +#else + _format = 16; +#endif + + spec.channels=_channels; + + spec.rate = _samplingRate; + + + _fragmentSize = requestedFragmentSize; + spec.max_blocks=_fragmentCount = requestedFragmentCount; + + + 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 AudioIOLibAudioIO::close() +{ + ::AudioIOClose(); +} + +void AudioIOLibAudioIO::setParam(AudioParam p, int& value) +{ + switch(p) + { + case fragmentSize: + param(p) = requestedFragmentSize = value; + break; + case fragmentCount: + param(p) = requestedFragmentCount = value; + break; + default: + param(p) = value; + break; + } +} + +int AudioIOLibAudioIO::getParam(AudioParam p) +{ + switch(p) + { + case canRead: + return AudioIOCheckRead(); + break; + + case canWrite: + return (AudioIOCheckWriteReady()) ? (16*1024) : 0; + // Assume if writable can write 16K + // Arts Really doesn't care + break; + + case selectReadFD: + return (param(direction) & directionRead)?audio_fd:-1; + break; + + case selectWriteFD: + return (param(direction) & directionWrite)?audio_fd:-1; + break; + + case autoDetect: + /* + * if there is a "native" aRts driver, we'll rather use this + * than the generic libaudioio one, because the native one + * is likely to be better optimized to the needs of aRts than + * a generic driver, so keep the value small + */ + return 3; + break; + + default: + return param(p); + break; + } +} + +int AudioIOLibAudioIO::read(void *buffer, int size) +{ + arts_assert(audio_fd != 0); + return ::AudioIORead(buffer,size); +} + +int AudioIOLibAudioIO::write(void *buffer, int size) +{ + arts_assert(audio_fd != 0); + return ::AudioIOWrite(buffer,size); +} + +#endif diff --git a/flow/audioiomas.cc b/flow/audioiomas.cc deleted file mode 100644 index cfebb65..0000000 --- a/flow/audioiomas.cc +++ /dev/null @@ -1,619 +0,0 @@ - /* - - Copyright (C) 2001-2003 Stefan Westerfeld - stefan@space.twc.de - - 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 -#endif - -/* - * Only compile this AudioIO class if we have MAS - */ -#ifdef HAVE_LIBMAS - -extern "C" { -#include -#include -#include -} - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "audioio.h" -#include "audiosubsys.h" -#include "iomanager.h" -#include "dispatcher.h" - -namespace Arts { - - class AudioIOMAS : public AudioIO, public TimeNotify { - protected: - mas_channel_t audio_channel; - mas_port_t mix_sink; - mas_port_t srate_source, srate_sink; - mas_port_t audio_source, audio_sink; - mas_port_t endian_sink, endian_source; - mas_port_t sbuf_source, sbuf_sink; - mas_port_t squant_sink, squant_source; - mas_port_t open_source; /* (!) */ - mas_device_t endian; - mas_device_t srate; - mas_device_t squant; - mas_device_t sbuf; - mas_data *data; - mas_package package; - int32 mas_error; - - std::list allocated_channels; - std::list allocated_ports; - std::list allocated_devices; - - double lastUpdate, bytesPerSec; - int readBufferAvailable; - int writeBufferAvailable; - - double currentTime(); - void updateBufferSizes(); - -#ifdef WORDS_BIGENDIAN - static const int defaultFormat = 17; -#else - static const int defaultFormat = 16; -#endif - bool close_with_error(const std::string& text); - public: - AudioIOMAS(); - - // Timer callback - void notifyTime(); - - 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(AudioIOMAS,"mas","MAS Audio Input/Output"); - -}; - -using namespace std; -using namespace Arts; - -AudioIOMAS::AudioIOMAS() -{ - /* - * default parameters - */ - param(samplingRate) = 44100; - paramStr(deviceName) = ""; // TODO - param(fragmentSize) = 4096; - param(fragmentCount) = 7; - param(channels) = 2; - param(direction) = 2; - param(format) = defaultFormat; -} - -namespace { - int masInitCount = 0; -} - -// Opens the audio device -bool AudioIOMAS::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); - - /* FIXME: do we need to free what we allocate with mas_init() in close() */ - if (!masInitCount) - { - mas_error = mas_init(); - - if (mas_error < 0) - return close_with_error("error connecting to MAS server"); - } - masInitCount++; - - if (param(direction) != 2) - { - _error = "unsupported direction (currently no full duplex support)"; - return false; - } - - /* - * data path - * - * audio_sink - * audio_channel: data_channel ("artsd") - * audio_source - * | - * V - * endian_sink - * endian: instantiate_device ("endian") - * open_source = endian_source - * | - * V - * [squant_sink] - * [squant] - * [squant_source] - * | - * V - * [srate_sink] - * [srate] - * [srate_source] - * | - * V - * sbuf_sink - * sbuf - * sbuf_source - * | - * V - * mix_sink: port ("default_mix_sink") - */ - - // audio_channel, source & sink - mas_error = mas_make_data_channel("artsd", &audio_channel, &audio_source, &audio_sink); - if (mas_error < 0) - return close_with_error("error initializing MAS data channel"); - - allocated_channels.push_back(audio_channel); - allocated_ports.push_back(audio_source); - allocated_ports.push_back(audio_sink); - - // endian, source & sink - mas_error = mas_asm_instantiate_device( "endian", 0, 0, &endian ); - if ( mas_error < 0 ) - return close_with_error("error initantiating MAS endian device"); - - allocated_devices.push_back(endian); - - mas_error = mas_asm_get_port_by_name( endian, "sink", &endian_sink ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS endian device sink port"); - - allocated_ports.push_back(endian_sink); - - mas_error = mas_asm_get_port_by_name( endian, "source", &endian_source ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS endian device source port"); - - allocated_ports.push_back(endian_source); - - char ratestring[16], resolutionstring[16]; - sprintf (ratestring, "%u", _samplingRate); - sprintf (resolutionstring, "%u", _format); - - mas_data_characteristic* dc; - - dc = (mas_data_characteristic *)MAS_NEW( dc ); - masc_setup_dc( dc, 6 ); - masc_append_dc_key_value( dc, "format", (_format==8) ? "ulinear":"linear" ); - - masc_append_dc_key_value( dc, "resolution", resolutionstring ); - masc_append_dc_key_value( dc, "sampling rate", ratestring ); - masc_append_dc_key_value( dc, "channels", "2" ); - masc_append_dc_key_value( dc, "endian", "little" ); - - mas_error = mas_asm_connect_source_sink( audio_source, endian_sink, dc ); - if ( mas_error < 0 ) - return close_with_error("error connecting MAS net audio source to endian sink"); - - /* The next device is 'if needed' only. After the following if() - statement, open_source will contain the current unconnected - source in the path (will be either endian_source or - squant_source in this case) - */ - open_source = endian_source; - - if ( _format != 16 ) - { - arts_debug("MAS output: Sample resolution is not 16 bit/sample, instantiating squant device."); - - // squant, source & sink - mas_error = mas_asm_instantiate_device( "squant", 0, 0, &squant ); - if ( mas_error < 0 ) - return close_with_error("error creating MAS squant device"); - - allocated_devices.push_back(squant); - - mas_error = mas_asm_get_port_by_name( squant, "sink", &squant_sink ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS squant device sink port"); - - allocated_ports.push_back(squant_sink); - - mas_error = mas_asm_get_port_by_name( squant, "source", &squant_source ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS squant device source port"); - - allocated_ports.push_back(squant_source); - - arts_debug( "MAS output: Connecting endian -> squant."); - - masc_strike_dc( dc ); - masc_setup_dc( dc, 6 ); - masc_append_dc_key_value( dc,"format",(_format==8) ? "ulinear":"linear" ); - masc_append_dc_key_value( dc, "resolution", resolutionstring ); - masc_append_dc_key_value( dc, "sampling rate", ratestring ); - masc_append_dc_key_value( dc, "channels", "2" ); - masc_append_dc_key_value( dc, "endian", "host" ); - - mas_error = mas_asm_connect_source_sink( endian_source, squant_sink, dc ); - if ( mas_error < 0 ) - return close_with_error("error connecting MAS endian output to squant device"); - - /* sneaky: the squant device is optional -> pretend it isn't there */ - open_source = squant_source; - } - - - /* Another 'if necessary' device, as above */ - if ( _samplingRate != 44100 ) - { - arts_debug ("MAS output: Sample rate is not 44100, instantiating srate device."); - - // srate, source & sink - mas_error = mas_asm_instantiate_device( "srate", 0, 0, &srate ); - if ( mas_error < 0 ) - return close_with_error("error initantiating MAS srate device"); - - allocated_devices.push_back(srate); - - mas_error = mas_asm_get_port_by_name( srate, "sink", &srate_sink ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS srate sink port"); - - allocated_ports.push_back(srate_sink); - - mas_error = mas_asm_get_port_by_name( srate, "source", &srate_source ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS srate source port"); - - allocated_ports.push_back(srate_source); - - arts_debug( "MAS output: Connecting to srate."); - - masc_strike_dc( dc ); - masc_setup_dc( dc, 6 ); - masc_append_dc_key_value( dc, "format", "linear" ); - masc_append_dc_key_value( dc, "resolution", "16" ); - masc_append_dc_key_value( dc, "sampling rate", ratestring ); - masc_append_dc_key_value( dc, "channels", "2" ); - masc_append_dc_key_value( dc, "endian", "host" ); - - mas_error = mas_asm_connect_source_sink( open_source, srate_sink, dc ); - if ( mas_error < 0 ) - return close_with_error("error connecting to MAS srate device"); - - open_source = srate_source; - } - - // sbuf, source & sink - mas_error = mas_asm_instantiate_device( "sbuf", 0, 0, &sbuf ); - if ( mas_error < 0 ) - return close_with_error("error initantiating MAS sbuf device"); - - allocated_devices.push_back(sbuf); - - mas_error = mas_asm_get_port_by_name( sbuf, "sink", &sbuf_sink ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS sbuf device sink port"); - - allocated_ports.push_back(sbuf_sink); - - mas_error = mas_asm_get_port_by_name( sbuf, "source", &sbuf_source ); - if ( mas_error < 0 ) - return close_with_error("error getting MAS sbuf device source port"); - - allocated_ports.push_back(sbuf_source); - - masc_strike_dc( dc ); - masc_setup_dc( dc, 6 ); - - masc_append_dc_key_value( dc, "format", "linear" ); - masc_append_dc_key_value( dc, "resolution", "16" ); - masc_append_dc_key_value( dc, "sampling rate", "44100" ); - masc_append_dc_key_value( dc, "channels", "2" ); - masc_append_dc_key_value( dc, "endian", "host" ); - - arts_debug("MAS output: Connecting to sbuf."); - - mas_error = mas_asm_connect_source_sink( open_source, sbuf_sink, dc ); - if ( mas_error < 0 ) - return close_with_error("error connecting to MAS mixer device"); - - /* configure sbuf */ - - float BUFTIME_MS = _fragmentSize * _fragmentCount; - BUFTIME_MS *= 1000.0; - BUFTIME_MS /= (float)_channels; - if (_format > 8) - BUFTIME_MS /= 2.0; - BUFTIME_MS /= (float)_samplingRate; - - arts_debug("MAS output: BUFTIME_MS = %f", BUFTIME_MS); - - masc_setup_package( &package, NULL, 0, 0 ); - masc_pushk_uint32( &package, "buftime_ms", (uint32) BUFTIME_MS ); - masc_finalize_package( &package ); - mas_set( sbuf, "buftime_ms", &package ); - masc_strike_package( &package ); - - masc_setup_package( &package, NULL, 0, 0 ); - masc_pushk_int32( &package, "mc_clkid", 9 ); - masc_finalize_package( &package ); - mas_set( sbuf, "mc_clkid", &package ); - masc_strike_package( &package ); - - mas_source_play( sbuf ); - - // mix_sink - mas_error = mas_asm_get_port_by_name( 0, "default_mix_sink", &mix_sink ); - if (mas_error < 0) - return close_with_error("error finding MAS default sink"); - - allocated_ports.push_back(mix_sink); - - arts_debug("MAS output: Connecting sbuf to mix_sink."); - - mas_error = mas_asm_connect_source_sink( sbuf_source, mix_sink, dc ); - if ( mas_error < 0 ) - return close_with_error("error connecting to MAS mixer device"); - - data = (mas_data *)MAS_NEW( data ); - masc_setup_data( data, _fragmentSize ); /* we can reuse this */ - data->length = _fragmentSize; - data->allocated_length = data->length; - data->header.type = 10; - - arts_debug("MAS output: playing."); - - // Install the timer - Dispatcher::the()->ioManager()->addTimer(10, this); - - bytesPerSec = _channels * _samplingRate; - if (_format > 8) - bytesPerSec *= 2; - - lastUpdate = 0; - - return true; -} - -double AudioIOMAS::currentTime() -{ - timeval tv; - gettimeofday(&tv,0); - - return (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; -} - -bool AudioIOMAS::close_with_error(const string& text) -{ - string& error = paramStr(lastError); - error = text; - error += masc_strmerror (mas_error); - return false; -} - -void AudioIOMAS::close() -{ - list::iterator pi; - for (pi = allocated_ports.begin(); pi != allocated_ports.end(); pi++) - mas_free_port (*pi); - allocated_ports.clear(); - - list::iterator ci; - for (ci = allocated_channels.begin(); ci != allocated_channels.end(); ci++) - mas_free_channel (*ci); - allocated_channels.clear(); - - list::iterator di; - for (di = allocated_devices.begin(); di != allocated_devices.end(); di++) - { - mas_device_t device = *di; - mas_error = mas_asm_terminate_device_instance(device, 0); - if (mas_error < 0) - arts_warning ("MAS output: error while closing device: %s", masc_strmerror(mas_error)); - - mas_free_device(device); - } - allocated_devices.clear(); - - Dispatcher::the()->ioManager()->removeTimer(this); -} - -void AudioIOMAS::updateBufferSizes() -{ - double time = currentTime(); - double waterMark = param(fragmentSize); - waterMark *= 1.3; - - if ((time - lastUpdate) * bytesPerSec < waterMark) - return; - - lastUpdate = time; - - uint32 inbuf_ms; - int32 mas_error; - - mas_error = mas_get( sbuf, "inbuf_ms", 0 , &package ); - if ( mas_error < 0 ) - arts_fatal ("MAS output: error getting size of buffer: %s", masc_strmerror(mas_error)); - - masc_pull_uint32( &package, &inbuf_ms ); - masc_strike_package( &package ); - - //arts_debug(" inbuf_ms = %u", inbuf_ms); - - float bytes = inbuf_ms; - bytes /= 1000.0; - bytes *= param(samplingRate); - bytes *= param(channels); - if(param(format) > 8) - bytes *= 2; - - int bytesFree = param(fragmentSize) * param(fragmentCount) - (int)bytes; - - if (bytesFree < param(fragmentSize)) - bytesFree = 0; - - writeBufferAvailable = bytesFree; - - arts_debug ("MAS output buffer: %6d / %6d bytes used => %6d bytes free", - (int)bytes, param(fragmentSize) * param(fragmentCount), writeBufferAvailable); -} - -// This is called on each timer tick -void AudioIOMAS::notifyTime() -{ - updateBufferSizes(); - - int& _direction = param(direction); - int& _fragmentSize = param(fragmentSize); - - for (;;) { - int todo = 0; - - if ((_direction & directionRead) && (getParam(canRead) >= _fragmentSize)) - todo |= AudioSubSystem::ioRead; - - if ((_direction & directionWrite) && (getParam(canWrite) >= _fragmentSize)) - todo |= AudioSubSystem::ioWrite; - - if (!todo) - return; - - AudioSubSystem::the()->handleIO(todo); - } -} - -void AudioIOMAS::setParam(AudioParam p, int& value) -{ - switch(p) { -#if 0 - case fragmentSize: - param(p) = requestedFragmentSize = value; - break; - case fragmentCount: - param(p) = requestedFragmentCount = value; - break; -#endif - default: - param(p) = value; - break; - } -} - -int AudioIOMAS::getParam(AudioParam p) -{ - int bytes; - int count; - - switch(p) - { -#if 0 - case canRead: - if (ioctl(audio_fd, AUDIO_GETINFO, &auinfo) < 0) - return (0); - bytes = (auinfo.record.samples * bytesPerSample) - bytesRead; - if (bytes < 0) { - printf("Error: bytes %d < 0, samples=%u, bytesRead=%u\n", - bytes, auinfo.record.samples, bytesRead); - bytes = 0; - } - return bytes; - - case canWrite: - if (ioctl(audio_fd, AUDIO_GETINFO, &auinfo) < 0) - return (0); - count = SUN_MAX_BUFFER_SIZE - - (bytesWritten - (auinfo.play.samples * bytesPerSample)); - return count; -#endif - case canWrite: - return writeBufferAvailable; - - case autoDetect: - /* - * Fairly small priority, for we haven't tested this a lot - */ - return 3; - - default: - return param(p); - } -} - -int AudioIOMAS::read(void *buffer, int size) -{ -#if 0 - size = ::read(audio_fd, buffer, size); - if (size < 0) - return 0; - - bytesRead += size; - return size; -#endif - return 0; -} - -int AudioIOMAS::write(void *buffer, int size) -{ - static int ts = 0; - static int seq = 0; - data->header.sequence = seq++; - data->header.media_timestamp = ts; - ts += size / 4; - - assert(size == data->length); - memcpy(data->segment, buffer, size); - - int32 mas_error = mas_send( audio_channel , data ); - if (mas_error < 0) - arts_fatal ("MAS output: problem during mas_send: %s", masc_strmerror(mas_error)); - - writeBufferAvailable -= size; - return size; -} - -#endif /* HAVE_LIBMAS */ diff --git a/flow/audioiomas.cpp b/flow/audioiomas.cpp new file mode 100644 index 0000000..cfebb65 --- /dev/null +++ b/flow/audioiomas.cpp @@ -0,0 +1,619 @@ + /* + + Copyright (C) 2001-2003 Stefan Westerfeld + stefan@space.twc.de + + 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 +#endif + +/* + * Only compile this AudioIO class if we have MAS + */ +#ifdef HAVE_LIBMAS + +extern "C" { +#include +#include +#include +} + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "audioio.h" +#include "audiosubsys.h" +#include "iomanager.h" +#include "dispatcher.h" + +namespace Arts { + + class AudioIOMAS : public AudioIO, public TimeNotify { + protected: + mas_channel_t audio_channel; + mas_port_t mix_sink; + mas_port_t srate_source, srate_sink; + mas_port_t audio_source, audio_sink; + mas_port_t endian_sink, endian_source; + mas_port_t sbuf_source, sbuf_sink; + mas_port_t squant_sink, squant_source; + mas_port_t open_source; /* (!) */ + mas_device_t endian; + mas_device_t srate; + mas_device_t squant; + mas_device_t sbuf; + mas_data *data; + mas_package package; + int32 mas_error; + + std::list allocated_channels; + std::list allocated_ports; + std::list allocated_devices; + + double lastUpdate, bytesPerSec; + int readBufferAvailable; + int writeBufferAvailable; + + double currentTime(); + void updateBufferSizes(); + +#ifdef WORDS_BIGENDIAN + static const int defaultFormat = 17; +#else + static const int defaultFormat = 16; +#endif + bool close_with_error(const std::string& text); + public: + AudioIOMAS(); + + // Timer callback + void notifyTime(); + + 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(AudioIOMAS,"mas","MAS Audio Input/Output"); + +}; + +using namespace std; +using namespace Arts; + +AudioIOMAS::AudioIOMAS() +{ + /* + * default parameters + */ + param(samplingRate) = 44100; + paramStr(deviceName) = ""; // TODO + param(fragmentSize) = 4096; + param(fragmentCount) = 7; + param(channels) = 2; + param(direction) = 2; + param(format) = defaultFormat; +} + +namespace { + int masInitCount = 0; +} + +// Opens the audio device +bool AudioIOMAS::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); + + /* FIXME: do we need to free what we allocate with mas_init() in close() */ + if (!masInitCount) + { + mas_error = mas_init(); + + if (mas_error < 0) + return close_with_error("error connecting to MAS server"); + } + masInitCount++; + + if (param(direction) != 2) + { + _error = "unsupported direction (currently no full duplex support)"; + return false; + } + + /* + * data path + * + * audio_sink + * audio_channel: data_channel ("artsd") + * audio_source + * | + * V + * endian_sink + * endian: instantiate_device ("endian") + * open_source = endian_source + * | + * V + * [squant_sink] + * [squant] + * [squant_source] + * | + * V + * [srate_sink] + * [srate] + * [srate_source] + * | + * V + * sbuf_sink + * sbuf + * sbuf_source + * | + * V + * mix_sink: port ("default_mix_sink") + */ + + // audio_channel, source & sink + mas_error = mas_make_data_channel("artsd", &audio_channel, &audio_source, &audio_sink); + if (mas_error < 0) + return close_with_error("error initializing MAS data channel"); + + allocated_channels.push_back(audio_channel); + allocated_ports.push_back(audio_source); + allocated_ports.push_back(audio_sink); + + // endian, source & sink + mas_error = mas_asm_instantiate_device( "endian", 0, 0, &endian ); + if ( mas_error < 0 ) + return close_with_error("error initantiating MAS endian device"); + + allocated_devices.push_back(endian); + + mas_error = mas_asm_get_port_by_name( endian, "sink", &endian_sink ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS endian device sink port"); + + allocated_ports.push_back(endian_sink); + + mas_error = mas_asm_get_port_by_name( endian, "source", &endian_source ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS endian device source port"); + + allocated_ports.push_back(endian_source); + + char ratestring[16], resolutionstring[16]; + sprintf (ratestring, "%u", _samplingRate); + sprintf (resolutionstring, "%u", _format); + + mas_data_characteristic* dc; + + dc = (mas_data_characteristic *)MAS_NEW( dc ); + masc_setup_dc( dc, 6 ); + masc_append_dc_key_value( dc, "format", (_format==8) ? "ulinear":"linear" ); + + masc_append_dc_key_value( dc, "resolution", resolutionstring ); + masc_append_dc_key_value( dc, "sampling rate", ratestring ); + masc_append_dc_key_value( dc, "channels", "2" ); + masc_append_dc_key_value( dc, "endian", "little" ); + + mas_error = mas_asm_connect_source_sink( audio_source, endian_sink, dc ); + if ( mas_error < 0 ) + return close_with_error("error connecting MAS net audio source to endian sink"); + + /* The next device is 'if needed' only. After the following if() + statement, open_source will contain the current unconnected + source in the path (will be either endian_source or + squant_source in this case) + */ + open_source = endian_source; + + if ( _format != 16 ) + { + arts_debug("MAS output: Sample resolution is not 16 bit/sample, instantiating squant device."); + + // squant, source & sink + mas_error = mas_asm_instantiate_device( "squant", 0, 0, &squant ); + if ( mas_error < 0 ) + return close_with_error("error creating MAS squant device"); + + allocated_devices.push_back(squant); + + mas_error = mas_asm_get_port_by_name( squant, "sink", &squant_sink ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS squant device sink port"); + + allocated_ports.push_back(squant_sink); + + mas_error = mas_asm_get_port_by_name( squant, "source", &squant_source ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS squant device source port"); + + allocated_ports.push_back(squant_source); + + arts_debug( "MAS output: Connecting endian -> squant."); + + masc_strike_dc( dc ); + masc_setup_dc( dc, 6 ); + masc_append_dc_key_value( dc,"format",(_format==8) ? "ulinear":"linear" ); + masc_append_dc_key_value( dc, "resolution", resolutionstring ); + masc_append_dc_key_value( dc, "sampling rate", ratestring ); + masc_append_dc_key_value( dc, "channels", "2" ); + masc_append_dc_key_value( dc, "endian", "host" ); + + mas_error = mas_asm_connect_source_sink( endian_source, squant_sink, dc ); + if ( mas_error < 0 ) + return close_with_error("error connecting MAS endian output to squant device"); + + /* sneaky: the squant device is optional -> pretend it isn't there */ + open_source = squant_source; + } + + + /* Another 'if necessary' device, as above */ + if ( _samplingRate != 44100 ) + { + arts_debug ("MAS output: Sample rate is not 44100, instantiating srate device."); + + // srate, source & sink + mas_error = mas_asm_instantiate_device( "srate", 0, 0, &srate ); + if ( mas_error < 0 ) + return close_with_error("error initantiating MAS srate device"); + + allocated_devices.push_back(srate); + + mas_error = mas_asm_get_port_by_name( srate, "sink", &srate_sink ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS srate sink port"); + + allocated_ports.push_back(srate_sink); + + mas_error = mas_asm_get_port_by_name( srate, "source", &srate_source ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS srate source port"); + + allocated_ports.push_back(srate_source); + + arts_debug( "MAS output: Connecting to srate."); + + masc_strike_dc( dc ); + masc_setup_dc( dc, 6 ); + masc_append_dc_key_value( dc, "format", "linear" ); + masc_append_dc_key_value( dc, "resolution", "16" ); + masc_append_dc_key_value( dc, "sampling rate", ratestring ); + masc_append_dc_key_value( dc, "channels", "2" ); + masc_append_dc_key_value( dc, "endian", "host" ); + + mas_error = mas_asm_connect_source_sink( open_source, srate_sink, dc ); + if ( mas_error < 0 ) + return close_with_error("error connecting to MAS srate device"); + + open_source = srate_source; + } + + // sbuf, source & sink + mas_error = mas_asm_instantiate_device( "sbuf", 0, 0, &sbuf ); + if ( mas_error < 0 ) + return close_with_error("error initantiating MAS sbuf device"); + + allocated_devices.push_back(sbuf); + + mas_error = mas_asm_get_port_by_name( sbuf, "sink", &sbuf_sink ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS sbuf device sink port"); + + allocated_ports.push_back(sbuf_sink); + + mas_error = mas_asm_get_port_by_name( sbuf, "source", &sbuf_source ); + if ( mas_error < 0 ) + return close_with_error("error getting MAS sbuf device source port"); + + allocated_ports.push_back(sbuf_source); + + masc_strike_dc( dc ); + masc_setup_dc( dc, 6 ); + + masc_append_dc_key_value( dc, "format", "linear" ); + masc_append_dc_key_value( dc, "resolution", "16" ); + masc_append_dc_key_value( dc, "sampling rate", "44100" ); + masc_append_dc_key_value( dc, "channels", "2" ); + masc_append_dc_key_value( dc, "endian", "host" ); + + arts_debug("MAS output: Connecting to sbuf."); + + mas_error = mas_asm_connect_source_sink( open_source, sbuf_sink, dc ); + if ( mas_error < 0 ) + return close_with_error("error connecting to MAS mixer device"); + + /* configure sbuf */ + + float BUFTIME_MS = _fragmentSize * _fragmentCount; + BUFTIME_MS *= 1000.0; + BUFTIME_MS /= (float)_channels; + if (_format > 8) + BUFTIME_MS /= 2.0; + BUFTIME_MS /= (float)_samplingRate; + + arts_debug("MAS output: BUFTIME_MS = %f", BUFTIME_MS); + + masc_setup_package( &package, NULL, 0, 0 ); + masc_pushk_uint32( &package, "buftime_ms", (uint32) BUFTIME_MS ); + masc_finalize_package( &package ); + mas_set( sbuf, "buftime_ms", &package ); + masc_strike_package( &package ); + + masc_setup_package( &package, NULL, 0, 0 ); + masc_pushk_int32( &package, "mc_clkid", 9 ); + masc_finalize_package( &package ); + mas_set( sbuf, "mc_clkid", &package ); + masc_strike_package( &package ); + + mas_source_play( sbuf ); + + // mix_sink + mas_error = mas_asm_get_port_by_name( 0, "default_mix_sink", &mix_sink ); + if (mas_error < 0) + return close_with_error("error finding MAS default sink"); + + allocated_ports.push_back(mix_sink); + + arts_debug("MAS output: Connecting sbuf to mix_sink."); + + mas_error = mas_asm_connect_source_sink( sbuf_source, mix_sink, dc ); + if ( mas_error < 0 ) + return close_with_error("error connecting to MAS mixer device"); + + data = (mas_data *)MAS_NEW( data ); + masc_setup_data( data, _fragmentSize ); /* we can reuse this */ + data->length = _fragmentSize; + data->allocated_length = data->length; + data->header.type = 10; + + arts_debug("MAS output: playing."); + + // Install the timer + Dispatcher::the()->ioManager()->addTimer(10, this); + + bytesPerSec = _channels * _samplingRate; + if (_format > 8) + bytesPerSec *= 2; + + lastUpdate = 0; + + return true; +} + +double AudioIOMAS::currentTime() +{ + timeval tv; + gettimeofday(&tv,0); + + return (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; +} + +bool AudioIOMAS::close_with_error(const string& text) +{ + string& error = paramStr(lastError); + error = text; + error += masc_strmerror (mas_error); + return false; +} + +void AudioIOMAS::close() +{ + list::iterator pi; + for (pi = allocated_ports.begin(); pi != allocated_ports.end(); pi++) + mas_free_port (*pi); + allocated_ports.clear(); + + list::iterator ci; + for (ci = allocated_channels.begin(); ci != allocated_channels.end(); ci++) + mas_free_channel (*ci); + allocated_channels.clear(); + + list::iterator di; + for (di = allocated_devices.begin(); di != allocated_devices.end(); di++) + { + mas_device_t device = *di; + mas_error = mas_asm_terminate_device_instance(device, 0); + if (mas_error < 0) + arts_warning ("MAS output: error while closing device: %s", masc_strmerror(mas_error)); + + mas_free_device(device); + } + allocated_devices.clear(); + + Dispatcher::the()->ioManager()->removeTimer(this); +} + +void AudioIOMAS::updateBufferSizes() +{ + double time = currentTime(); + double waterMark = param(fragmentSize); + waterMark *= 1.3; + + if ((time - lastUpdate) * bytesPerSec < waterMark) + return; + + lastUpdate = time; + + uint32 inbuf_ms; + int32 mas_error; + + mas_error = mas_get( sbuf, "inbuf_ms", 0 , &package ); + if ( mas_error < 0 ) + arts_fatal ("MAS output: error getting size of buffer: %s", masc_strmerror(mas_error)); + + masc_pull_uint32( &package, &inbuf_ms ); + masc_strike_package( &package ); + + //arts_debug(" inbuf_ms = %u", inbuf_ms); + + float bytes = inbuf_ms; + bytes /= 1000.0; + bytes *= param(samplingRate); + bytes *= param(channels); + if(param(format) > 8) + bytes *= 2; + + int bytesFree = param(fragmentSize) * param(fragmentCount) - (int)bytes; + + if (bytesFree < param(fragmentSize)) + bytesFree = 0; + + writeBufferAvailable = bytesFree; + + arts_debug ("MAS output buffer: %6d / %6d bytes used => %6d bytes free", + (int)bytes, param(fragmentSize) * param(fragmentCount), writeBufferAvailable); +} + +// This is called on each timer tick +void AudioIOMAS::notifyTime() +{ + updateBufferSizes(); + + int& _direction = param(direction); + int& _fragmentSize = param(fragmentSize); + + for (;;) { + int todo = 0; + + if ((_direction & directionRead) && (getParam(canRead) >= _fragmentSize)) + todo |= AudioSubSystem::ioRead; + + if ((_direction & directionWrite) && (getParam(canWrite) >= _fragmentSize)) + todo |= AudioSubSystem::ioWrite; + + if (!todo) + return; + + AudioSubSystem::the()->handleIO(todo); + } +} + +void AudioIOMAS::setParam(AudioParam p, int& value) +{ + switch(p) { +#if 0 + case fragmentSize: + param(p) = requestedFragmentSize = value; + break; + case fragmentCount: + param(p) = requestedFragmentCount = value; + break; +#endif + default: + param(p) = value; + break; + } +} + +int AudioIOMAS::getParam(AudioParam p) +{ + int bytes; + int count; + + switch(p) + { +#if 0 + case canRead: + if (ioctl(audio_fd, AUDIO_GETINFO, &auinfo) < 0) + return (0); + bytes = (auinfo.record.samples * bytesPerSample) - bytesRead; + if (bytes < 0) { + printf("Error: bytes %d < 0, samples=%u, bytesRead=%u\n", + bytes, auinfo.record.samples, bytesRead); + bytes = 0; + } + return bytes; + + case canWrite: + if (ioctl(audio_fd, AUDIO_GETINFO, &auinfo) < 0) + return (0); + count = SUN_MAX_BUFFER_SIZE - + (bytesWritten - (auinfo.play.samples * bytesPerSample)); + return count; +#endif + case canWrite: + return writeBufferAvailable; + + case autoDetect: + /* + * Fairly small priority, for we haven't tested this a lot + */ + return 3; + + default: + return param(p); + } +} + +int AudioIOMAS::read(void *buffer, int size) +{ +#if 0 + size = ::read(audio_fd, buffer, size); + if (size < 0) + return 0; + + bytesRead += size; + return size; +#endif + return 0; +} + +int AudioIOMAS::write(void *buffer, int size) +{ + static int ts = 0; + static int seq = 0; + data->header.sequence = seq++; + data->header.media_timestamp = ts; + ts += size / 4; + + assert(size == data->length); + memcpy(data->segment, buffer, size); + + int32 mas_error = mas_send( audio_channel , data ); + if (mas_error < 0) + arts_fatal ("MAS output: problem during mas_send: %s", masc_strmerror(mas_error)); + + writeBufferAvailable -= size; + return size; +} + +#endif /* HAVE_LIBMAS */ diff --git a/flow/audioionas.cc b/flow/audioionas.cc deleted file mode 100644 index 543d8c0..0000000 --- a/flow/audioionas.cc +++ /dev/null @@ -1,260 +0,0 @@ - /* - - Copyright (C) 2001 Jochen Hoenicke - jochen@gnu.org - - 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 -#endif - -/** - * only compile nas AudioIO class if libaudio was detected during - * configure - */ -#ifdef HAVE_LIBAUDIONAS - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "audioio.h" -#include "audiosubsys.h" -#include "iomanager.h" -#include "dispatcher.h" - -#include