summaryrefslogtreecommitdiffstats
path: root/flow/synth_play_wav_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'flow/synth_play_wav_impl.cpp')
-rw-r--r--flow/synth_play_wav_impl.cpp579
1 files changed, 579 insertions, 0 deletions
diff --git a/flow/synth_play_wav_impl.cpp b/flow/synth_play_wav_impl.cpp
new file mode 100644
index 0000000..dec68f0
--- /dev/null
+++ b/flow/synth_play_wav_impl.cpp
@@ -0,0 +1,579 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+
+ 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 "config.h"
+#ifdef HAVE_LIBAUDIOFILE
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include "cachedwav.h"
+#include "convert.h"
+#include <stdio.h>
+#include <iostream>
+#include <climits>
+#include <cstdlib>
+#include <cstring>
+
+extern "C" {
+/* Some versions of libaudiofile seem to lack the extern "C" declaration,
+ * so you you may need that extra one.
+ *
+ * Other versions of libaudiofile seem to have two closing "}" in aupvlist.h,
+ * so if you can't compile, this, check that /usr/include/aupvlist.h contains
+ * something like that
+ *
+ * #ifdef __cplusplus
+ * }
+ * #endif
+ *
+ * only once not twice.
+ */
+#include "audiofile.h"
+}
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+using namespace std;
+using namespace Arts;
+
+
+CachedWav *CachedWav::load(Cache *cache, string filename)
+{
+ CachedWav *wav;
+
+ wav = (CachedWav *)cache->get(string("CachedWav:")+filename);
+ if(!wav) {
+ wav = new CachedWav(cache,filename);
+
+ if(!wav->initOk) // loading failed
+ {
+ wav->decRef();
+ return 0;
+ }
+ }
+
+ return(wav);
+}
+
+bool CachedWav::isValid()
+{
+ if(!initOk)
+ return false;
+
+ struct stat newstat;
+
+ lstat(filename.c_str(),&newstat);
+ return(newstat.st_mtime == oldstat.st_mtime);
+}
+
+int CachedWav::memoryUsage()
+{
+ return(bufferSize);
+}
+
+CachedWav::CachedWav(Cache *cache, string filename) : CachedObject(cache),
+ filename(filename),initOk(false), buffer(0)
+{
+ int sampleFormat;
+ AFframecount frameCount;
+ AFfilehandle file;
+
+ setKey(string("CachedWav:")+filename);
+
+ if(lstat(filename.c_str(),&oldstat) == -1)
+ {
+ arts_info("CachedWav: Can't stat file '%s'", filename.c_str());
+ return;
+ }
+
+ file = afOpenFile(filename.c_str(), "r", NULL);
+ if(!file)
+ {
+ arts_info("CachedWav: Can't read file '%s'", filename.c_str());
+ return;
+ }
+
+ frameCount = afGetFrameCount(file, AF_DEFAULT_TRACK);
+ if(frameCount <= 0 || frameCount >= INT_MAX)
+ {
+ arts_info("CachedWav: Invalid length for '%s'", filename.c_str());
+ afCloseFile(file);
+ return;
+ }
+
+ channelCount = afGetChannels(file, AF_DEFAULT_TRACK);
+ afGetSampleFormat(file, AF_DEFAULT_TRACK, &sampleFormat, &sampleWidth);
+
+ // we want everything converted to little endian unconditionally
+ afSetVirtualByteOrder(file,AF_DEFAULT_TRACK, AF_BYTEORDER_LITTLEENDIAN);
+
+ arts_debug("loaded wav %s",filename.c_str());
+ arts_debug(" sample format: %d, sample width: %d",
+ sampleFormat,sampleWidth);
+ arts_debug(" channelCount: %d",channelCount);
+ arts_debug(" frameCount: %d",frameCount);
+
+ // different handling required for other sample widths
+ assert(sampleWidth == 16 || sampleWidth == 8);
+
+ long frameSize = (sampleWidth/8)*channelCount;
+ samplingRate = afGetRate(file, AF_DEFAULT_TRACK);
+
+ /*
+ * if we don't know the track bytes, we'll have to figure out ourselves
+ * how many frames are stored here - it would be nicer if libaudiofile
+ * let us know somehow whether the value returned for getFrameCount
+ * means "don't know" or is really the correct length
+ */
+ int trackBytes = afGetTrackBytes(file, AF_DEFAULT_TRACK);
+ if(trackBytes == -1)
+ {
+ arts_debug("unknown length");
+ long fcount = 0, f = 0;
+
+ list<void *> blocks;
+ do
+ {
+ void *block = malloc(1024 * frameSize);
+
+ f = afReadFrames(file, AF_DEFAULT_TRACK,block,1024);
+ if(f > 0)
+ {
+ fcount += f;
+ blocks.push_back(block);
+ }
+ else
+ {
+ free(block);
+ }
+ } while(f > 0);
+
+ frameCount = fcount;
+ arts_debug("figured out frameCount = %ld", fcount);
+
+ bufferSize = frameCount * frameSize;
+ buffer = new uchar[bufferSize];
+ assert(buffer);
+
+ // reassemble and free the blocks
+ while(!blocks.empty())
+ {
+ void *block = blocks.front();
+ blocks.pop_front();
+
+ f = (fcount>1024)?1024:fcount;
+ memcpy(&buffer[(frameCount-fcount)*frameSize],block,f*frameSize);
+ fcount -= f;
+ }
+ assert(fcount == 0);
+ }
+ else
+ {
+ bufferSize = frameCount * frameSize;
+ buffer = new uchar[bufferSize];
+ assert(buffer);
+
+ afReadFrames(file, AF_DEFAULT_TRACK,buffer,frameCount);
+ }
+
+ afCloseFile(file);
+ initOk = true;
+}
+
+CachedWav::~CachedWav()
+{
+ if(buffer)
+ delete[] buffer;
+}
+
+namespace Arts {
+
+class Synth_PLAY_WAV_impl : public Synth_PLAY_WAV_skel, public StdSynthModule {
+protected:
+ double flpos;
+
+ float _speed;
+ string _filename;
+ bool _finished;
+ CachedWav *cachedwav;
+
+ void unload()
+ {
+ if(cachedwav)
+ {
+ cachedwav->decRef();
+ cachedwav = 0;
+ }
+ }
+
+ void load()
+ {
+ // unload the old file if necessary
+ unload();
+
+ // load the new (which will reset the position)
+ cachedwav = CachedWav::load(Cache::the(), _filename);
+ flpos = 0.0;
+ }
+
+public:
+ float speed() { return _speed; }
+ void speed(float newSpeed) { _speed = newSpeed; }
+
+ string filename() { return _filename; }
+ void filename(const string& filename) { _filename = filename; load(); }
+
+ void finished(bool f)
+ {
+ if(_finished != f)
+ {
+ _finished = f;
+ finished_changed(f);
+ }
+ }
+ bool finished() { return _finished; }
+
+ Synth_PLAY_WAV_impl();
+ ~Synth_PLAY_WAV_impl();
+
+ void streamInit();
+ void calculateBlock(unsigned long samples);
+};
+
+REGISTER_IMPLEMENTATION(Synth_PLAY_WAV_impl);
+
+}
+
+Synth_PLAY_WAV_impl::Synth_PLAY_WAV_impl()
+{
+ cachedwav = 0;
+ _speed = 1.0;
+ _filename = "";
+ _finished = false;
+}
+
+Synth_PLAY_WAV_impl::~Synth_PLAY_WAV_impl()
+{
+ unload();
+}
+
+void Synth_PLAY_WAV_impl::streamInit()
+{
+ finished(false);
+}
+
+void Synth_PLAY_WAV_impl::calculateBlock(unsigned long samples)
+{
+ unsigned long haveSamples = 0;
+
+ if(cachedwav)
+ {
+ double speed = cachedwav->samplingRate / samplingRateFloat * _speed;
+
+ haveSamples = uni_convert_stereo_2float(samples, cachedwav->buffer,
+ cachedwav->bufferSize,cachedwav->channelCount,cachedwav->sampleWidth,
+ left,right,speed,flpos);
+
+ flpos += (double)haveSamples * speed;
+ }
+
+ if(haveSamples != samples)
+ {
+ unsigned long i;
+
+ for(i=haveSamples;i<samples;i++)
+ left[i] = right[i] = 0.0;
+
+ finished(true);
+ }
+
+/*
+ float speed = 0.0;
+ unsigned long haveSamples = 0;
+
+ if(cachedwav)
+ {
+ float allSamples = cachedwav->bufferSize*8 /
+ cachedwav->sampleWidth/cachedwav->channelCount;
+ float fHaveSamples = allSamples - flpos;
+
+ speed = cachedwav->samplingRate / (float)samplingRate * _speed;
+
+ fHaveSamples /= speed;
+ fHaveSamples -= 2.0; // one due to interpolation and another against
+ // rounding errors
+ if(fHaveSamples > 0)
+ {
+ haveSamples = (int)fHaveSamples;
+ if(haveSamples > samples) haveSamples = samples;
+ }
+ }
+
+ if(haveSamples) // something left to play?
+ {
+ if(cachedwav->channelCount == 1)
+ {
+ if(cachedwav->sampleWidth == 16) {
+ interpolate_mono_16le_float(haveSamples,
+ flpos,speed,cachedwav->buffer,left);
+ }
+ else {
+ interpolate_mono_8_float(haveSamples,
+ flpos,speed,cachedwav->buffer,left);
+ }
+ memcpy(right,left,sizeof(float)*haveSamples);
+ }
+ else if(cachedwav->channelCount == 2)
+ {
+ if(cachedwav->sampleWidth == 16) {
+ interpolate_stereo_i16le_2float(haveSamples,
+ flpos,speed,cachedwav->buffer,left,right);
+ }
+ else {
+ interpolate_stereo_i8_2float(haveSamples,
+ flpos,speed,cachedwav->buffer,left,right);
+ }
+ } else {
+ assert(false);
+ }
+
+ flpos += (float)haveSamples * speed;
+ }
+
+ if(haveSamples != samples)
+ {
+ unsigned long i;
+
+ for(i=haveSamples;i<samples;i++)
+ left[i] = right[i] = 0.0;
+
+ _finished = true;
+ }
+*/
+}
+
+
+#if 0
+class Synth_PLAY_WAV :public SynthModule {
+protected:
+ CachedWav *cachedwav;
+
+ unsigned char *buffer;
+ int channelCount;
+ unsigned long bufferSize, position, bytesPerSample;
+
+ // inputs:
+ enum { PROP_FILENAME };
+
+ // outputs:
+ enum { LEFT, RIGHT, DONE };
+
+public:
+ void Initialize();
+ void DeInitialize();
+ void Calculate() { assert(false); }
+ void CalculateBlock(unsigned long samples);
+ string getParams() { return("_filename;left,right,done"); }
+ static void *Creator() { return new Synth_PLAY_WAV; }
+};
+
+ModuleClient MC_Synth_PLAY_WAV(SynthModule::get_MS,"Synth_PLAY_WAV",Synth_PLAY_WAV::Creator);
+
+void Synth_PLAY_WAV::CalculateBlock(unsigned long samples)
+{
+ unsigned long haveSamples = samples;
+ unsigned long remainingSamples;
+
+ remainingSamples = (bufferSize-position)/bytesPerSample;
+ if(haveSamples > remainingSamples) haveSamples = remainingSamples;
+
+ float *left = out[LEFT], *right = out[RIGHT], *done = out[DONE];
+ unsigned long i;
+
+ if(haveSamples)
+ {
+ if(channelCount == 1)
+ {
+ if(bytesPerSample == 2) {
+ convert_mono_16le_float(haveSamples,&buffer[position],left);
+ }
+ else {
+ convert_mono_8_float(haveSamples,&buffer[position],left);
+ }
+ memcpy(right,left,sizeof(float)*haveSamples);
+ }
+ else if(channelCount == 2)
+ {
+ if(bytesPerSample == 2) {
+ convert_stereo_i16le_2float(haveSamples,&buffer[position],
+ left,right);
+ }
+ else {
+ convert_stereo_i8_2float(haveSamples,&buffer[position],
+ left,right);
+ }
+ } else {
+ assert(false);
+ }
+
+ for(i=0;i<haveSamples;i++) done[i] = 0.0;
+
+ position += bytesPerSample*channelCount*haveSamples;
+ }
+
+ for(i=haveSamples;i<samples;i++)
+ {
+ left[i] = right[i] = 0.0; done[i] = 1.0; // ready, kill me ;)
+ }
+}
+
+void Synth_PLAY_WAV::DeInitialize()
+{
+ cachedwav->decRef();
+}
+
+void Synth_PLAY_WAV::Initialize()
+{
+ cachedwav = CachedWav::load(Synthesizer->getCache(),
+ getStringProperty(PROP_FILENAME));
+
+ // may take some speed to access cachedwav every time
+ bufferSize = cachedwav->bufferSize;
+ channelCount = cachedwav->channelCount;
+ buffer = cachedwav->buffer;
+ bytesPerSample = cachedwav->sampleWidth/8;
+
+ haveCalculateBlock = true;
+ position = 0;
+}
+
+class Synth_PLAY_PITCHED_WAV :public SynthModule {
+protected:
+ CachedWav *cachedwav;
+ float flpos;
+
+ // inputs:
+ enum { FREQUENCY,RECFREQUENCY, PROP_FILENAME };
+
+ // outputs:
+ enum { LEFT, RIGHT, DONE };
+
+public:
+ void Initialize();
+ void DeInitialize();
+ void Calculate() { assert(false); }
+ void CalculateBlock(unsigned long samples);
+ string getParams() { return("frequency,recfrequency,_filename;left,right,done"); }
+ static void *Creator() { return new Synth_PLAY_PITCHED_WAV; }
+};
+
+ModuleClient MC_Synth_PLAY_PITCHED_WAV(SynthModule::get_MS,"Synth_PLAY_PITCHED_WAV",Synth_PLAY_PITCHED_WAV::Creator);
+
+void Synth_PLAY_PITCHED_WAV::CalculateBlock(unsigned long samples)
+{
+ float frequency = in[FREQUENCY][0], recfrequency = in[RECFREQUENCY][0];
+ float allSamples = cachedwav->bufferSize*8 /
+ cachedwav->sampleWidth/cachedwav->channelCount;
+ float fHaveSamples = allSamples - flpos;
+ float speed = cachedwav->samplingRate / (float)samplingRate *
+ frequency / recfrequency;
+
+ fHaveSamples /= speed;
+ fHaveSamples -= 2.0; // one due to interpolation and another against
+ // rounding errors
+
+ unsigned long haveSamples;
+
+ if(fHaveSamples < 0)
+ {
+ haveSamples = 0;
+ }
+ else
+ {
+ haveSamples = fHaveSamples;
+ if(haveSamples > samples) haveSamples = samples;
+ }
+
+ float *left = out[LEFT], *right = out[RIGHT], *done = out[DONE];
+ unsigned long i;
+
+ if(haveSamples)
+ {
+ if(cachedwav->channelCount == 1)
+ {
+ if(cachedwav->sampleWidth == 16) {
+ interpolate_mono_16le_float(haveSamples,
+ flpos,speed,cachedwav->buffer,left);
+ }
+ else {
+ interpolate_mono_8_float(haveSamples,
+ flpos,speed,cachedwav->buffer,left);
+ }
+ memcpy(right,left,sizeof(float)*haveSamples);
+ }
+ else if(cachedwav->channelCount == 2)
+ {
+ if(cachedwav->sampleWidth == 16) {
+ interpolate_stereo_i16le_2float(haveSamples,
+ flpos,speed,cachedwav->buffer,left,right);
+ }
+ else {
+ interpolate_stereo_i8_2float(haveSamples,
+ flpos,speed,cachedwav->buffer,left,right);
+ }
+ } else {
+ assert(false);
+ }
+
+ for(i=0;i<haveSamples;i++) done[i] = 0.0;
+
+ flpos += (float)haveSamples * speed;
+ }
+
+ for(i=haveSamples;i<samples;i++)
+ {
+ left[i] = right[i] = 0.0; done[i] = 1.0; // ready, kill me ;)
+ }
+}
+
+void Synth_PLAY_PITCHED_WAV::DeInitialize()
+{
+ cachedwav->decRef();
+}
+
+void Synth_PLAY_PITCHED_WAV::Initialize()
+{
+ cachedwav = CachedWav::load(Synthesizer->getCache(),
+ getStringProperty(PROP_FILENAME));
+
+ haveCalculateBlock = true;
+ flpos = 0.0;
+}
+#endif
+
+#else
+#ifdef __GNUC__
+#warning "No libaudiofile available, that means, you won't be able to play wavs"
+#endif
+#endif
+