You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
500 lines
10 KiB
500 lines
10 KiB
/*
|
|
|
|
Copyright (C) 2002 Hans Meine
|
|
hans_meine@gmx.net
|
|
|
|
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 <gsl/gsldatahandle.h>
|
|
#include <gsl/gslloader.h>
|
|
#include <gsl/gslwaveosc.h>
|
|
#include "artsflow.h"
|
|
#include "stdsynthmodule.h"
|
|
#include "debug.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <gslpp/datahandle.h>
|
|
|
|
using namespace std;
|
|
|
|
namespace Arts {
|
|
|
|
static GSL::DataHandle getDHandle(DataHandle handle);
|
|
|
|
class DataHandle_impl : virtual public DataHandle_skel
|
|
{
|
|
protected:
|
|
GSL::DataHandle dhandle_;
|
|
long errno_;
|
|
friend GSL::DataHandle getDHandle(DataHandle handle);
|
|
|
|
public:
|
|
DataHandle_impl(GSL::DataHandle dhandle = GSL::DataHandle::null())
|
|
: dhandle_(dhandle)
|
|
{
|
|
errno_ = dhandle_.isNull() ? 0 : dhandle_.open();
|
|
}
|
|
|
|
~DataHandle_impl()
|
|
{
|
|
if(dhandle_.isOpen())
|
|
dhandle_.close();
|
|
}
|
|
|
|
long valueCount()
|
|
{
|
|
return dhandle_.valueCount();
|
|
}
|
|
|
|
long bitDepth()
|
|
{
|
|
return dhandle_.bitDepth();
|
|
}
|
|
|
|
long channelCount()
|
|
{
|
|
return dhandle_.channelCount();
|
|
}
|
|
|
|
long errorNo()
|
|
{
|
|
return errno_;
|
|
}
|
|
|
|
GSL::DataHandle createCropped(long headCutValueCount,
|
|
long tailCutValueCount)
|
|
{
|
|
return dhandle_.createCropped(headCutValueCount, tailCutValueCount);
|
|
}
|
|
|
|
GSL::DataHandle createCut(long cutOffset,
|
|
long cutValueCount)
|
|
{
|
|
return dhandle_.createCut(cutOffset, cutValueCount);
|
|
}
|
|
|
|
GSL::DataHandle createReversed()
|
|
{
|
|
return dhandle_.createReversed();
|
|
}
|
|
};
|
|
|
|
class ReversedDataHandle_impl : virtual public DataHandle_impl,
|
|
virtual public ReversedDataHandle_skel
|
|
{
|
|
void init(DataHandle sourceHandle)
|
|
{
|
|
DataHandle_impl *impl = dynamic_cast<DataHandle_impl *>(sourceHandle._base());
|
|
dhandle_ = impl->createReversed();
|
|
}
|
|
};
|
|
|
|
class CroppedDataHandle_impl : virtual public DataHandle_impl,
|
|
virtual public CroppedDataHandle_skel
|
|
{
|
|
void init(DataHandle sourceHandle,
|
|
long headCutValueCount,
|
|
long tailCutValueCount)
|
|
{
|
|
DataHandle_impl *impl = dynamic_cast<DataHandle_impl *>(sourceHandle._base());
|
|
dhandle_ = impl->createCropped(headCutValueCount, tailCutValueCount);
|
|
}
|
|
};
|
|
|
|
class CutDataHandle_impl : virtual public DataHandle_impl,
|
|
virtual public CutDataHandle_skel
|
|
{
|
|
void init(DataHandle sourceHandle,
|
|
long cutOffset,
|
|
long cutValueCount)
|
|
{
|
|
DataHandle_impl *impl = dynamic_cast<DataHandle_impl *>(sourceHandle._base());
|
|
dhandle_ = impl->createCut(cutOffset, cutValueCount);
|
|
}
|
|
};
|
|
|
|
static GSL::DataHandle getDHandle(DataHandle handle)
|
|
{
|
|
DataHandle_impl *impl = dynamic_cast<DataHandle_impl *>(handle._base());
|
|
if(impl)
|
|
return impl->dhandle_;
|
|
return GSL::DataHandle::null();
|
|
}
|
|
|
|
GslWaveChunk* const_wchunk_from_freq(gpointer wchunk_data, gfloat)
|
|
{
|
|
return (GslWaveChunk*)wchunk_data;
|
|
}
|
|
|
|
// FIXME: Think about whether this is needed as a parameter like mixerFrequency
|
|
#define OSCILLATOR_FREQUENCY 440.0f
|
|
|
|
class DataHandlePlay_impl : virtual public DataHandlePlay_skel,
|
|
virtual public StdSynthModule
|
|
{
|
|
protected:
|
|
DataHandle dhandle_;
|
|
|
|
GSL::DataHandle gslDHandle_;
|
|
int errno_;
|
|
GslWaveChunk *wchunk_;
|
|
GslErrorType wchunkError_;
|
|
GslWaveOscData *wosc_;
|
|
|
|
float mixerFrequency_;
|
|
unsigned short channelIndex_;
|
|
float speed_;
|
|
unsigned long pos_;
|
|
bool finished_;
|
|
bool paused_;
|
|
|
|
public:
|
|
DataHandlePlay_impl()
|
|
: dhandle_(DataHandle::null()),
|
|
gslDHandle_(GSL::DataHandle::null()),
|
|
wchunk_(NULL),
|
|
wosc_(NULL),
|
|
mixerFrequency_(0), // FIXME: doc says soundserver's freq
|
|
channelIndex_(0),
|
|
speed_(1.0f),
|
|
pos_(0),
|
|
finished_(false),
|
|
paused_(false)
|
|
{}
|
|
|
|
~DataHandlePlay_impl()
|
|
{
|
|
handle(DataHandle::null());
|
|
}
|
|
|
|
DataHandle handle()
|
|
{
|
|
return dhandle_;
|
|
}
|
|
|
|
void handle(DataHandle handle)
|
|
{
|
|
deleteWaveChunk();
|
|
|
|
if(!gslDHandle_.isNull() && !errno_)
|
|
gslDHandle_.close();
|
|
|
|
dhandle_ = handle;
|
|
|
|
if(handle.isNull())
|
|
gslDHandle_ = GSL::DataHandle::null();
|
|
else
|
|
{
|
|
gslDHandle_ = getDHandle(dhandle_);
|
|
|
|
if(gslDHandle_.isNull())
|
|
{
|
|
arts_debug("ERROR: could not get internal GSL::DataHandle!");
|
|
finished(true); // FIXME: can this happen?
|
|
}
|
|
else
|
|
{
|
|
errno_ = gslDHandle_.open();
|
|
if(errno_)
|
|
arts_debug("DataHandlePlay got error from GSL::DataHandle.open(): '%s'",
|
|
strerror(errno_));
|
|
}
|
|
}
|
|
}
|
|
|
|
void streamInit()
|
|
{
|
|
if(!gslDHandle_.isNull() && !wosc_)
|
|
{
|
|
if(!wchunk_)
|
|
createWaveChunk();
|
|
configureWaveOsc();
|
|
}
|
|
}
|
|
|
|
float mixerFrequency() { return wosc_ ? wosc_->mix_freq : 0; }
|
|
void mixerFrequency(float f)
|
|
{
|
|
if(wchunk_)
|
|
arts_warning("DataHandlePlay: cannot change mixerFrequency after start of sound processing!");
|
|
|
|
if(mixerFrequency() != f)
|
|
{
|
|
mixerFrequency_ = f;
|
|
mixerFrequency_changed(f);
|
|
}
|
|
}
|
|
|
|
long channelIndex() { return channelIndex_; }
|
|
void channelIndex(long ci)
|
|
{
|
|
if(channelIndex_ != ci)
|
|
{
|
|
channelIndex_ = ci;
|
|
if(wosc_)
|
|
{
|
|
GslWaveOscConfig config = wosc_->config;
|
|
config.channel = ci;
|
|
gsl_wave_osc_config(wosc_, &config);
|
|
}
|
|
channelIndex_changed(ci);
|
|
}
|
|
}
|
|
|
|
float speed() { return speed_; }
|
|
void speed(float s)
|
|
{
|
|
if(speed_ != s)
|
|
{
|
|
speed_ = s;
|
|
if(wosc_)
|
|
{
|
|
GslWaveOscConfig config = wosc_->config;
|
|
config.cfreq = OSCILLATOR_FREQUENCY * speed();
|
|
gsl_wave_osc_config(wosc_, &config);
|
|
}
|
|
speed_changed(s);
|
|
}
|
|
}
|
|
|
|
long pos() { return wosc_ ? wosc_->block.offset : 0; }
|
|
void pos(long p)
|
|
{
|
|
if(pos() != p)
|
|
{
|
|
GslWaveOscConfig config = wosc_->config;
|
|
config.start_offset = p;
|
|
gsl_wave_osc_config(wosc_, &config);
|
|
pos_changed(p);
|
|
}
|
|
}
|
|
|
|
bool finished() { return finished_; }
|
|
void finished(bool f)
|
|
{
|
|
if(finished_ != f)
|
|
{
|
|
finished_ = f;
|
|
finished_changed(f);
|
|
}
|
|
}
|
|
|
|
bool paused() { return paused_; }
|
|
void paused(bool p)
|
|
{
|
|
paused_ = p;
|
|
}
|
|
|
|
void deleteWaveChunk()
|
|
{
|
|
deleteWaveOsc();
|
|
|
|
if(wchunk_)
|
|
{
|
|
arts_debug("DataHandlePlay_impl: close()ing gsl_wave_chunk");
|
|
gsl_wave_chunk_close(wchunk_);
|
|
gsl_wave_chunk_unref(wchunk_);
|
|
wchunk_ = NULL;
|
|
}
|
|
}
|
|
|
|
void createWaveChunk()
|
|
{
|
|
deleteWaveChunk();
|
|
|
|
if(!gslDHandle_.isNull() && gslDHandle_.isOpen())
|
|
{
|
|
GslDataCache *dcache = gslDHandle_.createGslDataCache();
|
|
if(!dcache)
|
|
{
|
|
arts_debug("FATAL: creating data cache failed!");
|
|
finished(true); // FIXME: can this happen?
|
|
}
|
|
else
|
|
{
|
|
wchunk_ =
|
|
gsl_wave_chunk_new(dcache,
|
|
OSCILLATOR_FREQUENCY,
|
|
mixerFrequency_,
|
|
GSL_WAVE_LOOP_NONE,
|
|
0, 0, 0);
|
|
arts_debug("DataHandlePlay_impl: open()ing gsl_wave_chunk");
|
|
wchunkError_ = gsl_wave_chunk_open(wchunk_);
|
|
gsl_data_cache_unref(dcache);
|
|
}
|
|
}
|
|
}
|
|
|
|
void deleteWaveOsc()
|
|
{
|
|
if(wosc_)
|
|
{
|
|
gsl_wave_osc_shutdown(wosc_);
|
|
delete wosc_;
|
|
wosc_ = NULL;
|
|
}
|
|
}
|
|
|
|
void configureWaveOsc()
|
|
{
|
|
if(wchunk_)
|
|
{
|
|
GslWaveOscConfig config;
|
|
memset(&config, 0, sizeof (GslWaveOscConfig));
|
|
config.start_offset = 0;
|
|
config.play_dir = 1;
|
|
config.wchunk_data = (gpointer)wchunk_;
|
|
config.wchunk_from_freq = &const_wchunk_from_freq;
|
|
config.channel = channelIndex();
|
|
config.cfreq = OSCILLATOR_FREQUENCY * speed();
|
|
|
|
if(!wosc_)
|
|
{
|
|
wosc_ = new GslWaveOscData();
|
|
gsl_wave_osc_init(wosc_);
|
|
}
|
|
|
|
gsl_wave_osc_config(wosc_, &config);
|
|
}
|
|
}
|
|
|
|
void calculateBlock(unsigned long samples)
|
|
{
|
|
if(!paused_ && wchunk_)
|
|
{
|
|
if (!gsl_wave_osc_process(wosc_, samples, 0, 0, 0, outvalue))
|
|
arts_debug("gsl_wave_osc_process failed.");
|
|
finished(wosc_->done);
|
|
}
|
|
else
|
|
for(unsigned long i = 0; i < samples; i++)
|
|
outvalue[i] = 0.f;
|
|
/*
|
|
unsigned long haveSamples = 0;
|
|
|
|
while(haveSamples != samples)
|
|
{
|
|
long n = min(gslDHandle_.valueCount() - pos_, samples - haveSamples);
|
|
long got = gslDHandle_.isNull() ?
|
|
0 : gslDHandle_.read(pos_, n, &outvalue[haveSamples]);
|
|
if(got <= 0)
|
|
{
|
|
for(unsigned long i = haveSamples; i < samples; i++)
|
|
outvalue[i] = 0.f;
|
|
break;
|
|
}
|
|
pos_ += got;
|
|
haveSamples += got;
|
|
}
|
|
finished(pos_ >= gslDHandle_.valueCount());
|
|
*/
|
|
}
|
|
|
|
DataHandlePlay clone()
|
|
{
|
|
arts_debug("DataHandlePlay_impl: clone()ing myself");
|
|
DataHandlePlay_impl *result = new DataHandlePlay_impl();
|
|
|
|
result->dhandle_ = dhandle_;
|
|
result->gslDHandle_ = gslDHandle_;
|
|
result->errno_ = errno_;
|
|
|
|
createWaveChunk();
|
|
if(wchunk_)
|
|
{
|
|
result->wchunk_ = wchunk_;
|
|
gsl_wave_chunk_ref(wchunk_);
|
|
result->wchunkError_ = gsl_wave_chunk_open(wchunk_);
|
|
}
|
|
else
|
|
result->wchunkError_ = wchunkError_;
|
|
|
|
result->mixerFrequency_ = mixerFrequency_;
|
|
result->channelIndex_ = channelIndex_;
|
|
result->speed_ = speed_;
|
|
result->pos_ = pos_;
|
|
result->finished_ = finished_;
|
|
result->paused_ = paused_;
|
|
|
|
return DataHandlePlay::_from_base(result);
|
|
}
|
|
};
|
|
|
|
class WaveDataHandle_impl : virtual public DataHandle_impl,
|
|
virtual public WaveDataHandle_skel
|
|
{
|
|
GSL::WaveDataHandle wdhandle_;
|
|
|
|
public:
|
|
WaveDataHandle_impl() : wdhandle_(GSL::WaveDataHandle::null()) {}
|
|
|
|
float mixerFrequency()
|
|
{
|
|
return wdhandle_.mixerFrequency();
|
|
}
|
|
|
|
float oscillatorFrequency()
|
|
{
|
|
return wdhandle_.oscillatorFrequency();
|
|
}
|
|
|
|
bool load(const string& filename)
|
|
{
|
|
return load(filename, 0, 0);
|
|
}
|
|
|
|
bool load(const string& filename,
|
|
long waveIndex,
|
|
long chunkIndex)
|
|
{
|
|
wdhandle_ = GSL::WaveDataHandle(filename, waveIndex, chunkIndex);
|
|
if(dhandle_.isOpen())
|
|
dhandle_.close();
|
|
dhandle_ = wdhandle_;
|
|
errno_ = dhandle_.isNull() ? 0 : dhandle_.open();
|
|
return isLoaded();
|
|
}
|
|
|
|
bool isLoaded()
|
|
{
|
|
if(wdhandle_.isNull())
|
|
return false;
|
|
|
|
return !wdhandle_.error();
|
|
}
|
|
|
|
DataHandlePlay createPlayer()
|
|
{
|
|
DataHandlePlay_impl *result= new DataHandlePlay_impl();
|
|
|
|
result->mixerFrequency(mixerFrequency());
|
|
result->handle(WaveDataHandle::_from_base(this->_copy()));
|
|
|
|
return DataHandlePlay::_from_base(result);
|
|
}
|
|
};
|
|
|
|
REGISTER_IMPLEMENTATION(DataHandlePlay_impl);
|
|
REGISTER_IMPLEMENTATION(DataHandle_impl);
|
|
REGISTER_IMPLEMENTATION(CroppedDataHandle_impl);
|
|
REGISTER_IMPLEMENTATION(CutDataHandle_impl);
|
|
REGISTER_IMPLEMENTATION(ReversedDataHandle_impl);
|
|
REGISTER_IMPLEMENTATION(WaveDataHandle_impl);
|
|
|
|
}
|