mirror of https://github.com/AxioDL/amuse.git
Working SongGroup player, DLS envelopes
This commit is contained in:
parent
44bb7a155b
commit
e9213ab179
|
@ -143,10 +143,7 @@ public:
|
||||||
m_tracking = true;
|
m_tracking = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey)
|
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||||
{
|
|
||||||
m_tracking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mouseMove(const boo::SWindowCoord& coord);
|
void mouseMove(const boo::SWindowCoord& coord);
|
||||||
|
|
||||||
|
@ -188,15 +185,20 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
bool m_wantsNext = false;
|
bool m_wantsNext = false;
|
||||||
bool m_wantsPrev = false;
|
bool m_wantsPrev = false;
|
||||||
|
int m_panicCount = 0;
|
||||||
|
|
||||||
void UpdateSongDisplay()
|
void UpdateSongDisplay()
|
||||||
{
|
{
|
||||||
size_t voxCount = 0;
|
size_t voxCount = 0;
|
||||||
|
int program = 0;
|
||||||
if (m_seq)
|
if (m_seq)
|
||||||
|
{
|
||||||
voxCount = m_seq->getVoiceCount();
|
voxCount = m_seq->getVoiceCount();
|
||||||
|
program = m_seq->getChanProgram(m_chanId);
|
||||||
|
}
|
||||||
printf("\r "
|
printf("\r "
|
||||||
"\r %" PRISize " Setup %d, Chan %d, Octave: %d, Vel: %d, VOL: %d%%\r", voxCount,
|
"\r %" PRISize " Setup %d, Chan %d, Prog %d, Octave: %d, Vel: %d, VOL: %d%%\r", voxCount,
|
||||||
m_setupId, m_chanId, m_octave, m_velocity, int(std::rint(m_volume * 100)));
|
m_setupId, m_chanId, program, m_octave, m_velocity, int(std::rint(m_volume * 100)));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,9 +221,9 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
||||||
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
||||||
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||||
"<left/right>: cycle MIDI setup / channel, <up/down>: volume\n"
|
"<left/right>: cycle MIDI setup / channel, <up/down>: volume, <space>: PANIC\n"
|
||||||
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
||||||
"<Z/X>: octave, <C/V>: velocity, <Q>: quit\n");
|
"<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit\n");
|
||||||
|
|
||||||
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
|
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
|
||||||
(index.m_midiSetups.cbegin(), index.m_midiSetups.cend());
|
(index.m_midiSetups.cbegin(), index.m_midiSetups.cend());
|
||||||
|
@ -257,6 +259,18 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_seq && m_panicCount)
|
||||||
|
{
|
||||||
|
if (m_panicCount > 1)
|
||||||
|
{
|
||||||
|
m_seq->allOff(true);
|
||||||
|
m_panicCount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_seq->allOff(false);
|
||||||
|
m_updateDisp = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_updateDisp)
|
if (m_updateDisp)
|
||||||
{
|
{
|
||||||
m_updateDisp = false;
|
m_updateDisp = false;
|
||||||
|
@ -355,6 +369,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
|
|
||||||
void charKeyDownRepeat(unsigned long charCode)
|
void charKeyDownRepeat(unsigned long charCode)
|
||||||
{
|
{
|
||||||
|
charCode = tolower(charCode);
|
||||||
|
|
||||||
if (m_seq && m_chanId != -1)
|
if (m_seq && m_chanId != -1)
|
||||||
{
|
{
|
||||||
switch (charCode)
|
switch (charCode)
|
||||||
|
@ -375,6 +391,22 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
|
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
|
||||||
m_updateDisp = true;
|
m_updateDisp = true;
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
m_chanId = amuse::clamp(0, m_chanId - 1, 15);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
m_chanId = amuse::clamp(0, m_chanId + 1, 15);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
m_seq->prevChanProgram(m_chanId);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
m_seq->nextChanProgram(m_chanId);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,8 +435,14 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
}
|
}
|
||||||
else if (m_seq && m_chanId != -1)
|
else if (m_seq && m_chanId != -1)
|
||||||
{
|
{
|
||||||
|
bool setPanic = false;
|
||||||
|
|
||||||
switch (charCode)
|
switch (charCode)
|
||||||
{
|
{
|
||||||
|
case ' ':
|
||||||
|
++m_panicCount;
|
||||||
|
setPanic = true;
|
||||||
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
m_octave = amuse::clamp(-1, m_octave - 1, 8);
|
m_octave = amuse::clamp(-1, m_octave - 1, 8);
|
||||||
m_updateDisp = true;
|
m_updateDisp = true;
|
||||||
|
@ -421,6 +459,22 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
|
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
|
||||||
m_updateDisp = true;
|
m_updateDisp = true;
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
m_chanId = amuse::clamp(0, m_chanId - 1, 15);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
m_chanId = amuse::clamp(0, m_chanId + 1, 15);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
m_seq->prevChanProgram(m_chanId);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
m_seq->nextChanProgram(m_chanId);
|
||||||
|
m_updateDisp = true;
|
||||||
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
m_seq->setCtrlValue(m_chanId, 64, 127);
|
m_seq->setCtrlValue(m_chanId, 64, 127);
|
||||||
break;
|
break;
|
||||||
|
@ -478,6 +532,9 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!setPanic)
|
||||||
|
m_panicCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,6 +770,14 @@ void EventCallback::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventCallback::mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey)
|
||||||
|
{
|
||||||
|
m_tracking = false;
|
||||||
|
m_app.m_pitchBend = 0.f;
|
||||||
|
if (m_app.m_seq && m_app.m_chanId != -1)
|
||||||
|
m_app.m_seq->setPitchWheel(m_app.m_chanId, m_app.m_pitchBend);
|
||||||
|
}
|
||||||
|
|
||||||
void EventCallback::mouseMove(const boo::SWindowCoord& coord)
|
void EventCallback::mouseMove(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
if (m_tracking)
|
if (m_tracking)
|
||||||
|
|
|
@ -5,10 +5,19 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
|
#include "Common.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** Converts time-cents representation to seconds */
|
||||||
|
static inline double TimeCentsToSeconds(int32_t tc)
|
||||||
|
{
|
||||||
|
if (tc == 0x80000000)
|
||||||
|
return 0.0;
|
||||||
|
return std::exp2(tc / (1200.0 * 65536.0));
|
||||||
|
}
|
||||||
|
|
||||||
/** Defines phase-based volume curve for macro volume control */
|
/** Defines phase-based volume curve for macro volume control */
|
||||||
struct ADSR
|
struct ADSR
|
||||||
{
|
{
|
||||||
|
@ -20,6 +29,39 @@ struct ADSR
|
||||||
uint8_t sustainCoarse; /* multiply by 6.25 for percentage */
|
uint8_t sustainCoarse; /* multiply by 6.25 for percentage */
|
||||||
uint8_t releaseFine; /* 0-255ms */
|
uint8_t releaseFine; /* 0-255ms */
|
||||||
uint8_t releaseCoarse; /* 0-65280ms */
|
uint8_t releaseCoarse; /* 0-65280ms */
|
||||||
|
|
||||||
|
double getAttack() const {return (attackCoarse * 255 + attackFine) / 1000.0;}
|
||||||
|
double getDecay() const {return decayCoarse == 128 ? 0.0 : ((decayCoarse * 255 + decayFine) / 1000.0);}
|
||||||
|
double getSustain() const {return decayCoarse == 128 ? 1.0 : ((sustainCoarse * 6.25 + sustainFine * 0.0244) / 100.0);}
|
||||||
|
double getRelease() const {return (releaseCoarse * 255 + releaseFine) / 1000.0;}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Defines phase-based volume curve for macro volume control (modified DLS standard) */
|
||||||
|
struct ADSRDLS
|
||||||
|
{
|
||||||
|
uint32_t attack; /* 16.16 Time-cents */
|
||||||
|
uint32_t decay; /* 16.16 Time-cents */
|
||||||
|
uint16_t sustain; /* 0x1000 == 100% */
|
||||||
|
uint16_t release; /* milliseconds */
|
||||||
|
uint32_t velToAttack; /* 16.16, 1000.0 == 100%; attack = <attack> + (vel/128) * <velToAttack> */
|
||||||
|
uint32_t keyToDecay; /* 16.16, 1000.0 == 100%; decay = <decay> + (note/128) * <keyToDecay> */
|
||||||
|
|
||||||
|
double getAttack() const {return TimeCentsToSeconds(attack);}
|
||||||
|
double getDecay() const {return TimeCentsToSeconds(decay);}
|
||||||
|
double getSustain() const {return sustain / double(0x1000);}
|
||||||
|
double getRelease() const {return release / double(1000);}
|
||||||
|
double getVelToAttack(int8_t vel) const
|
||||||
|
{
|
||||||
|
if (velToAttack == 0x80000000)
|
||||||
|
return getAttack();
|
||||||
|
return getAttack() + vel * (velToAttack / 65536.0 / 1000.0) / 128.0;
|
||||||
|
}
|
||||||
|
double getKeyToDecay(int8_t note) const
|
||||||
|
{
|
||||||
|
if (keyToDecay == 0x80000000)
|
||||||
|
return getDecay();
|
||||||
|
return getDecay() + note * (keyToDecay / 65536.0 / 1000.0) / 128.0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Maps individual MIDI keys to sound-entity as indexed in table
|
/** Maps individual MIDI keys to sound-entity as indexed in table
|
||||||
|
@ -59,6 +101,8 @@ public:
|
||||||
const Keymap* keymap(ObjectId id) const;
|
const Keymap* keymap(ObjectId id) const;
|
||||||
const std::vector<const LayerMapping*>* layer(ObjectId id) const;
|
const std::vector<const LayerMapping*>* layer(ObjectId id) const;
|
||||||
const ADSR* tableAsAdsr(ObjectId id) const;
|
const ADSR* tableAsAdsr(ObjectId id) const;
|
||||||
|
const ADSRDLS* tableAsAdsrDLS(ObjectId id) const
|
||||||
|
{return reinterpret_cast<const ADSRDLS*>(tableAsAdsr(id));}
|
||||||
const Curve* tableAsCurves(ObjectId id) const
|
const Curve* tableAsCurves(ObjectId id) const
|
||||||
{return reinterpret_cast<const Curve*>(tableAsAdsr(id));}
|
{return reinterpret_cast<const Curve*>(tableAsAdsr(id));}
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,7 @@ class Engine
|
||||||
{
|
{
|
||||||
friend class Voice;
|
friend class Voice;
|
||||||
friend class Emitter;
|
friend class Emitter;
|
||||||
|
friend class Sequencer;
|
||||||
friend class Sequencer::ChannelState;
|
friend class Sequencer::ChannelState;
|
||||||
|
|
||||||
IBackendVoiceAllocator& m_backend;
|
IBackendVoiceAllocator& m_backend;
|
||||||
|
@ -32,7 +33,7 @@ class Engine
|
||||||
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
|
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
|
||||||
std::list<std::shared_ptr<Sequencer>> m_activeSequencers;
|
std::list<std::shared_ptr<Sequencer>> m_activeSequencers;
|
||||||
std::list<Submix> m_activeSubmixes;
|
std::list<Submix> m_activeSubmixes;
|
||||||
std::unordered_map<uint16_t, std::tuple<AudioGroup*, int, ObjectId>> m_sfxLookup;
|
std::unordered_map<uint16_t, std::tuple<AudioGroup*, int, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
|
||||||
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
||||||
int m_nextVid = 0;
|
int m_nextVid = 0;
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,15 @@ public:
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
State m_phase = State::Attack; /**< Current envelope state */
|
State m_phase = State::Attack; /**< Current envelope state */
|
||||||
const ADSR* m_curADSR = nullptr; /**< Current timing envelope */
|
double m_attackTime = 0.0; /**< Time of attack in seconds */
|
||||||
double m_sustainFactor; /**< Evaluated sustain percentage as a double */
|
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
||||||
|
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
||||||
|
double m_releaseTime = 0.0; /**< Time of release in seconds */
|
||||||
double m_releaseStartFactor; /**< Level at whenever release event occurs */
|
double m_releaseStartFactor; /**< Level at whenever release event occurs */
|
||||||
double m_curMs; /**< Current time of envelope stage */
|
double m_curTime; /**< Current time of envelope stage in seconds */
|
||||||
public:
|
public:
|
||||||
void reset(const ADSR* adsr);
|
void reset(const ADSR* adsr);
|
||||||
|
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
|
||||||
void keyOff();
|
void keyOff();
|
||||||
float nextSample(double sampleRate);
|
float nextSample(double sampleRate);
|
||||||
bool isComplete() const {return m_phase == State::Complete;}
|
bool isComplete() const {return m_phase == State::Complete;}
|
||||||
|
|
|
@ -40,6 +40,7 @@ class Sequencer : public Entity
|
||||||
struct ChannelState
|
struct ChannelState
|
||||||
{
|
{
|
||||||
Sequencer& m_parent;
|
Sequencer& m_parent;
|
||||||
|
uint8_t m_chanId;
|
||||||
const SongGroupIndex::MIDISetup& m_setup;
|
const SongGroupIndex::MIDISetup& m_setup;
|
||||||
const SongGroupIndex::PageEntry* m_page = nullptr;
|
const SongGroupIndex::PageEntry* m_page = nullptr;
|
||||||
Submix* m_submix = nullptr;
|
Submix* m_submix = nullptr;
|
||||||
|
@ -50,12 +51,17 @@ class Sequencer : public Entity
|
||||||
std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs;
|
std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs;
|
||||||
std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs;
|
std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs;
|
||||||
int8_t m_ctrlVals[128]; /**< MIDI controller values */
|
int8_t m_ctrlVals[128]; /**< MIDI controller values */
|
||||||
|
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
|
||||||
|
int8_t m_curProgram = 0; /**< MIDI program number */
|
||||||
|
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
size_t getVoiceCount() const;
|
size_t getVoiceCount() const;
|
||||||
std::shared_ptr<Voice> keyOn(uint8_t note, uint8_t velocity);
|
std::shared_ptr<Voice> keyOn(uint8_t note, uint8_t velocity);
|
||||||
void keyOff(uint8_t note, uint8_t velocity);
|
void keyOff(uint8_t note, uint8_t velocity);
|
||||||
void setCtrlValue(uint8_t ctrl, int8_t val);
|
void setCtrlValue(uint8_t ctrl, int8_t val);
|
||||||
|
bool programChange(int8_t prog);
|
||||||
|
void nextProgram();
|
||||||
|
void prevProgram();
|
||||||
void setPitchWheel(float pitchWheel);
|
void setPitchWheel(float pitchWheel);
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
void allOff();
|
void allOff();
|
||||||
|
@ -114,6 +120,18 @@ public:
|
||||||
/** Set total volume of sequencer */
|
/** Set total volume of sequencer */
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
|
|
||||||
|
/** Get current program number of channel */
|
||||||
|
int8_t getChanProgram(int8_t chanId) const;
|
||||||
|
|
||||||
|
/** Set current program number of channel */
|
||||||
|
bool setChanProgram(int8_t chanId, int8_t prog);
|
||||||
|
|
||||||
|
/** Advance to next program in channel */
|
||||||
|
void nextChanProgram(int8_t chanId);
|
||||||
|
|
||||||
|
/** Advance to prev program in channel */
|
||||||
|
void prevChanProgram(int8_t chanId);
|
||||||
|
|
||||||
/** Manually kill sequencer for deferred release from engine */
|
/** Manually kill sequencer for deferred release from engine */
|
||||||
void kill() {m_state = SequencerState::Dead;}
|
void kill() {m_state = SequencerState::Dead;}
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,6 +67,7 @@ class Voice : public Entity
|
||||||
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
||||||
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
||||||
|
|
||||||
|
float m_userVol = 1.f; /**< User volume of voice */
|
||||||
float m_curVol; /**< Current volume of voice */
|
float m_curVol; /**< Current volume of voice */
|
||||||
float m_curReverbVol; /**< Current reverb volume of voice */
|
float m_curReverbVol; /**< Current reverb volume of voice */
|
||||||
float m_curPan; /**< Current pan of voice */
|
float m_curPan; /**< Current pan of voice */
|
||||||
|
@ -124,7 +125,7 @@ class Voice : public Entity
|
||||||
void _doKeyOff();
|
void _doKeyOff();
|
||||||
void _macroKeyOff();
|
void _macroKeyOff();
|
||||||
void _macroSampleEnd();
|
void _macroSampleEnd();
|
||||||
bool _advanceSample(int16_t& samp);
|
bool _advanceSample(int16_t& samp, int32_t& curPitch);
|
||||||
void _setTotalPitch(int32_t cents);
|
void _setTotalPitch(int32_t cents);
|
||||||
bool _isRecursivelyDead();
|
bool _isRecursivelyDead();
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
|
@ -238,7 +239,7 @@ public:
|
||||||
void setReverbVol(float rvol);
|
void setReverbVol(float rvol);
|
||||||
|
|
||||||
/** Set envelope for voice */
|
/** Set envelope for voice */
|
||||||
void setAdsr(ObjectId adsrId);
|
void setAdsr(ObjectId adsrId, bool dls);
|
||||||
|
|
||||||
/** Set pitch in absolute hertz */
|
/** Set pitch in absolute hertz */
|
||||||
void setPitchFrequency(uint32_t hz, uint16_t fine);
|
void setPitchFrequency(uint32_t hz, uint16_t fine);
|
||||||
|
@ -261,6 +262,8 @@ public:
|
||||||
/** Get note played on voice */
|
/** Get note played on voice */
|
||||||
uint8_t getLastNote() const {return m_state.m_initKey;}
|
uint8_t getLastNote() const {return m_state.m_initKey;}
|
||||||
|
|
||||||
|
void notifyCtrlChange(uint8_t ctrl, int8_t val);
|
||||||
|
|
||||||
/** Get MIDI Controller value on voice */
|
/** Get MIDI Controller value on voice */
|
||||||
int8_t getCtrlValue(uint8_t ctrl) const
|
int8_t getCtrlValue(uint8_t ctrl) const
|
||||||
{
|
{
|
||||||
|
@ -283,6 +286,7 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_extCtrlVals[ctrl] = val;
|
m_extCtrlVals[ctrl] = val;
|
||||||
|
notifyCtrlChange(ctrl, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get ModWheel value on voice */
|
/** Get ModWheel value on voice */
|
||||||
|
|
|
@ -79,7 +79,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data)
|
||||||
const uint8_t* setupEnd = data + header.groupEndOff;
|
const uint8_t* setupEnd = data + header.groupEndOff;
|
||||||
while (setupData < setupEnd)
|
while (setupData < setupEnd)
|
||||||
{
|
{
|
||||||
uint32_t songId = SBig(*reinterpret_cast<const uint32_t*>(setupData));
|
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
|
||||||
idx.m_midiSetups[songId] =
|
idx.m_midiSetups[songId] =
|
||||||
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
|
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
|
||||||
setupData += 5 * 16 + 4;
|
setupData += 5 * 16 + 4;
|
||||||
|
|
|
@ -102,7 +102,7 @@ void Engine::_bringOutYourDead()
|
||||||
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
|
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
|
||||||
{
|
{
|
||||||
Emitter* emitter = it->get();
|
Emitter* emitter = it->get();
|
||||||
if (emitter->getVoice()->m_voxState == VoiceState::Dead)
|
if (emitter->getVoice()->_isRecursivelyDead())
|
||||||
{
|
{
|
||||||
emitter->_destroy();
|
emitter->_destroy();
|
||||||
it = m_activeEmitters.erase(it);
|
it = m_activeEmitters.erase(it);
|
||||||
|
@ -115,7 +115,7 @@ void Engine::_bringOutYourDead()
|
||||||
{
|
{
|
||||||
Voice* vox = it->get();
|
Voice* vox = it->get();
|
||||||
vox->_bringOutYourDead();
|
vox->_bringOutYourDead();
|
||||||
if (vox->m_voxState == VoiceState::Dead)
|
if (vox->_isRecursivelyDead())
|
||||||
{
|
{
|
||||||
it = _destroyVoice(vox);
|
it = _destroyVoice(vox);
|
||||||
continue;
|
continue;
|
||||||
|
@ -166,7 +166,7 @@ const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data)
|
||||||
const SFXGroupIndex& sfxGroup = grp.second;
|
const SFXGroupIndex& sfxGroup = grp.second;
|
||||||
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
|
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
|
||||||
for (const auto& ent : sfxGroup.m_sfxEntries)
|
for (const auto& ent : sfxGroup.m_sfxEntries)
|
||||||
m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, SBig(ent.second->objId));
|
m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, ent.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -292,11 +292,12 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
AudioGroup* grp = std::get<0>(search->second);
|
AudioGroup* grp = std::get<0>(search->second);
|
||||||
|
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::shared_ptr<Voice> ret = _allocateVoice(*grp, std::get<1>(search->second), 32000.0, true, false, smx);
|
std::shared_ptr<Voice> ret = _allocateVoice(*grp, std::get<1>(search->second), 32000.0, true, false, smx);
|
||||||
if (!ret->loadSoundObject(std::get<2>(search->second), 0, 1000.f, 0x3c, 0, 0))
|
if (!ret->loadSoundObject(SBig(entry->objId), 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||||
{
|
{
|
||||||
_destroyVoice(ret.get());
|
_destroyVoice(ret.get());
|
||||||
return {};
|
return {};
|
||||||
|
@ -315,18 +316,20 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
AudioGroup* grp = std::get<0>(search->second);
|
AudioGroup* grp = std::get<0>(search->second);
|
||||||
|
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::shared_ptr<Voice> vox = _allocateVoice(*grp, std::get<1>(search->second), 32000.0, true, true, smx);
|
std::shared_ptr<Voice> vox = _allocateVoice(*grp, std::get<1>(search->second), 32000.0, true, true, smx);
|
||||||
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox)));
|
m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(vox)));
|
||||||
Emitter& ret = *m_activeEmitters.back();
|
Emitter& ret = *m_activeEmitters.back();
|
||||||
if (!ret.getVoice()->loadSoundObject(std::get<2>(search->second), 0, 1000.f, 0x3c, 0, 0))
|
if (!ret.getVoice()->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||||
{
|
{
|
||||||
ret._destroy();
|
ret._destroy();
|
||||||
m_activeEmitters.pop_back();
|
m_activeEmitters.pop_back();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
vox->setPan(entry->panning);
|
||||||
ret.setPos(pos);
|
ret.setPos(pos);
|
||||||
ret.setDir(dir);
|
ret.setDir(dir);
|
||||||
ret.setMaxDist(maxDist);
|
ret.setMaxDist(maxDist);
|
||||||
|
|
|
@ -6,55 +6,51 @@ namespace amuse
|
||||||
void Envelope::reset(const ADSR* adsr)
|
void Envelope::reset(const ADSR* adsr)
|
||||||
{
|
{
|
||||||
m_phase = State::Attack;
|
m_phase = State::Attack;
|
||||||
m_curADSR = adsr;
|
m_curTime = 0.0;
|
||||||
m_curMs = 0.0;
|
m_attackTime = adsr->getAttack();
|
||||||
if (m_curADSR->decayCoarse == 128)
|
m_decayTime = adsr->getDecay();
|
||||||
m_sustainFactor = 1.f;
|
m_sustainFactor = adsr->getSustain();
|
||||||
else
|
m_releaseTime = adsr->getRelease();
|
||||||
m_sustainFactor = (adsr->sustainCoarse * 6.25 + adsr->sustainFine * 0.0244) / 100.0;
|
m_releaseStartFactor = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Envelope::reset(const ADSRDLS* adsr, int8_t note, int8_t vel)
|
||||||
|
{
|
||||||
|
m_phase = State::Attack;
|
||||||
|
m_curTime = 0.0;
|
||||||
|
m_attackTime = adsr->getVelToAttack(vel);
|
||||||
|
m_decayTime = adsr->getKeyToDecay(note);
|
||||||
|
m_sustainFactor = adsr->getSustain();
|
||||||
|
m_releaseTime = adsr->getRelease();
|
||||||
m_releaseStartFactor = 0.0;
|
m_releaseStartFactor = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Envelope::keyOff()
|
void Envelope::keyOff()
|
||||||
{
|
{
|
||||||
m_phase = State::Release;
|
m_phase = (m_releaseTime != 0.0) ? State::Release : State::Complete;
|
||||||
m_curMs = 0.0;
|
m_curTime = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Envelope::nextSample(double sampleRate)
|
float Envelope::nextSample(double sampleRate)
|
||||||
{
|
{
|
||||||
if (!m_curADSR)
|
m_curTime += 1.0 / sampleRate;
|
||||||
{
|
|
||||||
if (m_phase == State::Release || m_phase == State::Complete)
|
|
||||||
return 0.f;
|
|
||||||
return 1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_curMs += 1.0 / (sampleRate / 1000.0);
|
|
||||||
|
|
||||||
switch (m_phase)
|
switch (m_phase)
|
||||||
{
|
{
|
||||||
case State::Attack:
|
case State::Attack:
|
||||||
{
|
{
|
||||||
uint16_t attack = m_curADSR->attackCoarse * 255 + m_curADSR->attackFine;
|
if (m_attackTime == 0.0)
|
||||||
if (attack == 0)
|
|
||||||
{
|
{
|
||||||
if (m_curADSR->decayCoarse == 128)
|
|
||||||
m_phase = State::Sustain;
|
|
||||||
else
|
|
||||||
m_phase = State::Decay;
|
m_phase = State::Decay;
|
||||||
m_curMs = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = 1.f;
|
m_releaseStartFactor = 1.f;
|
||||||
return 1.f;
|
return 1.f;
|
||||||
}
|
}
|
||||||
double attackFac = m_curMs / double(attack);
|
double attackFac = m_curTime / m_attackTime;
|
||||||
if (attackFac >= 1.0)
|
if (attackFac >= 1.0)
|
||||||
{
|
{
|
||||||
if (m_curADSR->decayCoarse == 128)
|
|
||||||
m_phase = State::Sustain;
|
|
||||||
else
|
|
||||||
m_phase = State::Decay;
|
m_phase = State::Decay;
|
||||||
m_curMs = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = 1.f;
|
m_releaseStartFactor = 1.f;
|
||||||
return 1.f;
|
return 1.f;
|
||||||
}
|
}
|
||||||
|
@ -63,19 +59,18 @@ float Envelope::nextSample(double sampleRate)
|
||||||
}
|
}
|
||||||
case State::Decay:
|
case State::Decay:
|
||||||
{
|
{
|
||||||
uint16_t decay = m_curADSR->decayCoarse * 255 + m_curADSR->decayFine;
|
if (m_decayTime == 0.0)
|
||||||
if (decay == 0)
|
|
||||||
{
|
{
|
||||||
m_phase = State::Sustain;
|
m_phase = State::Sustain;
|
||||||
m_curMs = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = m_sustainFactor;
|
m_releaseStartFactor = m_sustainFactor;
|
||||||
return m_sustainFactor;
|
return m_sustainFactor;
|
||||||
}
|
}
|
||||||
double decayFac = m_curMs / double(decay);
|
double decayFac = m_curTime / m_decayTime;
|
||||||
if (decayFac >= 1.0)
|
if (decayFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Sustain;
|
m_phase = State::Sustain;
|
||||||
m_curMs = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = m_sustainFactor;
|
m_releaseStartFactor = m_sustainFactor;
|
||||||
return m_sustainFactor;
|
return m_sustainFactor;
|
||||||
}
|
}
|
||||||
|
@ -88,19 +83,18 @@ float Envelope::nextSample(double sampleRate)
|
||||||
}
|
}
|
||||||
case State::Release:
|
case State::Release:
|
||||||
{
|
{
|
||||||
uint16_t release = m_curADSR->releaseCoarse * 255 + m_curADSR->releaseFine;
|
if (m_releaseTime == 0.0)
|
||||||
if (release == 0)
|
|
||||||
{
|
{
|
||||||
m_phase = State::Complete;
|
m_phase = State::Complete;
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
double releaseFac = m_curMs / double(release);
|
double releaseFac = m_curTime / m_releaseTime;
|
||||||
if (releaseFac >= 1.0)
|
if (releaseFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Complete;
|
m_phase = State::Complete;
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
return (1.0 - releaseFac) * m_releaseStartFactor;
|
return std::min(m_releaseStartFactor, 1.0 - releaseFac);
|
||||||
}
|
}
|
||||||
case State::Complete:
|
case State::Complete:
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
|
@ -69,9 +69,9 @@ Sequencer::ChannelState::~ChannelState()
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||||
: m_parent(parent), m_setup(m_parent.m_midiSetup[chanId])
|
: m_parent(parent), m_chanId(chanId), m_setup(m_parent.m_midiSetup[chanId])
|
||||||
{
|
{
|
||||||
if (chanId == 10)
|
if (chanId == 9)
|
||||||
{
|
{
|
||||||
auto it = m_parent.m_songGroup.m_drumPages.find(m_setup.programNo);
|
auto it = m_parent.m_songGroup.m_drumPages.find(m_setup.programNo);
|
||||||
if (it != m_parent.m_songGroup.m_drumPages.cend())
|
if (it != m_parent.m_songGroup.m_drumPages.cend())
|
||||||
|
@ -126,6 +126,11 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
|
||||||
}
|
}
|
||||||
ret->setVolume(m_parent.m_curVol * m_setup.volume / 127.f);
|
ret->setVolume(m_parent.m_curVol * m_setup.volume / 127.f);
|
||||||
ret->setPan(m_setup.panning / 64.f - 127.f);
|
ret->setPan(m_setup.panning / 64.f - 127.f);
|
||||||
|
ret->setPitchWheel(m_curPitchWheel);
|
||||||
|
|
||||||
|
if (m_ctrlVals[64] > 64)
|
||||||
|
ret->setPedal(true);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +169,51 @@ void Sequencer::keyOff(uint8_t chan, uint8_t note, uint8_t velocity)
|
||||||
void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
||||||
{
|
{
|
||||||
m_ctrlVals[ctrl] = val;
|
m_ctrlVals[ctrl] = val;
|
||||||
|
for (const auto& vox : m_chanVoxs)
|
||||||
|
vox.second->notifyCtrlChange(ctrl, val);
|
||||||
|
for (const auto& vox : m_keyoffVoxs)
|
||||||
|
vox->notifyCtrlChange(ctrl, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequencer::ChannelState::programChange(int8_t prog)
|
||||||
|
{
|
||||||
|
if (m_chanId == 9)
|
||||||
|
{
|
||||||
|
auto it = m_parent.m_songGroup.m_drumPages.find(prog);
|
||||||
|
if (it != m_parent.m_songGroup.m_drumPages.cend())
|
||||||
|
{
|
||||||
|
m_page = it->second;
|
||||||
|
m_curProgram = prog;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = m_parent.m_songGroup.m_normPages.find(prog);
|
||||||
|
if (it != m_parent.m_songGroup.m_normPages.cend())
|
||||||
|
{
|
||||||
|
m_page = it->second;
|
||||||
|
m_curProgram = prog;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::ChannelState::nextProgram()
|
||||||
|
{
|
||||||
|
int newProg = m_curProgram;
|
||||||
|
while ((newProg += 1) <= 127)
|
||||||
|
if (programChange(newProg))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::ChannelState::prevProgram()
|
||||||
|
{
|
||||||
|
int newProg = m_curProgram;
|
||||||
|
while ((newProg -= 1) >= 0)
|
||||||
|
if (programChange(newProg))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
|
void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
|
||||||
|
@ -177,6 +227,7 @@ void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
|
||||||
|
|
||||||
void Sequencer::ChannelState::setPitchWheel(float pitchWheel)
|
void Sequencer::ChannelState::setPitchWheel(float pitchWheel)
|
||||||
{
|
{
|
||||||
|
m_curPitchWheel = pitchWheel;
|
||||||
for (const auto& vox : m_chanVoxs)
|
for (const auto& vox : m_chanVoxs)
|
||||||
vox.second->setPitchWheel(pitchWheel);
|
vox.second->setPitchWheel(pitchWheel);
|
||||||
for (const auto& vox : m_keyoffVoxs)
|
for (const auto& vox : m_keyoffVoxs)
|
||||||
|
@ -207,7 +258,14 @@ void Sequencer::allOff(bool now)
|
||||||
{
|
{
|
||||||
if (now)
|
if (now)
|
||||||
for (auto& chan : m_chanStates)
|
for (auto& chan : m_chanStates)
|
||||||
|
{
|
||||||
|
for (const auto& vox : chan.second->m_chanVoxs)
|
||||||
|
m_engine._destroyVoice(vox.second.get());
|
||||||
|
for (const auto& vox : chan.second->m_keyoffVoxs)
|
||||||
|
m_engine._destroyVoice(vox.get());
|
||||||
chan.second->m_chanVoxs.clear();
|
chan.second->m_chanVoxs.clear();
|
||||||
|
chan.second->m_keyoffVoxs.clear();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
for (auto& chan : m_chanStates)
|
for (auto& chan : m_chanStates)
|
||||||
chan.second->allOff();
|
chan.second->allOff();
|
||||||
|
@ -324,4 +382,40 @@ void Sequencer::setVolume(float vol)
|
||||||
chan.second->setVolume(vol);
|
chan.second->setVolume(vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int8_t Sequencer::getChanProgram(int8_t chanId) const
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chanId);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return chanSearch->second->m_curProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequencer::setChanProgram(int8_t chanId, int8_t prog)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chanId);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return chanSearch->second->programChange(prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::nextChanProgram(int8_t chanId)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chanId);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
return chanSearch->second->nextProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequencer::prevChanProgram(int8_t chanId)
|
||||||
|
{
|
||||||
|
auto chanSearch = m_chanStates.find(chanId);
|
||||||
|
if (chanSearch == m_chanStates.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
return chanSearch->second->prevProgram();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,7 +405,8 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
case Op::SetAdsr:
|
case Op::SetAdsr:
|
||||||
{
|
{
|
||||||
ObjectId tableId = *reinterpret_cast<ObjectId*>(&cmd.m_data[0]);
|
ObjectId tableId = *reinterpret_cast<ObjectId*>(&cmd.m_data[0]);
|
||||||
vox.setAdsr(tableId);
|
bool dlsMode = cmd.m_data[2];
|
||||||
|
vox.setAdsr(tableId, dlsMode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::ScaleVolume:
|
case Op::ScaleVolume:
|
||||||
|
@ -423,12 +424,12 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
if (curveData)
|
if (curveData)
|
||||||
{
|
{
|
||||||
vox.setVolume((*curveData)[eval] / 127.f);
|
vox.m_curVol = (*curveData)[eval] / 127.f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vox.setVolume(eval / 127.f);
|
vox.m_curVol = eval / 127.f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Panning:
|
case Op::Panning:
|
||||||
|
@ -723,7 +724,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||||
{
|
{
|
||||||
int16_t scale = *reinterpret_cast<int16_t*>(&cmd.m_data[0]);
|
int16_t scale = *reinterpret_cast<int16_t*>(&cmd.m_data[0]);
|
||||||
bool orgVel = cmd.m_data[2];
|
bool orgVel = cmd.m_data[2];
|
||||||
vox.setVolume(int32_t(orgVel ? m_initVel : m_curVel) * scale / 4096.f / 127.f);
|
vox.m_curVol = int32_t(orgVel ? m_initVel : m_curVel) * scale / 4096.f / 127.f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Mod2Vibrange:
|
case Op::Mod2Vibrange:
|
||||||
|
|
113
lib/Voice.cpp
113
lib/Voice.cpp
|
@ -24,13 +24,13 @@ void Voice::_destroy()
|
||||||
|
|
||||||
Voice::~Voice()
|
Voice::~Voice()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "DEALLOC %p\n", this);
|
//fprintf(stderr, "DEALLOC %d\n", m_vid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx)
|
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx)
|
||||||
: Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_submix(smx)
|
: Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_submix(smx)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ALLOC %p\n", this);
|
//fprintf(stderr, "ALLOC %d\n", m_vid);
|
||||||
if (m_submix)
|
if (m_submix)
|
||||||
m_submix->m_activeVoices.insert(this);
|
m_submix->m_activeVoices.insert(this);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool
|
||||||
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx)
|
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx)
|
||||||
: Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_submix(smx)
|
: Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_submix(smx)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ALLOC %p\n", this);
|
//fprintf(stderr, "ALLOC %d\n", m_vid);
|
||||||
if (m_submix)
|
if (m_submix)
|
||||||
m_submix->m_activeVoices.insert(this);
|
m_submix->m_activeVoices.insert(this);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@ void Voice::_reset()
|
||||||
m_curReverbVol = 0.f;
|
m_curReverbVol = 0.f;
|
||||||
m_curPan = 0.f;
|
m_curPan = 0.f;
|
||||||
m_curSpan = 0.f;
|
m_curSpan = 0.f;
|
||||||
m_curPitchWheel = 0.f;
|
|
||||||
m_curAftertouch = 0;
|
m_curAftertouch = 0;
|
||||||
m_pitchWheelUp = 6;
|
m_pitchWheelUp = 6;
|
||||||
m_pitchWheelDown = 6;
|
m_pitchWheelDown = 6;
|
||||||
|
@ -67,7 +66,6 @@ void Voice::_reset()
|
||||||
m_tremoloModScale = 0.f;
|
m_tremoloModScale = 0.f;
|
||||||
m_lfoPeriods[0] = 0.f;
|
m_lfoPeriods[0] = 0.f;
|
||||||
m_lfoPeriods[1] = 0.f;
|
m_lfoPeriods[1] = 0.f;
|
||||||
memset(m_extCtrlVals, 0, 128);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::_macroSampleEnd()
|
void Voice::_macroSampleEnd()
|
||||||
|
@ -90,6 +88,9 @@ void Voice::_macroSampleEnd()
|
||||||
|
|
||||||
bool Voice::_checkSamplePos()
|
bool Voice::_checkSamplePos()
|
||||||
{
|
{
|
||||||
|
if (!m_curSample)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (m_curSamplePos >= m_lastSamplePos)
|
if (m_curSamplePos >= m_lastSamplePos)
|
||||||
{
|
{
|
||||||
if (m_curSample->first.m_loopLengthSamples)
|
if (m_curSample->first.m_loopLengthSamples)
|
||||||
|
@ -128,7 +129,7 @@ void Voice::_doKeyOff()
|
||||||
|
|
||||||
void Voice::_setTotalPitch(int32_t cents)
|
void Voice::_setTotalPitch(int32_t cents)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "PITCH %d\n", cents);
|
//fprintf(stderr, "PITCH %d\n", cents);
|
||||||
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
||||||
double ratio = std::exp2(interval / 1200.0);
|
double ratio = std::exp2(interval / 1200.0);
|
||||||
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
||||||
|
@ -200,7 +201,7 @@ std::list<std::shared_ptr<Voice>>::iterator Voice::_destroyVoice(Voice* voice)
|
||||||
return m_childVoices.erase(voice->m_engineIt);
|
return m_childVoices.erase(voice->m_engineIt);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Voice::_advanceSample(int16_t& samp)
|
bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
{
|
{
|
||||||
double dt = 1.0 / m_sampleRate;
|
double dt = 1.0 / m_sampleRate;
|
||||||
m_voiceTime += dt;
|
m_voiceTime += dt;
|
||||||
|
@ -223,7 +224,7 @@ bool Voice::_advanceSample(int16_t& samp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Factor in ADSR envelope state */
|
/* Factor in ADSR envelope state */
|
||||||
float totalVol = m_curVol * m_volAdsr.nextSample(m_sampleRate);
|
float totalVol = m_userVol * m_curVol * m_volAdsr.nextSample(m_sampleRate) /* (m_state.m_curVel / 127.f)*/;
|
||||||
|
|
||||||
/* Apply tremolo */
|
/* Apply tremolo */
|
||||||
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
|
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
|
||||||
|
@ -283,13 +284,13 @@ bool Voice::_advanceSample(int16_t& samp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate total pitch */
|
/* Calculate total pitch */
|
||||||
int32_t totalPitch = m_curPitch;
|
newPitch = m_curPitch;
|
||||||
bool pitchDirty = m_pitchDirty;
|
refresh |= m_pitchDirty;
|
||||||
m_pitchDirty = false;
|
m_pitchDirty = false;
|
||||||
if (m_pitchEnv)
|
if (m_pitchEnv)
|
||||||
{
|
{
|
||||||
totalPitch = m_curPitch * m_pitchAdsr.nextSample(m_sampleRate);
|
newPitch = m_curPitch * m_pitchAdsr.nextSample(m_sampleRate);
|
||||||
pitchDirty = true;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process pitch sweep 1 */
|
/* Process pitch sweep 1 */
|
||||||
|
@ -297,7 +298,7 @@ bool Voice::_advanceSample(int16_t& samp)
|
||||||
{
|
{
|
||||||
m_pitchSweep1 += m_pitchSweep1Add;
|
m_pitchSweep1 += m_pitchSweep1Add;
|
||||||
--m_pitchSweep1Times;
|
--m_pitchSweep1Times;
|
||||||
pitchDirty = true;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process pitch sweep 2 */
|
/* Process pitch sweep 2 */
|
||||||
|
@ -305,13 +306,6 @@ bool Voice::_advanceSample(int16_t& samp)
|
||||||
{
|
{
|
||||||
m_pitchSweep2 += m_pitchSweep2Add;
|
m_pitchSweep2 += m_pitchSweep2Add;
|
||||||
--m_pitchSweep2Times;
|
--m_pitchSweep2Times;
|
||||||
pitchDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply total pitch */
|
|
||||||
if (pitchDirty)
|
|
||||||
{
|
|
||||||
_setTotalPitch(totalPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal);
|
|
||||||
refresh = true;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +321,14 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
|
|
||||||
/* Attempt to load stopped sample for immediate decoding */
|
/* Attempt to load stopped sample for immediate decoding */
|
||||||
if (!m_curSample)
|
if (!m_curSample)
|
||||||
|
{
|
||||||
dead = m_state.advance(*this, 0.0);
|
dead = m_state.advance(*this, 0.0);
|
||||||
|
if (!dead)
|
||||||
|
{
|
||||||
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_curSample)
|
if (m_curSample)
|
||||||
{
|
{
|
||||||
|
@ -335,6 +336,9 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
uint32_t block = m_curSamplePos / 14;
|
uint32_t block = m_curSamplePos / 14;
|
||||||
uint32_t rem = m_curSamplePos % 14;
|
uint32_t rem = m_curSamplePos % 14;
|
||||||
|
|
||||||
|
bool refresh = false;
|
||||||
|
int32_t curPitch = m_curPitch;
|
||||||
|
|
||||||
if (rem)
|
if (rem)
|
||||||
{
|
{
|
||||||
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||||
|
@ -352,12 +356,15 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
case SampleFormat::PCM:
|
case SampleFormat::PCM:
|
||||||
{
|
{
|
||||||
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
||||||
|
remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos);
|
||||||
for (uint32_t i=0 ; i<remCount ; ++i)
|
for (uint32_t i=0 ; i<remCount ; ++i)
|
||||||
data[i] = SBig(pcm[m_curSamplePos+i]);
|
data[i] = SBig(pcm[m_curSamplePos+i]);
|
||||||
decSamples = remCount;
|
decSamples = remCount;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: return 0;
|
default:
|
||||||
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Per-sample processing */
|
/* Per-sample processing */
|
||||||
|
@ -365,8 +372,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
{
|
{
|
||||||
++samplesProc;
|
++samplesProc;
|
||||||
++m_curSamplePos;
|
++m_curSamplePos;
|
||||||
if (_advanceSample(data[i]))
|
refresh |= _advanceSample(data[i], curPitch);
|
||||||
return samplesProc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesRem -= decSamples;
|
samplesRem -= decSamples;
|
||||||
|
@ -398,12 +404,15 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
case SampleFormat::PCM:
|
case SampleFormat::PCM:
|
||||||
{
|
{
|
||||||
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
||||||
|
remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos);
|
||||||
for (uint32_t i=0 ; i<remCount ; ++i)
|
for (uint32_t i=0 ; i<remCount ; ++i)
|
||||||
data[i] = SBig(pcm[m_curSamplePos+i]);
|
data[i] = SBig(pcm[m_curSamplePos+i]);
|
||||||
decSamples = remCount;
|
decSamples = remCount;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: return 0;
|
default:
|
||||||
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Per-sample processing */
|
/* Per-sample processing */
|
||||||
|
@ -411,8 +420,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
{
|
{
|
||||||
++samplesProc;
|
++samplesProc;
|
||||||
++m_curSamplePos;
|
++m_curSamplePos;
|
||||||
if (_advanceSample(data[i]))
|
refresh |= _advanceSample(data[i], curPitch);
|
||||||
return samplesProc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesRem -= decSamples;
|
samplesRem -= decSamples;
|
||||||
|
@ -425,6 +433,9 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (refresh)
|
||||||
|
_setTotalPitch(curPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memset(data, 0, sizeof(int16_t) * samples);
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
@ -535,15 +546,17 @@ bool Voice::loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec
|
||||||
|
|
||||||
void Voice::_macroKeyOff()
|
void Voice::_macroKeyOff()
|
||||||
{
|
{
|
||||||
|
if (m_voxState == VoiceState::Playing)
|
||||||
|
{
|
||||||
if (m_sustained)
|
if (m_sustained)
|
||||||
m_sustainKeyOff = true;
|
m_sustainKeyOff = true;
|
||||||
else
|
else
|
||||||
_doKeyOff();
|
_doKeyOff();
|
||||||
|
m_voxState = VoiceState::KeyOff;
|
||||||
|
}
|
||||||
|
|
||||||
for (const std::shared_ptr<Voice>& vox : m_childVoices)
|
for (const std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
vox->keyOff();
|
vox->keyOff();
|
||||||
|
|
||||||
m_voxState = VoiceState::KeyOff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::keyOff()
|
void Voice::keyOff()
|
||||||
|
@ -588,7 +601,7 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
||||||
_reset();
|
_reset();
|
||||||
m_sampleRate = m_curSample->first.m_sampleRate;
|
m_sampleRate = m_curSample->first.m_sampleRate;
|
||||||
m_curPitch = m_curSample->first.m_pitch;
|
m_curPitch = m_curSample->first.m_pitch;
|
||||||
m_pitchDirty = true;
|
setPitchWheel(m_curPitchWheel);
|
||||||
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
||||||
m_curSamplePos = offset;
|
m_curSamplePos = offset;
|
||||||
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
m_curSampleData = m_audioGroup.getSampleData(m_curSample->first.m_sampleOff);
|
||||||
|
@ -630,25 +643,25 @@ void Voice::stopSample()
|
||||||
|
|
||||||
void Voice::setVolume(float vol)
|
void Voice::setVolume(float vol)
|
||||||
{
|
{
|
||||||
m_curVol = vol;
|
m_userVol = clamp(0.f, vol, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPan(float pan)
|
void Voice::setPan(float pan)
|
||||||
{
|
{
|
||||||
m_curPan = pan;
|
m_curPan = clamp(-1.f, pan, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setSurroundPan(float span)
|
void Voice::setSurroundPan(float span)
|
||||||
{
|
{
|
||||||
m_curSpan = span;
|
m_curSpan = clamp(-1.f, span, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
||||||
{
|
{
|
||||||
m_envelopeTime = m_voiceTime;
|
m_envelopeTime = m_voiceTime;
|
||||||
m_envelopeDur = dur;
|
m_envelopeDur = dur;
|
||||||
m_envelopeStart = m_curVol;
|
m_envelopeStart = clamp(0.f, m_curVol, 1.f);
|
||||||
m_envelopeEnd = vol;
|
m_envelopeEnd = clamp(0.f, vol, 1.f);
|
||||||
m_envelopeCurve = envCurve;
|
m_envelopeCurve = envCurve;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,7 +670,7 @@ void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
|
||||||
m_envelopeTime = m_voiceTime;
|
m_envelopeTime = m_voiceTime;
|
||||||
m_envelopeDur = dur;
|
m_envelopeDur = dur;
|
||||||
m_envelopeStart = 0.f;
|
m_envelopeStart = 0.f;
|
||||||
m_envelopeEnd = m_curVol;
|
m_envelopeEnd = clamp(0.f, m_curVol, 1.f);
|
||||||
m_envelopeCurve = envCurve;
|
m_envelopeCurve = envCurve;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,11 +750,20 @@ void Voice::setReverbVol(float rvol)
|
||||||
m_curReverbVol = rvol;
|
m_curReverbVol = rvol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setAdsr(ObjectId adsrId)
|
void Voice::setAdsr(ObjectId adsrId, bool dls)
|
||||||
{
|
{
|
||||||
|
if (dls)
|
||||||
|
{
|
||||||
|
const ADSRDLS* adsr = m_audioGroup.getPool().tableAsAdsrDLS(adsrId);
|
||||||
|
if (adsr)
|
||||||
|
m_volAdsr.reset(adsr, m_state.m_initKey, m_state.m_initVel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
const ADSR* adsr = m_audioGroup.getPool().tableAsAdsr(adsrId);
|
const ADSR* adsr = m_audioGroup.getPool().tableAsAdsr(adsrId);
|
||||||
if (adsr)
|
if (adsr)
|
||||||
m_volAdsr.reset(adsr);
|
m_volAdsr.reset(adsr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
|
void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
|
||||||
|
@ -753,10 +775,10 @@ void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
|
||||||
|
|
||||||
void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
||||||
{
|
{
|
||||||
const ADSR* adsr = m_audioGroup.getPool().tableAsAdsr(adsrId);
|
const ADSRDLS* adsr = m_audioGroup.getPool().tableAsAdsrDLS(adsrId);
|
||||||
if (adsr)
|
if (adsr)
|
||||||
{
|
{
|
||||||
m_pitchAdsr.reset(adsr);
|
m_pitchAdsr.reset(adsr, m_state.m_initKey, m_state.m_initVel);
|
||||||
m_pitchEnvRange = cents;
|
m_pitchEnvRange = cents;
|
||||||
m_pitchEnv = true;
|
m_pitchEnv = true;
|
||||||
}
|
}
|
||||||
|
@ -766,9 +788,9 @@ void Voice::setPitchWheel(float pitchWheel)
|
||||||
{
|
{
|
||||||
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
|
||||||
if (pitchWheel > 0.f)
|
if (pitchWheel > 0.f)
|
||||||
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel;
|
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel * 100;
|
||||||
else if (pitchWheel < 0.f)
|
else if (pitchWheel < 0.f)
|
||||||
m_pitchWheelVal = m_pitchWheelDown * -m_curPitchWheel;
|
m_pitchWheelVal = m_pitchWheelDown * m_curPitchWheel * 100;
|
||||||
else
|
else
|
||||||
m_pitchWheelVal = 0;
|
m_pitchWheelVal = 0;
|
||||||
m_pitchDirty = true;
|
m_pitchDirty = true;
|
||||||
|
@ -786,6 +808,17 @@ void Voice::setAftertouch(uint8_t aftertouch)
|
||||||
m_curAftertouch = aftertouch;
|
m_curAftertouch = aftertouch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::notifyCtrlChange(uint8_t ctrl, int8_t val)
|
||||||
|
{
|
||||||
|
if (ctrl == 64)
|
||||||
|
{
|
||||||
|
if (val >= 64)
|
||||||
|
setPedal(true);
|
||||||
|
else
|
||||||
|
setPedal(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t Voice::getTotalVoices() const
|
size_t Voice::getTotalVoices() const
|
||||||
{
|
{
|
||||||
size_t ret = 1;
|
size_t ret = 1;
|
||||||
|
|
Loading…
Reference in New Issue