--- linuxsampler/trunk/src/engines/sfz/sfz.h 2010/01/30 10:30:02 2055 +++ linuxsampler/trunk/src/engines/sfz/sfz.h 2011/08/19 15:51:18 2248 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2008 Anders Dahnielson * - * Copyright (C) 2009 - 2010 Anders Dahnielson and Grigor Iliev * + * Copyright (C) 2009 - 2011 Anders Dahnielson and Grigor Iliev * * * * 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 * @@ -47,17 +47,42 @@ class Group; class Instrument; class File; + class LookupTable; + class SampleManager; class Sample : public LinuxSampler::SampleFileBase { + private: + int End; + long TotalFrames; + public: - Sample(String File, bool DontClose = false) : LinuxSampler::SampleFileBase(File, DontClose) { } + Sample(String File, bool DontClose = false, uint offset = 0, int end = -2 /* -2 means unspecified */) + : LinuxSampler::SampleFileBase(File, DontClose) + { + Offset = offset; + End = end; + + long tfc = LinuxSampler::SampleFileBase::GetTotalFrameCount(); + if (Offset >= tfc) { + std::cerr << "Offset for file '" << this->GetFile() << "' too long (" << Offset << ")" << std::endl; + Offset = 0; + } + + if (End == -2 || End > tfc) TotalFrames = tfc; + else if (End == -1 || End < Offset) TotalFrames = 0; + else TotalFrames = End; + } virtual ~Sample() { } + + virtual long GetTotalFrameCount() { return TotalFrames; } + + friend class SampleManager; }; // Enumerations enum sw_vel_t { VEL_CURRENT, VEL_PREVIOUS }; enum off_mode_t { OFF_FAST, OFF_NORMAL }; - enum loop_mode_t { NO_LOOP, ONE_SHOT, LOOP_CONTINUOUS, LOOP_SUSTAIN }; + enum loop_mode_t { NO_LOOP, ONE_SHOT, LOOP_CONTINUOUS, LOOP_SUSTAIN, LOOP_UNSET }; enum curve_t { GAIN, POWER }; enum filter_t { LPF_1P, HPF_1P, BPF_1P, BRF_1P, APF_1P, LPF_2P, HPF_2P, BPF_2P, BRF_2P, PKF_2P, @@ -69,7 +94,7 @@ class SampleManager : public LinuxSampler::SampleManager { public: - Sample* FindSample(std::string samplePath); + Sample* FindSample(std::string samplePath, uint offset, int end); protected: virtual void OnSampleInUse(Sample* pSample) { @@ -80,6 +105,31 @@ pSample->Close(); } }; + + class CC { + public: + uint8_t Controller; ///< MIDI controller number. + short int Curve; + float Influence; ///< Controller Value. + float Smooth; ///< The speed of parameter change in milliseconds + + CC(uint8_t Controller = 0, float Influence = 0.0f, short int Curve = -1, float Smooth = 0) { + this->Controller = Controller; + this->Influence = Influence; + this->Curve = Curve; + this->Smooth = Smooth; + } + + CC(const CC& cc) { Copy(cc); } + void operator=(const CC& cc) { Copy(cc); } + + void Copy(const CC& cc) { + Controller = cc.Controller; + Influence = cc.Influence; + Curve = cc.Curve; + Smooth = cc.Smooth; + } + }; ///////////////////////////////////////////////////////////// // class Exception @@ -222,7 +272,13 @@ float level; float shape; float curve; + LinuxSampler::ArrayList time_oncc; + LinuxSampler::ArrayList level_oncc; + EGNode(); + EGNode(const EGNode& egNode) { Copy(egNode); } + void operator=(const EGNode& egNode) { Copy(egNode); } + void Copy(const EGNode& egNode); }; class EG @@ -233,8 +289,102 @@ int loop; int loop_count; float amplitude; + float volume; float cutoff; + int pitch; // -9600 to 9600 cents + float resonance; // 0 to 40 dB + float pan; // -100 to 100 % + int pan_curve; + + LinuxSampler::ArrayList amplitude_oncc; + LinuxSampler::ArrayList volume_oncc; + LinuxSampler::ArrayList cutoff_oncc; // -9600 to 9600 cents + LinuxSampler::ArrayList pitch_oncc; + LinuxSampler::ArrayList resonance_oncc; // 0 to 40 dB + LinuxSampler::ArrayList pan_oncc; // -100 to 100 % + LinuxSampler::ArrayList pan_curvecc; // used only as temporary buffer during the parsing - values are then moved to pan_oncc + EG(); + EG(const EG& eg) { Copy(eg); } + void operator=(const EG& eg) { Copy(eg); } + void Copy(const EG& eg); + }; + + class LFO + { + public: + float delay; // 0 to 100 seconds + float freq; // 0 to 20 Hz + float fade; // 0 to 100 seconds + float phase; // 0 to 360 degrees + uint wave; // 0 to 4294967296 + float volume; // -144 to 6 dB + int pitch; // -9600 to 9600 cents + int cutoff; // -9600 to 9600 cents + float resonance; // 0 to 40 dB + float pan; // -100 to 100 % + + LinuxSampler::ArrayList delay_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList freq_oncc; // 0 to 20 Hz + LinuxSampler::ArrayList freq_smoothcc; // 0 to ? milliseconds + LinuxSampler::ArrayList fade_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList phase_oncc; // 0 to 360 degrees + LinuxSampler::ArrayList volume_oncc; // -144 to 6 dB + LinuxSampler::ArrayList volume_smoothcc; // 0 to ? milliseconds + LinuxSampler::ArrayList pitch_oncc; + LinuxSampler::ArrayList pitch_smoothcc; // 0 to ? milliseconds + LinuxSampler::ArrayList pan_oncc; // -100 to 100 % + LinuxSampler::ArrayList pan_smoothcc; // 0 to ? milliseconds + LinuxSampler::ArrayList cutoff_oncc; // -9600 to 9600 cents + LinuxSampler::ArrayList cutoff_smoothcc; // 0 to ? milliseconds + LinuxSampler::ArrayList resonance_oncc; // 0 to 40 dB + LinuxSampler::ArrayList resonance_smoothcc; // 0 to ? milliseconds + + LFO(); + LFO(const LFO& lfo) { Copy(lfo); } + void operator=(const LFO& lfo) { Copy(lfo); } + void Copy(const LFO& lfo); + }; + + // Fixed size array with copy-on-write semantics + template + class Array + { + private: + struct Rep { + int refcount; + T a[128]; + + Rep() : refcount(1) { } + static void release(Rep* rep) { + if (!--rep->refcount) delete rep; + } + } *ptr; + public: + Array() : ptr(0) { } + ~Array() { Rep::release(ptr); } + + Array& operator=(const Array& array) { + if (this != &array) { + ptr = array.ptr; + if (ptr) ptr->refcount++; + } + return *this; + } + + const T& operator[](int i) const { return ptr->a[i]; } + + void set(int i, const T& v) { + if (!ptr) { + ptr = new Rep; + } else if (ptr->refcount > 1 && ptr->a[i] != v) { + Rep* newptr = new Rep(*ptr); + newptr->refcount = 1; + Rep::release(ptr); + ptr = newptr; + } + ptr->a[i] = v; + } }; ///////////////////////////////////////////////////////////// @@ -254,9 +404,9 @@ int lochan; int hichan; int lokey; int hikey; int lovel; int hivel; - std::vector locc; std::vector hicc; + Array locc; Array hicc; int lobend; int hibend; - int lobpm; int hibpm; + float lobpm; float hibpm; int lochanaft; int hichanaft; int lopolyaft; int hipolyaft; int loprog; int hiprog; @@ -266,8 +416,8 @@ int seq_length; int seq_position; - std::vector start_locc; std::vector start_hicc; - std::vector stop_locc; std::vector stop_hicc; + Array start_locc; Array start_hicc; + Array stop_locc; Array stop_hicc; int sw_lokey; int sw_hikey; int sw_last; @@ -279,19 +429,19 @@ trigger_t trigger; uint group; - optional off_by; + uint off_by; off_mode_t off_mode; - std::vector on_locc; std::vector on_hicc; + Array on_locc; Array on_hicc; // sample player optional count; - optional delay; optional delay_random; std::vector > delay_oncc; + optional delay; optional delay_random; Array > delay_oncc; optional delay_beats; optional stop_beats; - optional delay_samples; std::vector > delay_samples_oncc; + optional delay_samples; Array > delay_samples_oncc; optional end; optional loop_crossfade; - optional offset; optional offset_random; std::vector > offset_oncc; + optional offset; optional offset_random; Array > offset_oncc; loop_mode_t loop_mode; optional loop_start; optional loop_end; optional sync_beats; @@ -302,17 +452,17 @@ float pan; float width; float position; - float amp_keytrack; int amp_keycenter; float amp_veltrack; std::vector amp_velcurve_; float amp_random; + float amp_keytrack; int amp_keycenter; float amp_veltrack; Array amp_velcurve; float amp_random; float rt_decay; - std::vector gain_oncc; + Array gain_oncc; int xfin_lokey; int xfin_hikey; int xfout_lokey; int xfout_hikey; curve_t xf_keycurve; int xfin_lovel; int xfin_hivel; int xfout_lovel; int xfout_hivel; curve_t xf_velcurve; - std::vector xfin_locc; std::vector xfin_hicc; - std::vector xfout_locc; std::vector xfout_hicc; + Array xfin_locc; Array xfin_hicc; + Array xfout_locc; Array xfout_hicc; curve_t xf_cccurve; // pitch @@ -324,17 +474,19 @@ // filter filter_t fil_type; filter_t fil2_type; optional cutoff; optional cutoff2; - std::vector cutoff_oncc; std::vector cutoff2_oncc; - std::vector cutoff_smoothcc; std::vector cutoff2_smoothcc; - std::vector cutoff_stepcc; std::vector cutoff2_stepcc; - std::vector cutoff_curvecc; std::vector cutoff2_curvecc; + Array cutoff_oncc; Array cutoff2_oncc; + int cutoff_cc; // TODO: this is just a temporary fix to avoid + // looping through the cutoff_oncc array + Array cutoff_smoothcc; Array cutoff2_smoothcc; + Array cutoff_stepcc; Array cutoff2_stepcc; + Array cutoff_curvecc; Array cutoff2_curvecc; int cutoff_chanaft; int cutoff2_chanaft; int cutoff_polyaft; int cutoff2_polyaft; float resonance; float resonance2; - std::vector resonance_oncc; std::vector resonance2_oncc; - std::vector resonance_smoothcc; std::vector resonance2_smoothcc; - std::vector resonance_stepcc; std::vector resonance2_stepcc; - std::vector resonance_curvecc; std::vector resonance2_curvecc; + Array resonance_oncc; Array resonance2_oncc; + Array resonance_smoothcc; Array resonance2_smoothcc; + Array resonance_stepcc; Array resonance2_stepcc; + Array resonance_curvecc; Array resonance2_curvecc; int fil_keytrack; int fil2_keytrack; int fil_keycenter; int fil2_keycenter; int fil_veltrack; int fil2_veltrack; @@ -342,21 +494,81 @@ // per voice equalizer float eq1_freq; float eq2_freq; float eq3_freq; - std::vector eq1_freq_oncc; std::vector eq2_freq_oncc; std::vector eq3_freq_oncc; + Array eq1_freq_oncc; Array eq2_freq_oncc; Array eq3_freq_oncc; float eq1_vel2freq; float eq2_vel2freq; float eq3_vel2freq; float eq1_bw; float eq2_bw; float eq3_bw; - std::vector eq1_bw_oncc; std::vector eq2_bw_oncc; std::vector eq3_bw_oncc; + Array eq1_bw_oncc; Array eq2_bw_oncc; Array eq3_bw_oncc; float eq1_gain; float eq2_gain; float eq3_gain; - std::vector eq1_gain_oncc; std::vector eq2_gain_oncc; std::vector eq3_gain_oncc; + Array eq1_gain_oncc; Array eq2_gain_oncc; Array eq3_gain_oncc; float eq1_vel2gain; float eq2_vel2gain; float eq3_vel2gain; //Deprecated (from version 1) float ampeg_delay, ampeg_start, ampeg_attack, ampeg_hold, ampeg_decay, ampeg_sustain, ampeg_release; + float ampeg_vel2delay, ampeg_vel2attack, ampeg_vel2hold, ampeg_vel2decay, ampeg_vel2sustain, ampeg_vel2release; + LinuxSampler::ArrayList ampeg_delaycc, ampeg_startcc, ampeg_attackcc, ampeg_holdcc; + LinuxSampler::ArrayList ampeg_decaycc, ampeg_sustaincc, ampeg_releasecc; float fileg_delay, fileg_start, fileg_attack, fileg_hold, fileg_decay, fileg_sustain, fileg_release; + float fileg_vel2delay, fileg_vel2attack, fileg_vel2hold, fileg_vel2decay, fileg_vel2sustain, fileg_vel2release; float pitcheg_delay, pitcheg_start, pitcheg_attack, pitcheg_hold, pitcheg_decay, pitcheg_sustain, pitcheg_release; + float pitcheg_vel2delay, pitcheg_vel2attack, pitcheg_vel2hold, pitcheg_vel2decay, pitcheg_vel2sustain, pitcheg_vel2release; + int fileg_depth, pitcheg_depth; + float amplfo_delay, amplfo_fade, amplfo_freq, amplfo_depth; + float fillfo_delay, fillfo_fade, fillfo_freq, fillfo_depth; + float pitchlfo_delay, pitchlfo_fade, pitchlfo_freq; + int pitchlfo_depth; + Array pitchlfo_depthcc; + + LinuxSampler::ArrayList pitchlfo_delay_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList pitchlfo_fade_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList pitchlfo_freqcc; // 0 to 20 Hz + LinuxSampler::ArrayList fillfo_delay_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList fillfo_fade_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList fillfo_depthcc; // -1200 to 1200 cents + LinuxSampler::ArrayList fillfo_freqcc; // 0 to 20 Hz + LinuxSampler::ArrayList amplfo_delay_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList amplfo_fade_oncc; // 0 to 100 seconds + LinuxSampler::ArrayList amplfo_depthcc; // -10 to 10 dB + LinuxSampler::ArrayList amplfo_freqcc; // 0 to 20 Hz // envelope generators LinuxSampler::ArrayList eg; + + // low frequency oscillators + LinuxSampler::ArrayList lfos; + + LinuxSampler::ArrayList volume_oncc; + LinuxSampler::ArrayList volume_curvecc; // used only as temporary buffer during the parsing - values are then moved to volume_oncc + LinuxSampler::ArrayList volume_smoothcc; // used only as temporary buffer during the parsing - values are then moved to volume_oncc + + LinuxSampler::ArrayList pan_oncc; // -100 to 100 % + LinuxSampler::ArrayList pan_curvecc; // used only as temporary buffer during the parsing - values are then moved to pan_oncc + LinuxSampler::ArrayList pan_smoothcc; // used only as temporary buffer during the parsing - values are then moved to pan_oncc + }; + + class Query { + public: + uint8_t chan; // MIDI channel + uint8_t key; // MIDI note + uint8_t vel; // MIDI velocity + int bend; // MIDI pitch bend + uint8_t bpm; // host BPM + uint8_t chanaft; // MIDI channel pressure + uint8_t polyaft; // MIDI polyphonic aftertouch + uint8_t prog; // MIDI program change + float rand; // generated random number + trigger_t trig; // how it was triggered + uint8_t* cc; // all 128 CC values + float timer; // time since previous region in the group was triggered + bool* sw; // state of region key switches, 128 possible values + uint8_t last_sw_key; // last key pressed in the key switch range + uint8_t prev_sw_key; // previous note value + + void search(const Instrument* pInstrument); + void search(const Instrument* pInstrument, int triggercc); + Region* next(); + private: + LinuxSampler::ArrayList* pRegionList; + int regionIndex; }; ///////////////////////////////////////////////////////////// @@ -383,32 +595,34 @@ uint GetLoopEnd(); uint GetLoopCount(); - /// Return true if region is triggered by key - bool OnKey(uint8_t chan, uint8_t key, uint8_t vel, - int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, - uint8_t prog, float rand, trigger_t trig, uint8_t* cc, - float timer, uint8_t seq, bool* sw, uint8_t last_sw_key, uint8_t prev_sw_key); - - /// Return true if region is triggered by control change - bool OnControl(uint8_t chan, uint8_t cont, uint8_t val, - int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, - uint8_t prog, float rand, trigger_t trig, uint8_t* cc, - float timer, uint8_t seq, bool* sw, uint8_t last_sw_key, uint8_t prev_sw_key); + /// Return true if region is triggered by key. Region is + /// assumed to come from a search in the lookup table. + bool OnKey(const Query& q); /// Return an articulation for the current state - Articulation* GetArticulation(int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, uint8_t* cc); + Articulation* GetArticulation(int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, uint8_t* cc); // unique region id int id; private: Instrument* pInstrument; + int seq_counter; + }; + + class Curve { + public: + float v[128]; + Curve() { for (int i = 0; i < 128; i++) v[i] = 0; } + Curve(const Curve& curve) { Copy(curve); } + void operator=(const Curve& curve) { Copy(curve); } + void Copy(const Curve& curve) { for (int i = 0; i < 128; i++) v[i] = curve.v[i]; } }; ///////////////////////////////////////////////////////////// // class Instrument - /// Provides all neccessary information for the synthesis of an Instrument + /// Provides all necessary information for the synthesis of an Instrument class Instrument : public SampleManager { public: @@ -418,30 +632,24 @@ std::string GetName() { return name; } SampleManager* GetSampleManager() { return pSampleManager; } - /** - * @returns All regions that are triggered by key - */ - std::vector GetRegionsOnKey ( - uint8_t chan, uint8_t key, uint8_t vel, - int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, - uint8_t prog, float rand, trigger_t trig, uint8_t* cc, - float timer, uint8_t seq, bool* sw, uint8_t last_sw_key, uint8_t prev_sw_key - ); - bool DestroyRegion(Region* pRegion); bool HasKeyBinding(uint8_t key); bool HasKeySwitchBinding(uint8_t key); /// List of Regions belonging to this Instrument std::vector regions; + ::LinuxSampler::ArrayList curves; - friend class sfz::File; + friend class File; + friend class Query; private: std::string name; std::vector KeyBindings; std::vector KeySwitchBindings; SampleManager* pSampleManager; + LookupTable* pLookupTable; + LookupTable* pLookupTableCC[128]; }; ///////////////////////////////////////////////////////////// @@ -483,20 +691,27 @@ private: void push_header(std::string token); void push_opcode(std::string token); - int parseInt(std::string value); - float parseFloat(std::string value); + int parseKey(const std::string& value); EG& eg(int x); EGNode& egnode(int x, int y); + LFO& lfo(int x); + void copyCurves(LinuxSampler::ArrayList& curves, LinuxSampler::ArrayList& dest); + void copySmoothValues(LinuxSampler::ArrayList& smooths, LinuxSampler::ArrayList& dest); + + int ToInt(const std::string& s) throw(LinuxSampler::Exception); + float ToFloat(const std::string& s) throw(LinuxSampler::Exception); + int currentLine; std::string currentDir; /// Pointer to the Instrument belonging to this file Instrument* _instrument; // state variables - enum section_t { UNKNOWN, GROUP, REGION, CONTROL }; + enum section_t { UNKNOWN, GROUP, REGION, CONTROL, CURVE }; section_t _current_section; Region* _current_region; Group* _current_group; + Curve* _current_curve; Definition* pCurDef; // control header directives