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

List:       kde-multimedia
Subject:    [PATCH] proper resampling
From:       Stefan Westerfeld <stefan () space ! twc ! de>
Date:       2000-09-01 12:09:59
[Download RAW message or body]

Hi!

Until now, artsd didn't resample and/or convert incoming audio streams. If
you were running artsd with a sample rate of 44100 Hz, the only stream that
you could play properly (with artscat or artsdsp) was 44100 Hz, 16 bits,
stereo. The following patch (and new files for kdelibs/arts/flow), fixes

 * ByteStreamToAudio object: proper resampling functionality

 * aRts C API: as it relies on that, the aRts C API now instantly also
   supports all those streams (i.e. arts_play_stream)

 * artsdsp: as this is based on the C API, you can now run all kinds of apps
   with all kinds of weird sampling rates

 * artscat: supports new options like artscat -r 22050 -b 8 -c 1 to play
   an 22050 Hz, 8 bits, mono stream

The CPU usage is of course higher for incoming streams which have to be
resampled, whereas there should be not much change for those that simply
can be converted and played.

Please have a look at it.

   Cu... Stefan
-- 
  -* Stefan Westerfeld, stefan@space.twc.de (PGP!), Hamburg/Germany
     KDE Developer, project infos at http://space.twc.de/~stefan/kde *-         

["resample.cc" (text/plain)]

    /*

    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., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

    */


#include "resample.h"
#include <iostream.h>
#include <math.h>
#include <assert.h>
#include <stdio.h>

#define compose_16le(first,second) \
    (((((second)+128)&0xff) << 8)+(first))
 
#define conv_16le_float(x) \
    ((float)((x)-32768)/32768.0)                                                

#define conv_8_float(x) \
	((float)((x)-128)/128.0)

using namespace Arts;

Resampler::Resampler(Refiller *refiller) :
	refiller(refiller), pos(0.0), step(1.0), channels(2), bits(16),
	block(0), haveBlock(-1)
{
	updateSampleSize();
}

void Resampler::updateSampleSize()
{
	sampleSize = channels * bits / 8;
	bufferSamples = bufferSize / sampleSize;
}

void Resampler::setStep(double newStep)
{
	step = newStep;
}

void Resampler::setChannels(int newChannels)
{
	channels = newChannels;
	updateSampleSize();
}

void Resampler::setBits(int newBits)
{
	bits = newBits;
	updateSampleSize();
}

void Resampler::ensureRefill()
{
	if(haveBlock == block) return;

	unsigned long missing;
	if(block == 0)
	{
		missing = bufferSize+sampleSize
				- refiller->read(buffer,bufferSize+sampleSize);
	}
	else
	{
		missing = bufferSize - refiller->read(&buffer[sampleSize],bufferSize);
	}
	haveBlock++;
	assert((missing & (sampleSize - 1)) == 0);
	assert(haveBlock == block);

	unsigned int i = 0, wrap = (block == 0)?0:sampleSize;
	if(bits == 16)
	{
		// wrap the last part of the buffer back to the beginning (history)
		while(i<wrap)
		{
			fbuffer[i/2] = fbuffer[(bufferSize+i)/2];
			i += 2;
		}

		// convert data from incoming
		while(i<bufferSize+sampleSize-missing)
		{
			fbuffer[i/2] = conv_16le_float(compose_16le(buffer[i],buffer[i+1]));
			i += 2;
		}

		// fill up missing bytes with zero samples
		while(i<bufferSize+sampleSize)
		{
			fbuffer[i/2] = 0.0;
			i += 2;
		}
	}
	else if(bits == 8)
	{
		// wrap the last part of the buffer back to the beginning (history)
		while(i<wrap)
		{
			fbuffer[i] = fbuffer[bufferSize+i];
			i++;
		}

		// convert data from incoming
		while(i<bufferSize+sampleSize-missing)
		{
			fbuffer[i] = conv_8_float(buffer[i]);
			i++;
		}

		// fill up missing bytes with zero samples
		while(i<bufferSize+sampleSize)
		{
			fbuffer[i++] = 0.0;
		}
	}
	else
	{
		assert(false);
	}
}

#define RESAMPLER_STEP()							\
	pos += step;									\
	i++;											\
	while(pos >= bufferSamples)						\
	{												\
		pos -= bufferSamples;						\
		block++;									\
		ensureRefill();								\
	}

void Resampler::run(float *left, float *right, unsigned long samples)
{
	ensureRefill();
	unsigned long i = 0;
	double delta = step - floor(step);
	bool interpolate = fabs(delta) > 0.001;

	if(channels == 2 && interpolate)
	{
		while(i < samples)
		{
			double error = pos - floor(pos);
			unsigned long offset = 2*(unsigned long)pos;

			left[i]  = fbuffer[offset+0]*(1.0-error)+fbuffer[offset+2]*error;
			right[i] = fbuffer[offset+1]*(1.0-error)+fbuffer[offset+3]*error;
			RESAMPLER_STEP();
		}
	}
	else if(channels == 1 && interpolate)
	{
		while(i < samples)
		{
			double error = pos - floor(pos);
			unsigned long offset = (unsigned long)pos;

			left[i] = right[i] = fbuffer[offset]*(1.0-error)
							   + fbuffer[offset+1]*error;
			RESAMPLER_STEP();
		}
	}
	else if(channels == 2)
	{
		while(i < samples)
		{
			unsigned long offset = 2*(unsigned long)pos;

			left[i]  = fbuffer[offset+0];
			right[i] = fbuffer[offset+1];
			RESAMPLER_STEP();
		}
	}
	else if(channels == 1)
	{
		while(i < samples)
		{
			unsigned long offset = (unsigned long)pos;

			left[i] = right[i] = fbuffer[offset];
			RESAMPLER_STEP();
		}
	}
	else
	{
		assert(false);
	}
}

Refiller::~Refiller()
{
}

#undef RESAMPLER_STEP
#undef compose_16le
#undef conv_16le_float
#undef conv_8_float

["resample.h" (text/plain)]

    /*

    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., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

    */


#ifndef ARTS_FLOW_REFILL_H
#define ARTS_FLOW_REFILL_H

namespace Arts {

class Refiller {
public:
	virtual unsigned long read(unsigned char *buffer, unsigned long len) = 0;
	virtual ~Refiller();
};

class Resampler {
protected:
	static const unsigned int bufferSize = 256;		//  64 samples in buffer
	static const unsigned int bufferWrap = 4;		// + 1 sample "wrap around"

	int bufferSamples;
	int sampleSize;

	Refiller *refiller;
	double pos, step;
	int channels,bits;

	unsigned char buffer[bufferSize+bufferWrap];
	float fbuffer[bufferSize+bufferWrap];
	long block, haveBlock;

	class ResamplerPrivate *d;

	void updateSampleSize();
	void ensureRefill();
public:
	Resampler(Refiller *refiller);
	void setStep(double step);
	void setChannels(int channels);
	void setBits(int bits);
	void run(float *left, float *right, unsigned long samples);
};

};

#endif /* ARTS_FLOW_REFILL_H */

["proper-resampling.diff" (text/plain)]

? flow/x
? flow/resample.cc
? flow/resample.h
Index: flow/Makefile.am
===================================================================
RCS file: /home/kde/kdelibs/arts/flow/Makefile.am,v
retrieving revision 1.20
diff -b -u -p -r1.20 Makefile.am
--- flow/Makefile.am	2000/07/12 11:59:46	1.20
+++ flow/Makefile.am	2000/09/01 11:52:05
@@ -21,7 +21,7 @@ libartsflow_la_SOURCES =  synth_play_imp
   synth_play_wav_impl.cc stdsynthmodule.cc cache.cc utils.c 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
+  bus.cc audiomanager_impl.cc synth_record_impl.cc resample.cc
 
 artsincludedir = $(includedir)/arts
 artsinclude_HEADERS = artsflow.h audiosubsys.h cache.h \
Index: flow/bytestreamtoaudio_impl.cc
===================================================================
RCS file: /home/kde/kdelibs/arts/flow/bytestreamtoaudio_impl.cc,v
retrieving revision 1.7
diff -b -u -p -r1.7 bytestreamtoaudio_impl.cc
--- flow/bytestreamtoaudio_impl.cc	2000/05/17 22:48:20	1.7
+++ flow/bytestreamtoaudio_impl.cc	2000/09/01 11:52:06
@@ -22,103 +22,93 @@
 
 #include "artsflow.h"
 #include "stdsynthmodule.h"
+#include "resample.h"
 #include <iostream>
 
 using namespace std;
 using namespace Arts;
 
-#define compose_16le(first,second) \
-	(((((second)+128)&0xff) << 8)+(first))
 
-#define conv_16le_float(x) \
-	((float)((x)-32768)/32768.0)
-
-class ByteStreamToAudio_impl : public ByteStreamToAudio_skel,
-                               public StdSynthModule
-{
-	int haveBytes, pos;
-	queue< DataPacket<mcopbyte>* > inqueue;
-	long _samplingRate, _channels, _bits;
-	bool _running;
+class PacketRefiller : public Refiller {
 public:
-	ByteStreamToAudio_impl() :haveBytes(0), pos(0),
-			_samplingRate(44100), _channels(2), _bits(16), _running(false)
+	queue< DataPacket<mcopbyte>* > inqueue;
+	int pos;
+
+	PacketRefiller() : pos(0)
 	{
-		//
 	}
-
-	long samplingRate() { return _samplingRate; }
-	void samplingRate(long newRate) { _samplingRate = newRate; }
-
-	long channels() { return _channels; }
-	void channels(long newChannels) { _channels = newChannels; }
-
-	long bits() { return _bits; }
-	void bits(long newBits) { _bits = newBits; }
-
-	bool running() { return _running; }
-
-	void process_indata(DataPacket<mcopbyte> *packet)
+	void process(DataPacket<mcopbyte>* packet)
 	{
-		haveBytes += packet->size;
 		inqueue.push(packet);
 	}
-
-	inline mcopbyte getByte()
+	unsigned long read(unsigned char *buffer, unsigned long bytes)
+	{
+		unsigned long done = 0;
+		while(!inqueue.empty())
 	{
-		mcopbyte result;
+			long tocopy = bytes - done;
+			if(tocopy == 0) return bytes;			/* complete? */
 
-		assert(haveBytes);
 		DataPacket<mcopbyte> *packet = inqueue.front();
-		result = packet->contents[pos++];
+			if(tocopy > packet->size)
+				tocopy = packet->size;
 
+			memcpy(&buffer[done],&packet->contents[pos],tocopy);
+			pos += tocopy;
+			done += tocopy;
+
 		if(pos == packet->size) {
 			packet->processed();
 			pos = 0;
 			inqueue.pop();
 		}
-
-		haveBytes--;
-		return result;
 	}
+		return done;
+	}
+};
 
-	inline float mkSample()
+class ByteStreamToAudio_impl : public ByteStreamToAudio_skel,
+                               public StdSynthModule
+{
+	PacketRefiller refiller;
+	Resampler resampler;
+	long _samplingRate, _channels, _bits;
+	bool _running;
+public:
+	ByteStreamToAudio_impl() :resampler(&refiller),
+			_samplingRate(44100), _channels(2), _bits(16), _running(false)
 	{
-		mcopbyte first = getByte();
-		mcopbyte second = getByte();
-		return conv_16le_float(compose_16le(first,second));
+		//
 	}
-	void calculateBlock(unsigned long samples)
-	{
-		/* convert samples from buffer, as long as enough buffer space */
-		unsigned long doSamples = haveBytes/4,i;
-		if(samples < doSamples) doSamples = samples;
 
-		for(i=0;i<doSamples;i++)
-		{
-			left[i] = mkSample();
-			right[i] = mkSample();
+	long samplingRate() { return _samplingRate; }
+	void samplingRate(long newRate) {
+		_samplingRate = newRate;
+		resampler.setStep((float)_samplingRate / samplingRateFloat);
 		}
 		
-		if(i == samples) /* did we have enough input available? */
-		{
-			_running = true;
+	long channels() { return _channels; }
+	void channels(long newChannels) {
+		_channels = newChannels;
+		resampler.setChannels(_channels);
 		}
-		else
-		{
-			if(_running)
-			{
-				cout << "ByteStreamToAudio: input underrun" << endl;
-				_running = false;
+
+	long bits() { return _bits; }
+	void bits(long newBits) {
+		_bits = newBits;
+		resampler.setBits(_bits);
 			}
+
+	bool running() { return _running; }
 
-			/* fill the rest with zero samples */
-			while(i != samples)
+	void process_indata(DataPacket<mcopbyte> *packet)
 			{
-				left[i] = right[i] = 0.0;
-				i++;
-			}
+		refiller.process(packet);
 		}
+
+	void calculateBlock(unsigned long samples)
+	{
+		resampler.run(left,right,samples);
 	}
 };
 
Index: soundserver/artscat.cc
===================================================================
RCS file: /home/kde/kdelibs/arts/soundserver/artscat.cc,v
retrieving revision 1.12
diff -b -u -p -r1.12 artscat.cc
--- soundserver/artscat.cc	2000/05/17 22:48:20	1.12
+++ soundserver/artscat.cc	2000/09/01 11:52:07
@@ -34,6 +34,10 @@
 using namespace std;
 using namespace Arts;
 
+int cfgSamplingRate = 44100;
+int cfgBits = 16;
+int cfgChannels = 2;
+
 class Sender :	public ByteSoundProducer_skel,
 				public StdSynthModule,
 				public IONotify
@@ -69,9 +73,9 @@ public:
 		if(waiting) Dispatcher::the()->ioManager()->remove(this,IOType::read);
 	}
 
-	long samplingRate() { return 44100; }
-	long channels()     { return 2; }
-	long bits()         { return 16; }
+	long samplingRate() { return cfgSamplingRate; }
+	long channels()     { return cfgChannels; }
+	long bits()         { return cfgBits; }
 	bool finished()     { return (pfile == 0); }
 
 	void streamStart()
@@ -160,12 +164,34 @@ public:
 	}
 };
 
+static void exitUsage(const char *progname)
+{
+	fprintf(stderr,"usage: %s [ options ]\n",progname);
+	fprintf(stderr,"-r <samplingrate>   set samplingrate to use\n");
+	fprintf(stderr,"-b <bits>           set number of bits (8 or 16)\n");
+	fprintf(stderr,"-c <channels>       set number of channels (1 or 2)\n");
+	fprintf(stderr,"-h                  display this help and exit\n");
+	exit(1);	
+}
+
 int main(int argc, char **argv)
 {
-	if(argc != 1)
+	int optch;
+	while((optch = getopt(argc,argv,"r:b:c:h")) > 0)
 	{
-		cerr << "usage: " << argv[0] << endl;
-		return 1;
+		switch(optch)
+		{
+			case 'r': cfgSamplingRate = atoi(optarg);
+				break;
+			case 'b': cfgBits = atoi(optarg);
+				break;
+			case 'c': cfgChannels = atoi(optarg);
+				break;
+			case 'h':
+			default: 
+					exitUsage(argc?argv[0]:"artscat");
+				break;
+		}
 	}
 
 	Dispatcher dispatcher;
Index: soundserver/simplesoundserver_impl.cc
===================================================================
RCS file: /home/kde/kdelibs/arts/soundserver/simplesoundserver_impl.cc,v
retrieving revision 1.20
diff -b -u -p -r1.20 simplesoundserver_impl.cc
--- soundserver/simplesoundserver_impl.cc	2000/07/12 11:59:46	1.20
+++ soundserver/simplesoundserver_impl.cc	2000/09/01 11:52:08
@@ -52,6 +52,10 @@ PlayWavJob::PlayWavJob(const string& fil
 	wav.filename(filename);
 	wav.start();
 	out.start();
+
+    timeval tv;
+    gettimeofday(&tv, 0);
+    printf("[ARTS: file %s started] %d sec, %d usec\n",filename.c_str(), tv.tv_sec, \
tv.tv_usec);                                                                          \
  }
 
 void PlayWavJob::terminate()
@@ -68,9 +72,9 @@ PlayStreamJob::PlayStreamJob(ByteSoundPr
 {
 	printf("Attach ByteSoundProducer!\n");
 
-//	convert->samplingRate(bsp->samplingRate());
-//	convert->channels(bsp->channels());
-//	convert->bits(bsp->bits());
+	convert.samplingRate(bsp.samplingRate());
+	convert.channels(bsp.channels());
+	convert.bits(bsp.bits());
 
 	connect(sender,"outdata",convert,"indata");
 	connect(convert,out);


_______________________________________________
Kde-multimedia mailing list
Kde-multimedia@master.kde.org
http://master.kde.org/mailman/listinfo/kde-multimedia


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

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