[prev in list] [next in list] [prev in thread] [next in thread] 

List:       arts
Subject:    Multithreaded audioiooss for arts
From:       Matthias Welwarsky <matze () stud ! fbi ! fh-darmstadt ! de>
Date:       2001-05-28 23:09:30
[Download RAW message or body]

Hi there,

I've been mailing with Stefan Westerfeld already, but he seems to be pretty 
busy right now. I'd like to hear your opinion on some work I have done on 
aRts'  OSS driver. 

Background: I'm running a recent CVS version of KDE, where aRts is the main 
audio subsystem. As I'm not really satisfied with the it's performance, I 
decided to try something on it that I have used in the past to fix a similar 
problem with a mp3 jukebox I wrote back in the good old days of qt-1.4: 
Posix-Threads.

The main problem with aRts, as it stands, is that it's not really "Realtime". 
I've had a look at the implementation of the iomanager, dispatcher and 
audiosubsys classes and it a) uses a mainloop with select(), and b) uses 
"new" for allocation of stream data and all other kind of buffering.

select() and the mainloop is not really bad by itself, but as there is no 
prioritization for the events and only one thread of execution, it's not very 
deterministic how fast e.g. an audio io request gets served after it 
signalled out through select(). So you can go for either "crackles and pops" 
or high latency due to excessive buffering on the soundcard. Plus, as the 
producers seem to share the execution thread with the audio subsystem, a 
producer can hold off audio io request from beeing services indefinitely long.

"new", with the standard implementation, is not very deterministic, either. 
Plus, allocating and freeing tons of small memory blocks all over the heap 
makes a good deal with fragmenting it.

I know that posix threads are not really portable, because there's the 
bsd-posix, the solaris-posix, the hpux-posix, the linux-posix. though I think 
on most OS's it's definitely usable and worth trying.

I have attached a patch with a multithreaded audioiooss.cc I hacked up the 
last few days. It's not particularely mature, merely a "proof of concept", 
but it has two notable advantages above the original implementation: It uses 
much less CPU (about 1/6th) and gives crackle-free audio even under high cpu 
loads. I have tested it under KDE with noatun (the KDE media player), playing 
two DivX or mpeg videos simultaneously. The sound is absolutely peerless, 
even if the video stream becomes jumpy because of the lack of computing time.

The implementation is not perfect, though, because it adds additional 
buffering (16384 bytes in the current patch) and thus increased latency - but 
there is no other way to demontrate it without hacking up at least 
audioiosubsys and the iomanager and dispatcher classes to be also 
multithreaded.

The patch is against KDE CVS, and as you webpage tells that KDE always 
contains the latest stable release of aRts, it should be not too difficult to 
try it out.

best regards
	Matthias

["arts-oss-mt.patch" (text/plain)]

Index: flow/Makefile.am
===================================================================
RCS file: /cvs/kdelibs/arts/flow/Makefile.am,v
retrieving revision 1.42
diff -u -r1.42 Makefile.am
--- flow/Makefile.am	2001/05/19 01:12:10	1.42
+++ flow/Makefile.am	2001/05/28 22:29:42
@@ -11,7 +11,7 @@
 libartsflow_idl_la_LDFLAGS = -no-undefined
 libartsflow_idl_la_LIBADD = $(top_builddir)/arts/mcop/libmcop.la
 
-libartsflow_la_LIBADD = $(top_builddir)/arts/mcop/libmcop.la libartsflow_idl.la \
$(LIBAUDIOFILE) $(LIBASOUND) $(LIBAUDIOIO) $(LIBOSSAUDIO) $(LIBAUDIONAS) -lm \
+libartsflow_la_LIBADD = $(top_builddir)/arts/mcop/libmcop.la libartsflow_idl.la \
$(LIBAUDIOFILE) $(LIBASOUND) $(LIBAUDIOIO) $(LIBOSSAUDIO) $(LIBAUDIONAS) \
$(LIBPTHREAD) -lm  libartsflow_la_LDFLAGS = -no-undefined
 libartsflow_la_SOURCES =  synth_play_impl.cc \
   synthschedule.cc audiosubsys.cc \
@@ -22,13 +22,14 @@
   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 audioionull.cc audioiolibaudioio.cc \
-  audioiosun.cc audioioaix.cc audioionas.cc cpuinfo.cc
+  audioiosun.cc audioioaix.cc audioionas.cc cpuinfo.cc \
+  audioioossthreaded.cc audioioalsathreaded.cc
 
 artsincludedir = $(includedir)/arts
 artsinclude_HEADERS = artsflow.h audiosubsys.h cache.h \
   cachedwav.h convert.h pipebuffer.h stdsynthmodule.h \
   synthschedule.h fft.h artsflow.idl audioio.h resample.h \
-  cpuinfo.h
+  cpuinfo.h bufferqueue.h
 
 DISTCLEANFILES = artsflow.cc artsflow.h artsflow.mcoptype artsflow.mcopclass
 
Index: mcop/mcoputils.cc
===================================================================
RCS file: /cvs/kdelibs/arts/mcop/mcoputils.cc,v
retrieving revision 1.18
diff -u -r1.18 mcoputils.cc
--- mcop/mcoputils.cc	2001/04/28 18:07:03	1.18
+++ mcop/mcoputils.cc	2001/05/28 22:29:49
@@ -85,7 +85,7 @@
 	if(lstat(tmpdir.c_str(),&st) != 0)
 		arts_fatal("can't stat %s (%s)", tmpdir.c_str(),strerror(errno));
 
-	if (st.st_uid != getuid ())
+	if (st.st_uid != geteuid ())
 		arts_fatal("%s is not owned by user", tmpdir.c_str());
 
 	if(st.st_mode & 0077)
Index: soundserver/artswrapper.c
===================================================================
RCS file: /cvs/kdelibs/arts/soundserver/artswrapper.c,v
retrieving revision 1.11
diff -u -r1.11 artswrapper.c
--- soundserver/artswrapper.c	2001/03/26 16:01:30	1.11
+++ soundserver/artswrapper.c	2001/05/28 22:29:50
@@ -30,7 +30,7 @@
 		struct sched_param sp;
 		long priority = (sched_get_priority_max(SCHED_FIFO) +
 			             sched_get_priority_min(SCHED_FIFO))/2;
-		
+
 		sp.sched_priority = priority;
 
 		if(sched_setscheduler(0, SCHED_FIFO, &sp) == 0)
@@ -88,10 +88,13 @@
 
 	/* drop root privileges if running setuid root
 	   (due to realtime priority stuff) */
-	if (geteuid() != getuid()) 
+	if (geteuid() != getuid())
 	{
-#if defined (HAVE_SETEUID) && !defined (HAVE_SETEUID_FAKE) 
-		seteuid(getuid());
+#if defined (HAVE_SETEUID) && !defined (HAVE_SETEUID_FAKE)
+        //seteuid(getuid());
+        uid_t ruid = getuid();
+        setuid(0);
+        seteuid(ruid);
 #else
 		setreuid(-1, getuid());
 #endif
--- /dev/null	Fri Jan 19 09:35:41 2001
+++ flow/bufferqueue.h	Mon May 28 00:08:10 2001
@@ -0,0 +1,115 @@
+#ifndef _BUFFERQUEUE_H
+#define _BUFFERQUEUE_H
+
+#include <pthread.h>
+#include <semaphore.h>
+
+#define _CHUNK_SIZE 4096
+#define _MAX_CHUNKS 4
+
+class ByteBuffer
+{
+	unsigned char content[_CHUNK_SIZE];
+	int _size;
+	int rp;
+
+public:
+	ByteBuffer() { _size = rp = 0; }
+	ByteBuffer(const void* s, int len) { put(s, len); }
+
+	~ByteBuffer() {}
+
+	void put(const void* s, int len) {
+		if ((_size = len) != 0)
+			memcpy(content, s, len);
+		rp = 0;
+	}
+
+	char* get()                { return content+rp; }
+    char* reset()              { _size = 0; rp = 0; return content; }
+	int push(int len)          { _size -= len; rp += len; return _size; }
+    void set(int len)          { _size = len; rp = 0; }
+	int size()                 { return _size; }
+    int maxSize()              { return _CHUNK_SIZE; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class BufferQueue
+{
+private:
+	ByteBuffer bufs[_MAX_CHUNKS];
+	int rp;
+	int wp;
+	sem_t sema_produced;
+	sem_t sema_consumed;
+
+    void semaReinit() {
+    	sem_destroy(&sema_consumed);
+	    sem_destroy(&sema_produced);
+	    sem_init(&sema_consumed, 0, _MAX_CHUNKS);
+	    sem_init(&sema_produced, 0, 0);
+    }
+
+
+public:
+	BufferQueue() {
+	    rp = wp = 0;
+	    sem_init(&sema_consumed, 0, _MAX_CHUNKS);
+	    sem_init(&sema_produced, 0, 0);
+    }
+
+	~BufferQueue() {
+	    sem_destroy(&sema_consumed);
+	    sem_destroy(&sema_produced);
+    }
+
+	void write(void* data, int len);
+    ByteBuffer* waitConsumed();
+    void produced();
+
+    ByteBuffer* waitProduced();
+	void consumed();
+
+    bool isEmpty() const       { int i; sem_getvalue(&sema_produced, &i); return i \
== 0; } +	int bufferedChunks() const { int i; sem_getvalue(&sema_produced, &i); \
return i; } +    int maxChunks() const      { return _MAX_CHUNKS; }
+    int chunkSize() const      { return _CHUNK_SIZE; }
+	void clear()               { rp = wp = 0; semaReinit(); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline void BufferQueue::write(void* data, int len)
+{
+	sem_wait(&sema_consumed);
+	bufs[wp].put(data, len);
+	++wp %= _MAX_CHUNKS;
+	sem_post(&sema_produced);
+}
+
+inline ByteBuffer* BufferQueue::waitConsumed()
+{
+    sem_wait(&sema_consumed);
+    return &bufs[wp];
+}
+
+inline void BufferQueue::produced()
+{
+    ++wp %= _MAX_CHUNKS;
+    sem_post(&sema_produced);
+}
+
+inline ByteBuffer* BufferQueue::waitProduced()
+{
+	sem_wait(&sema_produced);
+	return &bufs[rp];
+}
+
+inline void BufferQueue::consumed()
+{
+	++rp %=_MAX_CHUNKS;
+	sem_post(&sema_consumed);
+}
+
+#endif
--- /dev/null	Fri Jan 19 09:35:41 2001
+++ flow/audioioossthreaded.cc	Sun May 27 15:47:28 2001
@@ -0,0 +1,673 @@
+    /*
+
+    Copyright (C) 2000 Stefan Westerfeld
+                       stefan@space.twc.de
+
+    Multithreading support by
+        Matthias Welwarsky <matze@stud.fbi.fh-darmstadt.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., 59 Temple Place - Suite 330,
+    Boston, MA 02111-1307, USA.
+
+    */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+#define COMPILE_AUDIOIO_OSS 1
+#endif
+
+#ifdef HAVE_SOUNDCARD_H
+#include <soundcard.h>
+#define COMPILE_AUDIOIO_OSS 1
+#endif
+
+/**
+ * only compile 'oss' AudioIO class if sys/soundcard.h or soundcard.h is present
+ * also, only compile if libpthread was found
+ */
+#if defined(COMPILE_AUDIOIO_OSS) && defined(HAVE_LIBPTHREAD)
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <iostream>
+#include <algorithm>
+
+#include "debug.h"
+#include "audioio.h"
+#include "audiosubsys.h"
+#include "iomanager.h"
+#include "dispatcher.h"
+
+// thread safe data queue implementation
+// this also includes pthread.h, semaphore.h
+#include "bufferqueue.h"
+
+static void* startReaderThreadInternal(void*);
+static void* startWriterThreadInternal(void*);
+
+namespace Arts {
+
+class AudioIOOSSThreaded : public AudioIO, public TimeNotify {
+private:
+    void startThread();
+    void stopThread();
+
+    BufferQueue readerQueue;
+    BufferQueue writerQueue;
+    bool runReaderThread;
+    bool runWriterThread;
+
+    pthread_t readerThreadID;
+    pthread_t writerThreadID;
+    pthread_mutex_t audio_fd_lock;
+
+    uid_t euid;
+
+protected:
+	int audio_fd;
+	int requestedFragmentSize;
+	int requestedFragmentCount;
+
+	std::string findDefaultDevice();
+
+public:
+	AudioIOOSSThreaded();
+
+	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);
+
+    void notifyTime();
+
+    void readerThread();
+    void writerThread();
+};
+
+REGISTER_AUDIO_IO(AudioIOOSSThreaded,"toss","Threaded Open Sound System");
+};
+
+using namespace std;
+using namespace Arts;
+
+static void* startReaderThreadInternal(void* object)
+{
+    ((AudioIOOSSThreaded*)object)->readerThread();
+    return 0;
+}
+
+static void* startWriterThreadInternal(void* object)
+{
+    ((AudioIOOSSThreaded*)object)->writerThread();
+    return 0;
+}
+
+/*
+ * Tries to figure out which is the OSS device we should write to
+ */
+string AudioIOOSSThreaded::findDefaultDevice()
+{
+	static const char *device[] = {
+		"/dev/dsp",						/* Linux (and lots of others) */
+		"/dev/sound/dsp0",				/* Linux with devfs-only installation */
+		"/dev/audio",					/* OpenBSD */
+		0
+	};
+
+	for(int i = 0; device[i]; i++)
+		if(access(device[i],F_OK) == 0)
+			return device[i];
+
+	return device[0];
+}
+
+AudioIOOSSThreaded::AudioIOOSSThreaded()
+{
+	/*
+	 * default parameters
+	 */
+	param(samplingRate) = 44100;
+	paramStr(deviceName) = findDefaultDevice();
+	requestedFragmentSize = param(fragmentSize) = 1024;
+	requestedFragmentCount = param(fragmentCount) = 7;
+	param(channels) = 2;
+	param(direction) = 2;
+
+    runReaderThread = false;
+    runWriterThread = false;
+
+    pthread_mutex_init(&audio_fd_lock, 0);
+
+    euid = geteuid();
+}
+
+bool AudioIOOSSThreaded::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;
+
+	if(param(direction) == 3)
+		mode = O_RDWR;
+	else if(param(direction) == 2)
+		mode = O_WRONLY;
+	else
+	{
+		_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;
+	}
+	/*
+	 * check device capabilities
+	 */
+	int device_caps;
+	if(ioctl(audio_fd,SNDCTL_DSP_GETCAPS,&device_caps) == -1)
+            device_caps=0;
+
+	string caps = "";
+	if(device_caps & DSP_CAP_DUPLEX) caps += "duplex ";
+	if(device_caps & DSP_CAP_REALTIME) caps += "realtime ";
+	if(device_caps & DSP_CAP_BATCH) caps += "batch ";
+	if(device_caps & DSP_CAP_COPROC) caps += "coproc ";
+	if(device_caps & DSP_CAP_TRIGGER) caps += "trigger ";
+	if(device_caps & DSP_CAP_MMAP) caps += "mmap ";
+	artsdebug("device capabilities: revision%d %s",
+					device_caps & DSP_CAP_REVISION, caps.c_str());
+
+	int requestedFormat = (_format == 8)?AFMT_U8:AFMT_S16_LE;
+	int gotFormat = requestedFormat;
+	if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &gotFormat)==-1)
+	{
+		_error = "SNDCTL_DSP_SETFMT failed - ";
+		_error += strerror(errno);
+
+		close();
+		return false;
+	}
+
+	if (_format && (gotFormat != 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);
+
+	//////////////////////////////////////////////////////////////////////
+	// MW
+	// stopping here because the fragment settings cannot be done
+	// is not sensible. Especially not as we check the settings in the
+	// very next step. We should handle this gracefully.
+	//
+	//////////////////////////////////////////////////////////////////////
+	//
+	//	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;
+	//}
+	ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_arg);
+
+	/*
+	 * 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("AudioIOOSSThreaded: 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 of sound i/o operation";
+
+			close();
+			return false;
+		}
+	}
+
+    startThread();
+
+    Dispatcher::the()->ioManager()->addTimer(10, this);
+
+	return true;
+}
+
+void AudioIOOSSThreaded::close()
+{
+
+    Dispatcher::the()->ioManager()->removeTimer(this);
+
+    stopThread();
+	::close(audio_fd);
+}
+
+void AudioIOOSSThreaded::notifyTime()
+{
+	int& _direction = param(direction);
+    int& _fragmentSize = param(fragmentSize);
+
+    //fprintf(stderr, "AudioIOOSSThreaded::notifyTime() entered, canRead:%d, \
canWrite:%d\n", +    //                getParam(canRead), getParam(canWrite));
+
+    for(;;)
+	{
+		int todo = 0;
+
+		if((_direction & directionRead) && getParam(canRead) >= _fragmentSize)
+			todo |= AudioSubSystem::ioRead;
+
+		if((_direction & directionWrite) && getParam(canWrite) >= _fragmentSize)
+			todo |= AudioSubSystem::ioWrite;
+
+		if(!todo) // break endless loop
+			break;
+
+		AudioSubSystem::the()->handleIO(todo);
+	}
+    //fprintf(stderr, "AudioIOOSSThreaded::notifyTime() exit after %d loops\n", \
count); +}
+
+void AudioIOOSSThreaded::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 AudioIOOSSThreaded::getParam(AudioParam p)
+{
+	switch(p)
+	{
+		case canRead:
+                return readerQueue.bufferedChunks() * readerQueue.chunkSize();
+			break;
+
+		case canWrite:
+                return (writerQueue.maxChunks() - writerQueue.bufferedChunks()) * \
writerQueue.chunkSize(); +			break;
+
+		case selectFD:
+				return -1 /* audio_fd */;
+			break;
+
+		case autoDetect:
+				/* It compiles, but Posix Threads don't work everywhere */
+				return 4;
+			break;
+
+		default:
+				return param(p);
+			break;
+	}
+}
+
+int AudioIOOSSThreaded::read(void *buffer, int size)
+{
+    int copied = 0;
+    int copySize;
+    ByteBuffer* tmpBuf = 0;
+    while (size > 0) {
+        if (!tmpBuf)
+            tmpBuf = readerQueue.waitProduced();
+
+        copySize = (size > tmpBuf->size())? tmpBuf->size():size;
+        memcpy(((char*)buffer)+copied, tmpBuf->get(), copySize);
+
+        if (tmpBuf->push(copySize) == 0) {
+            tmpBuf = 0;
+            readerQueue.consumed();
+        }
+
+        copied += copySize;
+        size   -= copySize;
+    }
+    //fprintf(stderr, "AudioIOOSSThreaded::read() got %d samples\n", copied);
+    return copied;
+}
+
+int AudioIOOSSThreaded::write(void *buffer, int size)
+{
+    int copied = 0;
+    while (size > 0) {
+        int copySize = (size > \
writerQueue.chunkSize())?writerQueue.chunkSize():size; +        \
writerQueue.write(buffer, copySize); +        copied += copySize;
+        size   -= copySize;
+    }
+    //fprintf(stderr, "AudioIOOSSThreaded::write() wrote %d samples\n", copied);
+    return copied;
+}
+
+/*
+ * posix thread to feed the audio device
+ */
+
+void AudioIOOSSThreaded::writerThread()
+{
+    fprintf(stderr, "AudioIOOSSThreaded::writerThread() thread started\n");
+
+    seteuid(euid);
+
+    ssize_t size;
+    ByteBuffer* tmpBuf = 0;
+	while (runWriterThread) {
+
+		if (!tmpBuf) {
+			tmpBuf = writerQueue.waitProduced();
+
+            if (!tmpBuf->size()) {
+				tmpBuf = NULL;
+				writerQueue.consumed();
+                continue;
+            }
+		}
+
+        //pthread_mutex_lock(&audio_fd_lock);
+		size = ::write(audio_fd, tmpBuf->get(), tmpBuf->size());
+        //pthread_mutex_unlock(&audio_fd_lock);
+
+		if (size >= 0) {
+			if (tmpBuf->push(size) == 0) {
+				tmpBuf = NULL;
+				writerQueue.consumed();
+			}
+		} else if (errno != EINTR) {
+            // this is a fatal error. we cannot write to the fd any more.
+			runWriterThread = false;
+            fprintf(stderr, "AudioIOOSSTHreaded::writerThread() fatal error writing \
to audio_fd\n"); +		}
+	}
+    fprintf(stderr, "AudioIOOSSThreaded::writerThread() thread stopped\n");
+}
+
+/*
+ * posix thread to read the audio device
+ */
+
+void AudioIOOSSThreaded::readerThread()
+{
+    fprintf(stderr, "AudioIOOSSThreaded::readerThread() thread started\n");
+
+    seteuid(euid);
+
+    ByteBuffer* tmpBuf;
+    ssize_t size;
+    while (runReaderThread) {
+        tmpBuf = readerQueue.waitConsumed();
+
+        //pthread_mutex_lock(&audio_fd_lock);
+        size = ::read(audio_fd, tmpBuf->reset(), tmpBuf->maxSize());
+        //pthread_mutex_unlock(&audio_fd_lock);
+
+        if (size >= 0) {
+            tmpBuf->set(size);
+            readerQueue.produced();
+        } else if (errno != EINTR) {
+            runReaderThread = false;
+            fprintf(stderr, "AudioIOOSSTHreaded::readerThread() fatal error reading \
from audio_fd\n"); +        }
+        //fprintf(stderr, "AudioIOOSSThreaed::readerThread got %d bytes, \
queued:%d\n", size,readerQueue.bufferedChunks()); +	}
+    fprintf(stderr, "AudioIOOSSThreaded::readerThread() thread stopped\n");
+}
+
+void AudioIOOSSThreaded::startThread()
+{
+    pthread_attr_t attrs;
+    struct sched_param sp;
+
+	pthread_attr_init(&attrs);
+    pthread_attr_setschedpolicy(&attrs, SCHED_FIFO);
+    sp.sched_priority = 45;
+    pthread_attr_setschedparam(&attrs, &sp);
+
+    seteuid(0);
+    if (param(direction) & directionWrite) {
+        runWriterThread = true;
+        pthread_create(&writerThreadID,&attrs, startWriterThreadInternal, \
(void*)this); +    }
+    if (param(direction) & directionRead) {
+        runReaderThread = true;
+        pthread_create(&readerThreadID, &attrs, startReaderThreadInternal, \
(void*)this); +    }
+    seteuid(euid);
+}
+
+void AudioIOOSSThreaded::stopThread()
+{
+    runReaderThread = false;
+    runWriterThread = false;
+
+    void *foo;
+    pthread_join(writerThreadID,&foo);
+    pthread_join(readerThreadID,&foo);
+}
+
+#endif



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic