diff options
Diffstat (limited to 'flow/synth_play_wav_impl.cpp')
-rw-r--r-- | flow/synth_play_wav_impl.cpp | 579 |
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 + 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 "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 + |