Apologies, forgot to add a file to the patch. Use the following
attached git changeset instead.

On Tue, Apr 6, 2010 at 12:24 AM, Carmelo Piccione
<[email protected]> wrote:
> Ring buffer solution attached. It uses spin locks to check if the
> buffer has sufficient data available before reading from it.
>
> There is a define called RING_BUFFER_SIZE_MULTIPLIER which defaults to
> 4 and sets the ring buffer size to 4 times that of what it would be if
> it was the original buffer fifo implementation. I found that 4x, 8x,
> or higher gave most smooth audio playback results. Ultimately, this
> define should go and the size should be set dynamically.
>
> This is just a draft, so please give me feedback if you try it!
>
> - Carmelo
>
>
> On Thu, Apr 1, 2010 at 5:46 PM, Tobias Doerffel
> <[email protected]> wrote:
>> Hi,
>>
>> Am Mittwoch, 31. März 2010 14:34:43 schrieb Carmelo Piccione:
>>> I reached the same solution as you with the RT safe ring buffer. Sadly I
>>> moronically misunderstood the implementation  in AudioJack.cpp and ended up
>>> using the read / write ring buffer in reverse of how it was intended (I
>>> copy the space given by jack in the processing thread to write audio, then
>>> write to the copy in the ring buffer on a RT qthread. )
>>>
>>> Instead I need to copy the audio buffer from lmms into jack using a similar
>>> mechanism. I'm hoping I can patch the code I wrote some time this week to
>>> do just that.
>> Please also have a look at the jack-fixes branch at
>> http://lmms.git.sourceforge.net/git/gitweb.cgi?p=lmms/lmms;a=shortlog;h=refs/heads/jack-
>> fixes - it does not work very well yet but includes some fixes for better RT
>> safety.
>>
>> Toby
>>
>> ------------------------------------------------------------------------------
>> Download Intel&#174; Parallel Studio Eval
>> Try the new software tools for yourself. Speed compiling, find bugs
>> proactively, and fine-tune applications for parallel performance.
>> See why Intel Parallel Studio got high marks during beta.
>> http://p.sf.net/sfu/intel-sw-dev
>> _______________________________________________
>> LMMS-devel mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/lmms-devel
>>
>>
>
From 8729d752f7abded80c06e38d2f61461926e1b6e5 Mon Sep 17 00:00:00 2001
From: Carmelo Piccione <[email protected]>
Date: Tue, 6 Apr 2010 00:28:14 -0400
Subject: [PATCH] jack ring buffer patch

Allows a settable cmake option to enable use of jack ring buffers for more performant audio playback.
---
 CMakeLists.txt                        |    6 +
 include/AudioOutputContext.h          |   45 ++++---
 include/RingBufferFifo.h              |  250 +++++++++++++++++++++++++++++++++
 src/core/audio/AudioOutputContext.cpp |   15 ++-
 4 files changed, 293 insertions(+), 23 deletions(-)
 create mode 100644 include/RingBufferFifo.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd13070..6078d36 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,12 @@ OPTION(WANT_VST_NOWINE	"Include partial VST support (without wine)" OFF)
 OPTION(WANT_WINMM	"Include WinMM MIDI support" OFF)
 OPTION(WANT_ZIP		"Include zip support" ON)
 OPTION(WANT_FLAC	"Include flac support" ON)
+OPTION(WANT_JACK_RING_BUFFERS "Enable jack ring buffers" ON)
+
+IF(WANT_JACK_RING_BUFFERS)
+ADD_DEFINITIONS("-DUSE_JACK_RING_BUFFERS")
+ELSE(WANT_JACK_RING_BUFFERS)
+ENDIF(WANT_JACK_RING_BUFFERS)
 
 IF(LMMS_BUILD_WIN32)
 	SET(WANT_ALSA OFF)
diff --git a/include/AudioOutputContext.h b/include/AudioOutputContext.h
index f8847ca..93646db 100644
--- a/include/AudioOutputContext.h
+++ b/include/AudioOutputContext.h
@@ -32,6 +32,11 @@
 
 class AudioBackend;
 
+#ifdef USE_JACK_RING_BUFFERS
+#include "RingBufferFifo.h"	
+#endif
+
+
 /*! \brief The AudioOutputContext class centralizes all functionality
  * and data related to output of audio data.
  *
@@ -70,22 +75,22 @@ public:
 
 		/*! Lists all supported oversampling ratios. */
 		enum Oversampling
-		{
+		{		        
 			Oversampling_None,			/*!< No oversampling - fast */
 			Oversampling_2x,			/*!< 2x oversampling - good quality */
 			Oversampling_4x,			/*!< 4x oversampling - better quality */
-			Oversampling_8x				/*!< 8x oversampling - best quality but might break some filters */
+			Oversampling_8x,				/*!< 8x oversampling - best quality but might break some filters */
 		} ;
-
+				
 		/*! \brief Constructs a QualitySettings object based on a given preset. */
 		QualitySettings( Preset m )
 		{
 			switch( m )
 			{
 				case Preset_Draft:
-					m_interpolation = Interpolation_Linear;
-					m_oversampling = Oversampling_None;
-					m_sampleExactControllers = false;
+					m_interpolation = Interpolation_Linear;					  
+					m_oversampling = Oversampling_None;					
+					m_sampleExactControllers = false;					  
 					m_aliasFreeOscillators = false;
 					break;
 				case Preset_HighQuality:
@@ -109,7 +114,7 @@ public:
 		QualitySettings( Interpolation _i, Oversampling _o, bool _sec,
 								bool _afo ) :
 			m_interpolation( _i ),
-			m_oversampling( _o ),
+			m_oversampling( _o ),			
 			m_sampleExactControllers( _sec ),
 			m_aliasFreeOscillators( _afo )
 		{
@@ -119,7 +124,7 @@ public:
 		int sampleRateMultiplier() const
 		{
 			switch( oversampling() )
-			{
+			{			  
 				case Oversampling_None: return 1;
 				case Oversampling_2x: return 2;
 				case Oversampling_4x: return 4;
@@ -127,7 +132,7 @@ public:
 			}
 			return 1;
 		}
-
+		
 		/*! \brief Maps interpolation setting to libsamplerate constants. */
 		int libsrcInterpolation() const
 		{
@@ -162,13 +167,13 @@ public:
 		{
 			return m_oversampling;
 		}
-
+		
 		/*! \brief Sets a new oversampling factor. */
 		void setOversampling( Oversampling oversampling )
 		{
 			m_oversampling = oversampling;
 		}
-
+		
 		/*! \brief Returns whether to use sample exact controllers. */
 		bool sampleExactControllers() const
 		{
@@ -183,7 +188,7 @@ public:
 
 	private:
 		Interpolation m_interpolation;
-		Oversampling m_oversampling;
+		Oversampling m_oversampling;		
 		bool m_sampleExactControllers;
 		bool m_aliasFreeOscillators;
 
@@ -218,7 +223,7 @@ public:
 		 * \param bufferSize The size of each buffer in the FIFO
 		 */
 		BufferFifo( int size, int bufferSize );
-		~BufferFifo();
+		virtual ~BufferFifo();
 
 		/*! \brief Enable or disable support for realtime reading */
 		void setRealtimeReading( bool enabled )
@@ -259,7 +264,7 @@ public:
 			return m_fillState == 0;
 		}
 
-
+  
 	private:
 		bool m_realtimeReading;
 		volatile int m_fillState;
@@ -274,6 +279,12 @@ public:
 
 	} ;
 
+	#ifdef USE_JACK_RING_BUFFERS
+	typedef RingBufferFifo BufferFifoType;
+	#else
+	typedef BufferFifo BufferFifoType;
+	#endif
+
 	/*! \brief The FifoWriter class provides an internal thread for feeding
 	 * the FIFO read by the active AudioBackend */
 	class FifoWriter : public QThread
@@ -334,7 +345,7 @@ public:
 	}
 
 	/*! \brief Returns BufferFifo object used by this context. */
-	BufferFifo * fifo()
+	BufferFifoType * fifo()
 	{
 		return m_fifo;
 	}
@@ -366,12 +377,12 @@ public:
 	int getCurrentOutputBuffer( sampleFrameA * destBuffer,
 									sample_rate_t destSampleRate );
 
-
 private:
 	Mixer * m_mixer;
 	QualitySettings m_qualitySettings;
 	AudioBackend * m_audioBackend;
-	BufferFifo * m_fifo;
+	
+	BufferFifoType * m_fifo;
 	FifoWriter * m_fifoWriter;
 
 	// resample data
diff --git a/include/RingBufferFifo.h b/include/RingBufferFifo.h
new file mode 100644
index 0000000..f85a514
--- /dev/null
+++ b/include/RingBufferFifo.h
@@ -0,0 +1,250 @@
+
+ /* RingBufferFifo.h - base-class for audio-devices, used by LMMS-mixer
+ *
+ * Copyright (c) 2004-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
+ *
+ * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program (see COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _RING_BUFFER_FIFO_H
+#define _RING_BUFFER_FIFO_H
+
+#include <QtCore/QPair>
+#include <QtCore/QThread>
+#include <QtDebug>
+
+#include "Mixer.h"
+#include "config_mgr.h"
+
+#include "tab_widget.h"
+
+#ifdef LMMS_HAVE_JACK
+//#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <jack/types.h>
+#endif
+
+#include <cassert>
+#include "Cpu.h"
+#define RING_BUFFER_SIZE_MULTIPLER 4
+
+class RingBufferFifo // implements BufferFifoType
+{
+  public :
+	
+		/*! \brief Constructs a new RingBufferFifo object.
+		 *
+		 * \param size The number of buffers in the FIFO
+		 * \param bufferSize The size of each ring buffer in the FIFO
+		 */
+		RingBufferFifo( int size, int bufferSize );
+		virtual ~RingBufferFifo();
+		
+		/*! Each buffer in the FIFO can have a special state. This is used
+		 * by FifoWriter to inject NULL buffers to indicate, the FIFO has
+		 * been emptied after FifoWriter was told to finish. */
+		enum BufferStates
+		{
+			Running,	/*!< Regular buffer */
+			NullBuffer	/*!< Even if the buffer returned by currentReadBuffer()
+						 * is not NULL, the FIFO input was NULL. FIFO reader can
+						 * use this information for own purposes. */
+		} ;
+		
+		typedef BufferStates BufferState;
+		/*! \brief Enable or disable support for realtime reading */
+		void setRealtimeReading( bool enabled )
+		{
+			// always real time mode
+		}
+
+		/*! \brief Pushes a new buffer into the FIFO.
+		 *
+		 * You can also push NULL which will set the according buffer state
+		 * to HasNullBuffer. */
+		void write( sampleFrameA * buffer );
+
+		/*! \brief Prepares for reading next buffer (might block until one is available). */
+		void startRead();
+
+		/*! \brief Returns current front buffer for reading. */
+		inline sampleFrameA * currentReadBuffer() const
+		{		  
+			return m_tempBuffer;
+		}
+
+		/*! \brief Returns state of current front buffer. */
+		inline BufferState currentReadBufferState() const
+		{
+			return m_bufferState;
+		}
+
+		/*! \brief Finishes the read operation for the current buffer.
+		 *
+		* The buffer returned by currentReadBuffer() is not guaranteed to
+		* be valid anymore after calling this function. */
+		void finishRead();
+
+		/*! \brief Returns whether FIFO is empty. */
+		inline bool isEmpty() const
+		{
+			return jack_ringbuffer_read_space(m_ringbuffer) < sizeof(sampleFrameA)*m_bufferSize;
+		}
+  private:
+		void readNextBuffer() const;
+		    		
+		QSemaphore m_readerSem;
+		QSemaphore m_writerSem;
+		const int m_size;
+		const int m_bufferSize;
+		#ifdef LMMS_HAVE_JACK
+		mutable jack_ringbuffer_t * m_ringbuffer;
+		#endif
+		mutable volatile BufferState m_bufferState;
+		mutable sampleFrameA * m_tempBuffer;
+
+} ;
+
+
+/* Implementations below */
+inline RingBufferFifo::RingBufferFifo( int _size, int _bufferSize ) :
+	m_readerSem( _size ),
+	m_writerSem( _size ),
+	m_size( _size ),
+	m_bufferSize( _bufferSize ),
+	m_bufferState(NullBuffer)
+{
+
+	m_ringbuffer = jack_ringbuffer_create( m_size * m_bufferSize *sizeof(sampleFrameA) * RING_BUFFER_SIZE_MULTIPLER);
+	qDebug() << "ring buffer size: " << jack_ringbuffer_write_space(m_ringbuffer);
+	m_tempBuffer = new sampleFrameA[m_bufferSize];
+	
+	CPU::memClear(m_tempBuffer, m_bufferSize*sizeof(sampleFrameA));
+	
+	m_readerSem.acquire( _size );
+}
+
+inline void RingBufferFifo::write( sampleFrameA * _buffer )
+{
+  
+//	qDebug () << "write space is: " << jack_ringbuffer_write_space(m_ringbuffer);
+		  
+		
+	while(_buffer != NULL && jack_ringbuffer_write_space(m_ringbuffer) <
+		  m_bufferSize*sizeof(sampleFrameA))
+		{
+#if QT_VERSION >= 0x040500
+			QThread::yieldCurrentThread();
+#else
+#ifdef LMMS_BUILD_LINUX
+			usleep( 1 );
+#elif defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
+			asm( "pause" );
+#endif
+#endif
+		}
+
+	if( _buffer != NULL )
+	{
+	        
+		const std::size_t written = jack_ringbuffer_write(m_ringbuffer, (char*)_buffer, 
+			    m_bufferSize*sizeof(sampleFrameA));
+//		qDebug() << "wrote " << written << " / " << (m_bufferSize*sizeof(sampleFrameA));
+		
+		assert(written == m_bufferSize*sizeof(sampleFrameA));
+		
+		m_bufferState = Running;
+	}
+	else
+	{
+//		qDebug() << "null buffer state";
+		m_bufferState = NullBuffer;
+	}
+
+
+}
+
+
+
+
+inline void RingBufferFifo::startRead()
+{
+  readNextBuffer();	
+}
+
+
+
+
+inline void RingBufferFifo::finishRead()
+{
+  // qDebug() << " read finished!";
+   // set a boolean here or something to new?
+}
+
+inline RingBufferFifo::~RingBufferFifo()
+{
+
+	jack_ringbuffer_free(m_ringbuffer);
+	
+	delete[] m_tempBuffer;
+	
+	m_readerSem.release( m_size );
+}
+
+
+inline void RingBufferFifo::readNextBuffer() const {
+  
+  
+  // Frame skip idea, uses previous buffer rather than spin locking. 
+  // Thus far I found conventional spin locking more agreeable, but
+  // it's worth considering spinning N number of times at most as the best
+  // of both worlds in practice, if we can decide what N should be.
+  // - [email protected]
+  //if (jack_ringbuffer_read_space(m_ringbuffer) < sizeof(sampleFrameA)*m_bufferSize) {
+    //qDebug() << "insufficient read space: " << jack_ringbuffer_read_space(m_ringbuffer) 
+      //<< " < " << sizeof(sampleFrameA)*m_bufferSize;
+    //return;
+    
+  //}
+  
+  
+  while (jack_ringbuffer_read_space(m_ringbuffer) < sizeof(sampleFrameA)*m_bufferSize)
+   {
+#if QT_VERSION >= 0x040500
+	QThread::yieldCurrentThread();
+#else
+#ifdef LMMS_BUILD_LINUX
+	usleep( 1 );
+#elif defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
+	asm( "pause" );
+#endif
+#endif
+  
+  }
+  
+  //qDebug() << "about to read: " << m_bufferSize*sizeof(sampleFrameA);
+  
+  const std::size_t read = jack_ringbuffer_read(m_ringbuffer, (char*)m_tempBuffer, sizeof(sampleFrameA)*m_bufferSize);
+  
+  assert(read == sizeof(sampleFrameA)*m_bufferSize);
+  //qDebug() << "read : " << read;
+}
+
+
+#endif
\ No newline at end of file
diff --git a/src/core/audio/AudioOutputContext.cpp b/src/core/audio/AudioOutputContext.cpp
index 664453c..91e946d 100644
--- a/src/core/audio/AudioOutputContext.cpp
+++ b/src/core/audio/AudioOutputContext.cpp
@@ -29,6 +29,9 @@
 #include "config_mgr.h"
 #include "engine.h"
 
+#ifdef USE_JACK_RING_BUFFERS
+#include "RingBufferFifo.h"
+#endif
 
 AudioOutputContext::BufferFifo::BufferFifo( int _size, int _bufferSize ) :
 	m_realtimeReading( false ),
@@ -173,7 +176,7 @@ AudioOutputContext::AudioOutputContext( Mixer * mixer,
 	// just rendering?
 	if( !engine::hasGUI() )
 	{
-		m_fifo = new BufferFifo( 1, framesPerPeriod );
+		m_fifo = new BufferFifoType( 1, framesPerPeriod );
 	}
 	else if( configManager::inst()->value( "mixer", "framesperaudiobuffer"
 						).toInt() >= 32 )
@@ -184,12 +187,12 @@ AudioOutputContext::AudioOutputContext( Mixer * mixer,
 
 		if( framesPerPeriod > DEFAULT_BUFFER_SIZE )
 		{
-			m_fifo = new BufferFifo( framesPerPeriod / DEFAULT_BUFFER_SIZE,
-														DEFAULT_BUFFER_SIZE );
+			m_fifo = new BufferFifoType( framesPerPeriod / DEFAULT_BUFFER_SIZE,
+													DEFAULT_BUFFER_SIZE );
 		}
 		else
 		{
-			m_fifo = new BufferFifo( 1, framesPerPeriod );
+			m_fifo = new BufferFifoType( 1, framesPerPeriod );
 		}
 	}
 	else
@@ -197,7 +200,7 @@ AudioOutputContext::AudioOutputContext( Mixer * mixer,
 		configManager::inst()->setValue( "mixer",
 							"framesperaudiobuffer",
 				QString::number( framesPerPeriod ) );
-		m_fifo = new BufferFifo( 1, framesPerPeriod );
+		m_fifo = new BufferFifoType( 1, framesPerPeriod );
 	}
 
 }
@@ -265,7 +268,7 @@ int AudioOutputContext::getCurrentOutputBuffer( sampleFrameA * _destBuf,
 {
 	int frames = mixer()->framesPerPeriod();
 	m_fifo->startRead();
-	if( m_fifo->currentReadBufferState() == BufferFifo::NullBuffer )
+	if( m_fifo->currentReadBufferState() == BufferFifoType::NullBuffer )
 	{
 		m_fifo->finishRead();
 		return 0;
-- 
1.6.6.1

------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
LMMS-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/lmms-devel

Reply via email to