mirror of https://github.com/AxioDL/amuse.git
Work on Voice state
This commit is contained in:
parent
1102d50f8f
commit
e510a0ad84
|
@ -7,13 +7,6 @@
|
||||||
#include "optional.hpp"
|
#include "optional.hpp"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#ifndef _WIN32
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#else
|
|
||||||
#include <conio.h>
|
|
||||||
#endif
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -186,7 +179,7 @@ struct AppCallback : boo::IApplicationCallback
|
||||||
"░░░ 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\n"
|
||||||
"<tab>: sustain pedal, <1/2>: pitch wheel, <3-8>: mod wheel\n"
|
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
||||||
"<Z/X>: octave, <C/V>: velocity\n"
|
"<Z/X>: octave, <C/V>: velocity\n"
|
||||||
"<Q>: quit\n");
|
"<Q>: quit\n");
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,9 @@ class AudioGroupPool
|
||||||
std::unordered_map<ObjectId, std::vector<const LayerMapping*>> m_layers;
|
std::unordered_map<ObjectId, std::vector<const LayerMapping*>> m_layers;
|
||||||
public:
|
public:
|
||||||
AudioGroupPool(const unsigned char* data);
|
AudioGroupPool(const unsigned char* data);
|
||||||
|
const unsigned char* soundMacro(ObjectId id) const;
|
||||||
|
const Keymap* keymap(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 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));}
|
||||||
|
|
|
@ -22,6 +22,8 @@ class AudioGroupData;
|
||||||
/** Main audio playback system for a single audio output */
|
/** Main audio playback system for a single audio output */
|
||||||
class Engine
|
class Engine
|
||||||
{
|
{
|
||||||
|
friend class Voice;
|
||||||
|
|
||||||
IBackendVoiceAllocator& m_backend;
|
IBackendVoiceAllocator& m_backend;
|
||||||
std::unordered_map<int, std::unique_ptr<AudioGroup>> m_audioGroups;
|
std::unordered_map<int, std::unique_ptr<AudioGroup>> m_audioGroups;
|
||||||
std::list<Voice> m_activeVoices;
|
std::list<Voice> m_activeVoices;
|
||||||
|
@ -39,6 +41,9 @@ class Engine
|
||||||
public:
|
public:
|
||||||
Engine(IBackendVoiceAllocator& backend);
|
Engine(IBackendVoiceAllocator& backend);
|
||||||
|
|
||||||
|
/** Access voice backend of engine */
|
||||||
|
IBackendVoiceAllocator& getBackend() {return m_backend;}
|
||||||
|
|
||||||
/** Update all active audio entities and fill OS audio buffers as needed */
|
/** Update all active audio entities and fill OS audio buffers as needed */
|
||||||
void pumpEngine();
|
void pumpEngine();
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,32 @@
|
||||||
#ifndef __AMUSE_ENVELOPE_HPP__
|
#ifndef __AMUSE_ENVELOPE_HPP__
|
||||||
#define __AMUSE_ENVELOPE_HPP__
|
#define __AMUSE_ENVELOPE_HPP__
|
||||||
|
|
||||||
|
#include "AudioGroupPool.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** Per-sample state tracker for ADSR envelope data */
|
||||||
class Envelope
|
class Envelope
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
Attack,
|
||||||
|
Decay,
|
||||||
|
Sustain,
|
||||||
|
Release
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
State m_phase = State::Attack; /**< Current envelope state */
|
||||||
|
const ADSR* m_curADSR = nullptr; /**< Current timing envelope */
|
||||||
|
double m_sustainFactor; /**< Evaluated sustain percentage as a double */
|
||||||
|
double m_releaseStartFactor; /**< Level at whenever release event occurs */
|
||||||
|
unsigned m_curMs; /**< Current time of envelope stage */
|
||||||
|
public:
|
||||||
|
void reset(const ADSR* adsr);
|
||||||
|
void keyOff();
|
||||||
|
float nextSample(double sampleRate);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ class Voice;
|
||||||
|
|
||||||
class SoundMacroState
|
class SoundMacroState
|
||||||
{
|
{
|
||||||
|
friend class Voice;
|
||||||
|
|
||||||
/** SoundMacro header */
|
/** SoundMacro header */
|
||||||
struct Header
|
struct Header
|
||||||
{
|
{
|
||||||
|
@ -115,15 +117,6 @@ class SoundMacroState
|
||||||
/** 'program counter' stack for the active SoundMacro */
|
/** 'program counter' stack for the active SoundMacro */
|
||||||
std::vector<std::pair<const unsigned char*, int>> m_pc;
|
std::vector<std::pair<const unsigned char*, int>> m_pc;
|
||||||
|
|
||||||
float m_curVol; /**< final volume sent to voice */
|
|
||||||
bool m_volDirty; /**< set when voice needs updated volume */
|
|
||||||
|
|
||||||
float m_curPan; /**< final pan sent to voice */
|
|
||||||
bool m_panDirty; /**< set when voice needs updated pan */
|
|
||||||
|
|
||||||
float m_curSpan; /**< final span sent to voice */
|
|
||||||
bool m_spanDirty; /**< set when voice needs updated span */
|
|
||||||
|
|
||||||
float m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
|
float m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
|
||||||
uint8_t m_initVel; /**< Velocity played for this macro invocation */
|
uint8_t m_initVel; /**< Velocity played for this macro invocation */
|
||||||
uint8_t m_initMod; /**< Modulation played for this macro invocation */
|
uint8_t m_initMod; /**< Modulation played for this macro invocation */
|
||||||
|
@ -131,34 +124,11 @@ class SoundMacroState
|
||||||
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
|
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
|
||||||
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
|
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
|
||||||
uint32_t m_curKey; /**< Current key played for this macro invocation (in cents) */
|
uint32_t m_curKey; /**< Current key played for this macro invocation (in cents) */
|
||||||
uint32_t m_pitchSweep1; /**< Current value of PITCHSWEEP1 controller (in cents) */
|
|
||||||
uint32_t m_pitchSweep2; /**< Current value of PITCHSWEEP2 controller (in cents) */
|
|
||||||
int16_t m_pitchSweep1Add; /**< Value to add to PITCHSWEEP1 controller each cycle */
|
|
||||||
int16_t m_pitchSweep2Add; /**< Value to add to PITCHSWEEP2 controller each cycle */
|
|
||||||
uint8_t m_pitchSweep1Times; /**< Remaining times to advance PITCHSWEEP1 controller */
|
|
||||||
uint8_t m_pitchSweep2Times; /**< Remaining times to advance PITCHSWEEP2 controller */
|
|
||||||
bool m_pitchDirty; /**< set when voice needs latest pitch computation */
|
|
||||||
|
|
||||||
float m_execTime; /**< time in seconds of SoundMacro execution */
|
float m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
|
||||||
bool m_keyoff; /**< keyoff message has been received */
|
bool m_keyoff; /**< keyoff message has been received */
|
||||||
bool m_sampleEnd; /**< sample has finished playback */
|
bool m_sampleEnd; /**< sample has finished playback */
|
||||||
|
|
||||||
float m_envelopeTime; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
|
||||||
float m_envelopeDur; /**< requested duration of last ENVELOPE command */
|
|
||||||
uint8_t m_envelopeStart; /**< initial value for last ENVELOPE command */
|
|
||||||
uint8_t m_envelopeEnd; /**< final value for last ENVELOPE command */
|
|
||||||
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
|
|
||||||
|
|
||||||
float m_panningTime; /**< time since last PANNING command, -1 for no active pan-sweep */
|
|
||||||
float m_panningDur; /**< requested duration of last PANNING command */
|
|
||||||
uint8_t m_panPos; /**< initial pan value of last PANNING command */
|
|
||||||
uint8_t m_panWidth; /**< delta pan value to target of last PANNING command */
|
|
||||||
|
|
||||||
float m_spanningTime; /**< time since last SPANNING command, -1 for no active span-sweep */
|
|
||||||
float m_spanningDur; /**< requested duration of last SPANNING command */
|
|
||||||
uint8_t m_spanPos; /**< initial pan value of last SPANNING command */
|
|
||||||
uint8_t m_spanWidth; /**< delta pan value to target of last SPANNING command */
|
|
||||||
|
|
||||||
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
|
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
|
||||||
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
|
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
|
||||||
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
|
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
|
||||||
|
@ -177,16 +147,6 @@ class SoundMacroState
|
||||||
uint8_t m_portamentoType; /**< (0: New key pressed while old key pressed, 1: Always) */
|
uint8_t m_portamentoType; /**< (0: New key pressed while old key pressed, 1: Always) */
|
||||||
float m_portamentoTime; /**< portamento transition time, 0.f will perform legato */
|
float m_portamentoTime; /**< portamento transition time, 0.f will perform legato */
|
||||||
|
|
||||||
int32_t m_vibratoLevel; /**< scale of vibrato effect (in cents) */
|
|
||||||
int32_t m_vibratoModLevel; /**< scale of vibrato mod-wheel influence (in cents) */
|
|
||||||
float m_vibratoPeriod; /**< vibrato wave period-time, 0.f will disable vibrato */
|
|
||||||
bool m_vibratoModWheel; /**< vibrato scaled with mod-wheel if set */
|
|
||||||
|
|
||||||
float m_tremoloScale; /**< minimum volume factor produced via LFO */
|
|
||||||
float m_tremoloModScale; /**< minimum volume factor produced via LFO, scaled via mod wheel */
|
|
||||||
|
|
||||||
float m_lfoPeriods[2]; /**< time-periods for LFO1 and LFO2 */
|
|
||||||
|
|
||||||
/** Used to build a multi-component formula for overriding controllers */
|
/** Used to build a multi-component formula for overriding controllers */
|
||||||
struct Evaluator
|
struct Evaluator
|
||||||
{
|
{
|
||||||
|
@ -259,8 +219,8 @@ class SoundMacroState
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** initialize state for SoundMacro data at `ptr` */
|
/** initialize state for SoundMacro data at `ptr` */
|
||||||
void initialize(const unsigned char* ptr);
|
void initialize(const unsigned char* ptr, int step);
|
||||||
void initialize(const unsigned char* ptr, float ticksPerSec,
|
void initialize(const unsigned char* ptr, int step, float ticksPerSec,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
|
||||||
|
|
||||||
/** advances `dt` seconds worth of commands in the SoundMacro
|
/** advances `dt` seconds worth of commands in the SoundMacro
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
#include "AudioGroupSampleDirectory.hpp"
|
#include "AudioGroupSampleDirectory.hpp"
|
||||||
#include "AudioGroup.hpp"
|
#include "AudioGroup.hpp"
|
||||||
|
#include "Envelope.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
@ -27,14 +28,15 @@ enum class VoiceState
|
||||||
class Voice : public Entity
|
class Voice : public Entity
|
||||||
{
|
{
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
|
friend class SoundMacroState;
|
||||||
int m_vid; /**< VoiceID of this voice instance */
|
int m_vid; /**< VoiceID of this voice instance */
|
||||||
bool m_emitter; /**< Voice is part of an Emitter */
|
bool m_emitter; /**< Voice is part of an Emitter */
|
||||||
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
||||||
std::list<Voice>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
std::list<Voice>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
|
||||||
|
|
||||||
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
||||||
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
||||||
std::list<Voice> m_childVoices; /**< Child voices for PLAYMACRO usage */
|
std::list<Voice> m_childVoices; /**< Child voices for PLAYMACRO usage */
|
||||||
uint8_t m_lastNote = 0; /**< Last MIDI semitone played by voice */
|
|
||||||
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
||||||
|
|
||||||
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
||||||
|
@ -43,14 +45,67 @@ class Voice : public Entity
|
||||||
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
||||||
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
||||||
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
|
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
|
||||||
|
double m_sampleRate; /**< Current sample rate computed from relative sample key or SETPITCH */
|
||||||
|
double m_voiceTime; /**< Current seconds of voice playback (per-sample resolution) */
|
||||||
|
|
||||||
VoiceState m_voxState = VoiceState::Finished; /**< Current high-level state of voice */
|
VoiceState m_voxState = VoiceState::Finished; /**< Current high-level state of voice */
|
||||||
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
||||||
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
||||||
|
|
||||||
|
float m_curVol; /**< Current volume of voice */
|
||||||
|
float m_curReverbVol; /**< Current reverb volume of voice */
|
||||||
|
float m_curPan; /**< Current pan of voice */
|
||||||
|
float m_curSpan; /**< Current surround pan of voice */
|
||||||
|
int32_t m_curPitch; /**< Current base pitch in cents */
|
||||||
|
bool m_pitchDirty; /**< m_curPitch has been updated and needs sending to voice */
|
||||||
|
|
||||||
|
Envelope m_volAdsr; /**< Volume envelope */
|
||||||
|
double m_envelopeTime; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
||||||
|
double m_envelopeDur; /**< requested duration of last ENVELOPE command */
|
||||||
|
float m_envelopeStart; /**< initial value for last ENVELOPE command */
|
||||||
|
float m_envelopeEnd; /**< final value for last ENVELOPE command */
|
||||||
|
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
|
||||||
|
|
||||||
|
bool m_pitchEnv = false; /**< Pitch envelope activated */
|
||||||
|
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
|
||||||
|
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
|
||||||
|
|
||||||
|
uint32_t m_pitchSweep1; /**< Current value of PITCHSWEEP1 controller (in cents) */
|
||||||
|
uint32_t m_pitchSweep2; /**< Current value of PITCHSWEEP2 controller (in cents) */
|
||||||
|
int16_t m_pitchSweep1Add; /**< Value to add to PITCHSWEEP1 controller each cycle */
|
||||||
|
int16_t m_pitchSweep2Add; /**< Value to add to PITCHSWEEP2 controller each cycle */
|
||||||
|
uint8_t m_pitchSweep1Times; /**< Remaining times to advance PITCHSWEEP1 controller */
|
||||||
|
uint8_t m_pitchSweep2Times; /**< Remaining times to advance PITCHSWEEP2 controller */
|
||||||
|
|
||||||
|
float m_panningTime; /**< time since last PANNING command, -1 for no active pan-sweep */
|
||||||
|
float m_panningDur; /**< requested duration of last PANNING command */
|
||||||
|
uint8_t m_panPos; /**< initial pan value of last PANNING command */
|
||||||
|
uint8_t m_panWidth; /**< delta pan value to target of last PANNING command */
|
||||||
|
|
||||||
|
float m_spanningTime; /**< time since last SPANNING command, -1 for no active span-sweep */
|
||||||
|
float m_spanningDur; /**< requested duration of last SPANNING command */
|
||||||
|
uint8_t m_spanPos; /**< initial pan value of last SPANNING command */
|
||||||
|
uint8_t m_spanWidth; /**< delta pan value to target of last SPANNING command */
|
||||||
|
|
||||||
|
int32_t m_vibratoLevel; /**< scale of vibrato effect (in cents) */
|
||||||
|
int32_t m_vibratoModLevel; /**< scale of vibrato mod-wheel influence (in cents) */
|
||||||
|
float m_vibratoPeriod; /**< vibrato wave period-time, 0.f will disable vibrato */
|
||||||
|
bool m_vibratoModWheel; /**< vibrato scaled with mod-wheel if set */
|
||||||
|
|
||||||
|
float m_tremoloScale; /**< minimum volume factor produced via LFO */
|
||||||
|
float m_tremoloModScale; /**< minimum volume factor produced via LFO, scaled via mod wheel */
|
||||||
|
|
||||||
|
float m_lfoPeriods[2]; /**< time-periods for LFO1 and LFO2 */
|
||||||
|
|
||||||
void _destroy();
|
void _destroy();
|
||||||
|
void _reset();
|
||||||
bool _checkSamplePos();
|
bool _checkSamplePos();
|
||||||
void _doKeyOff();
|
void _doKeyOff();
|
||||||
|
bool _advanceSample(int16_t& samp);
|
||||||
|
void _setTotalPitch(int32_t cents);
|
||||||
|
|
||||||
|
Voice* _allocateVoice(double sampleRate, bool dynamicPitch);
|
||||||
|
std::list<Voice>::iterator _destroyVoice(Voice* voice);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
|
||||||
|
@ -69,11 +124,16 @@ public:
|
||||||
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
||||||
int vid() const {return m_vid;}
|
int vid() const {return m_vid;}
|
||||||
|
|
||||||
|
/** Get max VoiceId of this voice and any contained children */
|
||||||
|
int maxVid() const;
|
||||||
|
|
||||||
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
||||||
Voice* startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
|
Voice* startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
|
||||||
|
|
||||||
/** Load specified SoundMacro ID of within group into voice */
|
/** Load specified SoundMacro ID of within group into voice */
|
||||||
bool loadSoundMacro(ObjectId macroId, int macroStep=0, bool pushPc=false);
|
bool loadSoundMacro(ObjectId macroId, int macroStep, float ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||||
|
bool pushPc=false);
|
||||||
|
|
||||||
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
||||||
void keyOff();
|
void keyOff();
|
||||||
|
@ -91,10 +151,22 @@ public:
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
|
|
||||||
/** Set current voice panning immediately */
|
/** Set current voice panning immediately */
|
||||||
void setPanning(float pan);
|
void setPan(float pan);
|
||||||
|
|
||||||
/** Set current voice surround-panning immediately */
|
/** Set current voice surround-panning immediately */
|
||||||
void setSurroundPanning(float span);
|
void setSurroundPan(float span);
|
||||||
|
|
||||||
|
/** Start volume envelope to specified level */
|
||||||
|
void startEnvelope(double dur, float vol, const Curve* envCurve);
|
||||||
|
|
||||||
|
/** Start volume envelope from zero to current level */
|
||||||
|
void startFadeIn(double dur, float vol, const Curve* envCurve);
|
||||||
|
|
||||||
|
/** Start pan envelope to specified position */
|
||||||
|
void startPanning(double dur, uint8_t panPos, uint8_t panWidth);
|
||||||
|
|
||||||
|
/** Start span envelope to specified position */
|
||||||
|
void startSpanning(double dur, uint8_t spanPos, uint8_t spanWidth);
|
||||||
|
|
||||||
/** Set voice relative-pitch in cents */
|
/** Set voice relative-pitch in cents */
|
||||||
void setPitchKey(int32_t cents);
|
void setPitchKey(int32_t cents);
|
||||||
|
@ -108,6 +180,27 @@ public:
|
||||||
/** Set doppler factor for voice */
|
/** Set doppler factor for voice */
|
||||||
void setDoppler(float doppler);
|
void setDoppler(float doppler);
|
||||||
|
|
||||||
|
/** Set vibrato parameters for voice */
|
||||||
|
void setVibrato(int32_t level, int32_t modLevel, float period);
|
||||||
|
|
||||||
|
/** Configure modwheel influence range over vibrato */
|
||||||
|
void setMod2VibratoRange(int32_t modLevel);
|
||||||
|
|
||||||
|
/** Setup tremolo parameters for voice */
|
||||||
|
void setTremolo(float tremoloScale, float tremoloModScale);
|
||||||
|
|
||||||
|
/** Setup LFO1 for voice */
|
||||||
|
void setLFO1Period(float period) {m_lfoPeriods[0] = period;}
|
||||||
|
|
||||||
|
/** Setup LFO2 for voice */
|
||||||
|
void setLFO2Period(float period) {m_lfoPeriods[1] = period;}
|
||||||
|
|
||||||
|
/** Setup pitch sweep controller 1 */
|
||||||
|
void setPitchSweep1(uint8_t times, int16_t add);
|
||||||
|
|
||||||
|
/** Setup pitch sweep controller 2 */
|
||||||
|
void setPitchSweep2(uint8_t times, int16_t add);
|
||||||
|
|
||||||
/** Set reverb mix for voice */
|
/** Set reverb mix for voice */
|
||||||
void setReverbVol(float rvol);
|
void setReverbVol(float rvol);
|
||||||
|
|
||||||
|
@ -126,7 +219,7 @@ public:
|
||||||
/** Assign voice to keygroup for coordinated mass-silencing */
|
/** Assign voice to keygroup for coordinated mass-silencing */
|
||||||
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
||||||
|
|
||||||
uint8_t getLastNote() const {return m_lastNote;}
|
uint8_t getLastNote() const {return m_state.m_initKey;}
|
||||||
int8_t getCtrlValue(uint8_t ctrl) const {}
|
int8_t getCtrlValue(uint8_t ctrl) const {}
|
||||||
void setCtrlValue(uint8_t ctrl, int8_t val) {}
|
void setCtrlValue(uint8_t ctrl, int8_t val) {}
|
||||||
int8_t getPitchWheel() const {}
|
int8_t getPitchWheel() const {}
|
||||||
|
|
|
@ -81,6 +81,30 @@ AudioGroupPool::AudioGroupPool(const unsigned char* data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unsigned char* AudioGroupPool::soundMacro(ObjectId id) const
|
||||||
|
{
|
||||||
|
auto search = m_soundMacros.find(id);
|
||||||
|
if (search == m_soundMacros.cend())
|
||||||
|
return nullptr;
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Keymap* AudioGroupPool::keymap(ObjectId id) const
|
||||||
|
{
|
||||||
|
auto search = m_keymaps.find(id);
|
||||||
|
if (search == m_keymaps.cend())
|
||||||
|
return nullptr;
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<const LayerMapping*>* AudioGroupPool::layer(ObjectId id) const
|
||||||
|
{
|
||||||
|
auto search = m_layers.find(id);
|
||||||
|
if (search == m_layers.cend())
|
||||||
|
return nullptr;
|
||||||
|
return &search->second;
|
||||||
|
}
|
||||||
|
|
||||||
const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
|
const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
|
||||||
{
|
{
|
||||||
auto search = m_tables.find(id);
|
auto search = m_tables.find(id);
|
||||||
|
|
|
@ -56,9 +56,7 @@ void Engine::pumpEngine()
|
||||||
{
|
{
|
||||||
int maxVid = -1;
|
int maxVid = -1;
|
||||||
for (Voice& vox : m_activeVoices)
|
for (Voice& vox : m_activeVoices)
|
||||||
{
|
maxVid = std::max(maxVid, vox.maxVid());
|
||||||
maxVid = std::max(maxVid, vox.vid());
|
|
||||||
}
|
|
||||||
m_nextVid = maxVid + 1;
|
m_nextVid = maxVid + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +186,7 @@ Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
||||||
|
|
||||||
Voice* ret = _allocateVoice(*grp, 32000.0, true, false, smx);
|
Voice* ret = _allocateVoice(*grp, 32000.0, true, false, smx);
|
||||||
ret->setVolume(vol);
|
ret->setVolume(vol);
|
||||||
ret->setPanning(pan);
|
ret->setPan(pan);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
#include "amuse/Envelope.hpp"
|
||||||
|
|
||||||
|
namespace amuse
|
||||||
|
{
|
||||||
|
|
||||||
|
void Envelope::reset(const ADSR* adsr)
|
||||||
|
{
|
||||||
|
m_phase = State::Attack;
|
||||||
|
m_curADSR = adsr;
|
||||||
|
m_curMs = 0;
|
||||||
|
m_sustainFactor = (adsr->sustainCoarse * 6.25 + adsr->sustainFine * 0.0244) / 100.0;
|
||||||
|
m_releaseStartFactor = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Envelope::keyOff()
|
||||||
|
{
|
||||||
|
m_phase = State::Release;
|
||||||
|
m_curMs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Envelope::nextSample(double sampleRate)
|
||||||
|
{
|
||||||
|
if (!m_curADSR)
|
||||||
|
{
|
||||||
|
if (m_phase == State::Release)
|
||||||
|
return 0.f;
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_curMs += 1.0 / (sampleRate / 1000.0);
|
||||||
|
|
||||||
|
switch (m_phase)
|
||||||
|
{
|
||||||
|
case State::Attack:
|
||||||
|
{
|
||||||
|
double attackFac = m_curMs / (m_curADSR->attackCoarse * 255 + m_curADSR->attackFine);
|
||||||
|
if (attackFac >= 1.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Decay;
|
||||||
|
m_curMs = 0;
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
|
m_releaseStartFactor = attackFac;
|
||||||
|
return attackFac;
|
||||||
|
}
|
||||||
|
case State::Decay:
|
||||||
|
{
|
||||||
|
double decayFac = m_curMs / (m_curADSR->decayCoarse * 255 + m_curADSR->decayFine);
|
||||||
|
if (decayFac >= 1.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Sustain;
|
||||||
|
m_curMs = 0;
|
||||||
|
m_releaseStartFactor = m_sustainFactor;
|
||||||
|
return m_sustainFactor;
|
||||||
|
}
|
||||||
|
m_releaseStartFactor = (1.0 - decayFac) + decayFac * m_sustainFactor;
|
||||||
|
return m_releaseStartFactor;
|
||||||
|
}
|
||||||
|
case State::Sustain:
|
||||||
|
{
|
||||||
|
return m_sustainFactor;
|
||||||
|
}
|
||||||
|
case State::Release:
|
||||||
|
{
|
||||||
|
double releaseFac = m_curMs / (m_curADSR->releaseCoarse * 255 + m_curADSR->releaseFine);
|
||||||
|
if (releaseFac >= 1.0)
|
||||||
|
return 0.f;
|
||||||
|
return (1.0 - releaseFac) * m_releaseStartFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -52,17 +52,17 @@ float SoundMacroState::Evaluator::evaluate(Voice& vox, const SoundMacroState& st
|
||||||
break;
|
break;
|
||||||
case 130:
|
case 130:
|
||||||
/* LFO1 */
|
/* LFO1 */
|
||||||
if (st.m_lfoPeriods[0])
|
if (vox.m_lfoPeriods[0])
|
||||||
thisValue = std::sin(st.m_execTime / st.m_lfoPeriods[0] * 2.f * M_PIF);
|
thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF);
|
||||||
break;
|
break;
|
||||||
case 131:
|
case 131:
|
||||||
/* LFO2 */
|
/* LFO2 */
|
||||||
if (st.m_lfoPeriods[1])
|
if (vox.m_lfoPeriods[1])
|
||||||
thisValue = std::sin(st.m_execTime / st.m_lfoPeriods[1] * 2.f * M_PIF);
|
thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF);
|
||||||
break;
|
break;
|
||||||
case 132:
|
case 132:
|
||||||
/* Surround panning */
|
/* Surround panning */
|
||||||
thisValue = st.m_curSpan * 64.f + 64.f;
|
thisValue = vox.m_curSpan * 64.f + 64.f;
|
||||||
break;
|
break;
|
||||||
case 133:
|
case 133:
|
||||||
/* Macro-starting key */
|
/* Macro-starting key */
|
||||||
|
@ -110,50 +110,30 @@ float SoundMacroState::Evaluator::evaluate(Voice& vox, const SoundMacroState& st
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundMacroState::initialize(const unsigned char* ptr)
|
void SoundMacroState::initialize(const unsigned char* ptr, int step)
|
||||||
{
|
{
|
||||||
initialize(ptr, 1000.f, 0, 0, 0);
|
initialize(ptr, step, 1000.f, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundMacroState::initialize(const unsigned char* ptr, float ticksPerSec,
|
void SoundMacroState::initialize(const unsigned char* ptr, int step, float ticksPerSec,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod)
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod)
|
||||||
{
|
{
|
||||||
m_curVol = 1.f;
|
|
||||||
m_volDirty = true;
|
|
||||||
m_curPan = 0.f;
|
|
||||||
m_panDirty = true;
|
|
||||||
m_curSpan = 0.f;
|
|
||||||
m_spanDirty = true;
|
|
||||||
m_ticksPerSec = ticksPerSec;
|
m_ticksPerSec = ticksPerSec;
|
||||||
m_initKey = 0;
|
m_initKey = midiKey;
|
||||||
m_initVel = 0;
|
m_initVel = midiVel;
|
||||||
m_initMod = 0;
|
m_initMod = midiMod;
|
||||||
m_curVel = 0;
|
m_curVel = 0;
|
||||||
m_curMod = 0;
|
m_curMod = 0;
|
||||||
m_curKey = 0;
|
m_curKey = 0;
|
||||||
m_pitchSweep1 = 0;
|
|
||||||
m_pitchSweep1Times = 0;
|
|
||||||
m_pitchSweep2 = 0;
|
|
||||||
m_pitchSweep2Times = 0;
|
|
||||||
m_pitchDirty = true;
|
|
||||||
m_pc.clear();
|
m_pc.clear();
|
||||||
m_pc.push_back({ptr, 0});
|
m_pc.push_back({ptr, step});
|
||||||
m_execTime = 0.f;
|
m_execTime = 0.f;
|
||||||
m_keyoff = false;
|
m_keyoff = false;
|
||||||
m_sampleEnd = false;
|
m_sampleEnd = false;
|
||||||
m_envelopeTime = -1.f;
|
|
||||||
m_panningTime = -1.f;
|
|
||||||
m_loopCountdown = -1;
|
m_loopCountdown = -1;
|
||||||
m_lastPlayMacroVid = -1;
|
m_lastPlayMacroVid = -1;
|
||||||
m_useAdsrControllers = false;
|
m_useAdsrControllers = false;
|
||||||
m_portamentoMode = 0;
|
m_portamentoMode = 0;
|
||||||
m_vibratoLevel = 0;
|
|
||||||
m_vibratoModLevel = 0;
|
|
||||||
m_vibratoPeriod = 0.f;
|
|
||||||
m_tremoloScale = 0.f;
|
|
||||||
m_tremoloModScale = 0.f;
|
|
||||||
m_lfoPeriods[0] = 0.f;
|
|
||||||
m_lfoPeriods[1] = 0.f;
|
|
||||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||||
m_header.swapBig();
|
m_header.swapBig();
|
||||||
}
|
}
|
||||||
|
@ -164,107 +144,6 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (m_pc.empty() || m_pc.back().first == nullptr || m_pc.back().second == -1)
|
if (m_pc.empty() || m_pc.back().first == nullptr || m_pc.back().second == -1)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Process active envelope */
|
|
||||||
if (m_envelopeTime >= 0.f)
|
|
||||||
{
|
|
||||||
m_envelopeTime += dt;
|
|
||||||
float start = m_envelopeStart / 127.f;
|
|
||||||
float end = m_envelopeEnd / 127.f;
|
|
||||||
float t = std::max(0.f, std::min(1.f, m_envelopeTime / m_envelopeDur));
|
|
||||||
if (m_envelopeCurve)
|
|
||||||
t = (*m_envelopeCurve)[int(t*127.f)] / 127.f;
|
|
||||||
m_curVol = (start * (1.0f - t)) + (end * t);
|
|
||||||
m_volDirty = true;
|
|
||||||
|
|
||||||
/* Done with envelope */
|
|
||||||
if (m_envelopeTime > m_envelopeDur)
|
|
||||||
m_envelopeTime = -1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply tremolo */
|
|
||||||
float totalVol = m_curVol;
|
|
||||||
if (m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
|
|
||||||
{
|
|
||||||
float t = m_tremoloSel.evaluate(vox, *this);
|
|
||||||
if (m_tremoloScale && m_tremoloModScale)
|
|
||||||
{
|
|
||||||
float fac = (1.0f - t) + (m_tremoloScale * t);
|
|
||||||
float modT = vox.getModWheel() / 127.f;
|
|
||||||
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
|
||||||
totalVol *= fac * modFac;
|
|
||||||
}
|
|
||||||
else if (m_tremoloScale)
|
|
||||||
{
|
|
||||||
float fac = (1.0f - t) + (m_tremoloScale * t);
|
|
||||||
totalVol *= fac;
|
|
||||||
}
|
|
||||||
else if (m_tremoloModScale)
|
|
||||||
{
|
|
||||||
float modT = vox.getModWheel() / 127.f;
|
|
||||||
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
|
||||||
totalVol *= modFac;
|
|
||||||
}
|
|
||||||
m_volDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply total volume */
|
|
||||||
if (m_volDirty)
|
|
||||||
{
|
|
||||||
vox.setVolume(totalVol);
|
|
||||||
m_volDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process active pan-sweep */
|
|
||||||
if (m_panningTime >= 0.f)
|
|
||||||
{
|
|
||||||
m_panningTime += dt;
|
|
||||||
float start = (m_panPos - 64) / 64.f;
|
|
||||||
float end = (m_panPos + m_panWidth - 64) / 64.f;
|
|
||||||
float t = std::max(0.f, std::min(1.f, m_panningTime / m_panningDur));
|
|
||||||
vox.setPanning((start * (1.0f - t)) + (end * t));
|
|
||||||
|
|
||||||
/* Done with panning */
|
|
||||||
if (m_panningTime > m_panningDur)
|
|
||||||
m_panningTime = -1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process active span-sweep */
|
|
||||||
if (m_spanningTime >= 0.f)
|
|
||||||
{
|
|
||||||
m_spanningTime += dt;
|
|
||||||
float start = (m_spanPos - 64) / 64.f;
|
|
||||||
float end = (m_spanPos + m_spanWidth - 64) / 64.f;
|
|
||||||
float t = std::max(0.f, std::min(1.f, m_spanningTime / m_spanningDur));
|
|
||||||
vox.setSurroundPanning((start * (1.0f - t)) + (end * t));
|
|
||||||
|
|
||||||
/* Done with spanning */
|
|
||||||
if (m_spanningTime > m_spanningDur)
|
|
||||||
m_spanningTime = -1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process pitch sweep 1 */
|
|
||||||
if (m_pitchSweep1Times)
|
|
||||||
{
|
|
||||||
m_pitchSweep1 += m_pitchSweep1Add;
|
|
||||||
--m_pitchSweep1Times;
|
|
||||||
m_pitchDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process pitch sweep 2 */
|
|
||||||
if (m_pitchSweep2Times)
|
|
||||||
{
|
|
||||||
m_pitchSweep2 += m_pitchSweep2Add;
|
|
||||||
--m_pitchSweep2Times;
|
|
||||||
m_pitchDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply total pitch */
|
|
||||||
if (m_pitchDirty)
|
|
||||||
{
|
|
||||||
vox.setPitchKey(m_curKey + m_pitchSweep1 + m_pitchSweep2);
|
|
||||||
m_pitchDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loop through as many commands as we can for this time period */
|
/* Loop through as many commands as we can for this time period */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -305,7 +184,8 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep);
|
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
||||||
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -322,7 +202,8 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep);
|
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
||||||
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -398,7 +279,8 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep);
|
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
||||||
|
m_initKey, m_initVel, m_initMod);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -479,7 +361,8 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep);
|
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
||||||
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -492,7 +375,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
|
|
||||||
int32_t pan = int32_t(m_initKey - cenKey) * scale / 127 + cenPan;
|
int32_t pan = int32_t(m_initKey - cenKey) * scale / 127 + cenPan;
|
||||||
pan = std::max(-127, std::min(127, pan));
|
pan = std::max(-127, std::min(127, pan));
|
||||||
vox.setPanning(pan / 127.f);
|
vox.setPan(pan / 127.f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::SetAdsr:
|
case Op::SetAdsr:
|
||||||
|
@ -516,14 +399,12 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
if (curveData)
|
if (curveData)
|
||||||
{
|
{
|
||||||
m_curVol = (*curveData)[eval] / 127.f;
|
vox.setVolume((*curveData)[eval] / 127.f);
|
||||||
m_volDirty = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_curVol = eval / 127.f;
|
vox.setVolume(eval / 127.f);
|
||||||
m_volDirty = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Panning:
|
case Op::Panning:
|
||||||
|
@ -532,11 +413,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
||||||
int8_t width = cmd.m_data[3];
|
int8_t width = cmd.m_data[3];
|
||||||
|
|
||||||
m_panningTime = 0.f;
|
vox.startPanning(timeMs / 1000.0, panPos, width);
|
||||||
m_panningDur = timeMs / 1000.f;
|
|
||||||
m_panPos = panPos;
|
|
||||||
m_panWidth = width;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Envelope:
|
case Op::Envelope:
|
||||||
|
@ -547,22 +424,19 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
bool ms = cmd.m_data[4];
|
bool ms = cmd.m_data[4];
|
||||||
int16_t fadeTime = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
int16_t fadeTime = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
||||||
|
|
||||||
float q = ms ? 1000.f : m_ticksPerSec;
|
double q = ms ? 1000.0 : m_ticksPerSec;
|
||||||
float secTime = fadeTime / q;
|
double secTime = fadeTime / q;
|
||||||
|
|
||||||
int32_t eval = int32_t(m_curVel) * scale / 127 + add;
|
int32_t eval = int32_t(m_curVel) * scale / 127 + add;
|
||||||
eval = std::max(0, std::min(127, eval));
|
eval = std::max(0, std::min(127, eval));
|
||||||
|
|
||||||
m_envelopeTime = 0.f;
|
const Curve* curveData;
|
||||||
m_envelopeDur = secTime;
|
|
||||||
m_envelopeStart = m_curVel;
|
|
||||||
m_envelopeEnd = eval;
|
|
||||||
|
|
||||||
if (curve != 0)
|
if (curve != 0)
|
||||||
m_envelopeCurve = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
else
|
else
|
||||||
m_envelopeCurve = nullptr;
|
curveData = nullptr;
|
||||||
|
|
||||||
|
vox.startEnvelope(secTime, eval, curveData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::StartSample:
|
case Op::StartSample:
|
||||||
|
@ -608,7 +482,8 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = macroStep;
|
m_pc.back().second = macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep);
|
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
||||||
|
m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -627,16 +502,13 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
int32_t eval = int32_t(m_curVel) * scale / 127 + add;
|
int32_t eval = int32_t(m_curVel) * scale / 127 + add;
|
||||||
eval = std::max(0, std::min(127, eval));
|
eval = std::max(0, std::min(127, eval));
|
||||||
|
|
||||||
m_envelopeTime = 0.f;
|
const Curve* curveData;
|
||||||
m_envelopeDur = secTime;
|
|
||||||
m_envelopeStart = 0.f;
|
|
||||||
m_envelopeEnd = eval;
|
|
||||||
|
|
||||||
if (curve != 0)
|
if (curve != 0)
|
||||||
m_envelopeCurve = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
curveData = vox.getAudioGroup().getPool().tableAsCurves(curve);
|
||||||
else
|
else
|
||||||
m_envelopeCurve = nullptr;
|
curveData = nullptr;
|
||||||
|
|
||||||
|
vox.startFadeIn(secTime, eval, curveData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Spanning:
|
case Op::Spanning:
|
||||||
|
@ -645,11 +517,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
||||||
int8_t width = cmd.m_data[3];
|
int8_t width = cmd.m_data[3];
|
||||||
|
|
||||||
m_spanningTime = 0.f;
|
vox.startSpanning(timeMs / 1000.0, panPos, width);
|
||||||
m_spanningDur = timeMs / 1000.f;
|
|
||||||
m_spanPos = panPos;
|
|
||||||
m_spanWidth = width;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::SetAdsrCtrl:
|
case Op::SetAdsrCtrl:
|
||||||
|
@ -682,7 +550,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (!free)
|
if (!free)
|
||||||
m_curKey = m_curKey / 100 * 100 + detune;
|
m_curKey = m_curKey / 100 * 100 + detune;
|
||||||
|
|
||||||
m_pitchDirty = true;
|
vox.setPitchKey(m_curKey);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::AddNote:
|
case Op::AddNote:
|
||||||
|
@ -704,7 +572,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_inWait = true;
|
m_inWait = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pitchDirty = true;
|
vox.setPitchKey(m_curKey);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::SetNote:
|
case Op::SetNote:
|
||||||
|
@ -725,7 +593,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_inWait = true;
|
m_inWait = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pitchDirty = true;
|
vox.setPitchKey(m_curKey);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::LastNote:
|
case Op::LastNote:
|
||||||
|
@ -746,7 +614,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_inWait = true;
|
m_inWait = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pitchDirty = true;
|
vox.setPitchKey(m_curKey);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Portamento:
|
case Op::Portamento:
|
||||||
|
@ -762,20 +630,19 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
}
|
}
|
||||||
case Op::Vibrato:
|
case Op::Vibrato:
|
||||||
{
|
{
|
||||||
m_vibratoModLevel = m_vibratoLevel = cmd.m_data[0] * 100 + cmd.m_data[1];
|
int32_t level = cmd.m_data[0] * 100 + cmd.m_data[1];
|
||||||
m_vibratoModWheel = cmd.m_data[2];
|
int32_t modLevel = cmd.m_data[2];
|
||||||
int8_t ms = cmd.m_data[4];
|
int8_t ms = cmd.m_data[4];
|
||||||
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
||||||
|
|
||||||
float q = ms ? 1000.f : m_ticksPerSec;
|
float q = ms ? 1000.f : m_ticksPerSec;
|
||||||
m_vibratoPeriod = timeMs / q;
|
vox.setVibrato(level, modLevel, timeMs / q);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::PitchSweep1:
|
case Op::PitchSweep1:
|
||||||
{
|
{
|
||||||
m_pitchSweep1 = 0;
|
int32_t times = int32_t(cmd.m_data[0]);
|
||||||
m_pitchSweep1Times = int32_t(cmd.m_data[0]);
|
int16_t add = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
||||||
m_pitchSweep1Add = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
|
||||||
|
|
||||||
int8_t ms = cmd.m_data[4];
|
int8_t ms = cmd.m_data[4];
|
||||||
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
||||||
|
@ -789,13 +656,13 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_inWait = true;
|
m_inWait = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vox.setPitchSweep1(times, add);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::PitchSweep2:
|
case Op::PitchSweep2:
|
||||||
{
|
{
|
||||||
m_pitchSweep2 = 0;
|
int32_t times = int32_t(cmd.m_data[0]);
|
||||||
m_pitchSweep2Times = int32_t(cmd.m_data[0]);
|
int16_t add = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
||||||
m_pitchSweep2Add = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
|
||||||
|
|
||||||
int8_t ms = cmd.m_data[4];
|
int8_t ms = cmd.m_data[4];
|
||||||
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
||||||
|
@ -809,6 +676,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
m_inWait = true;
|
m_inWait = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vox.setPitchSweep2(times, add);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::SetPitch:
|
case Op::SetPitch:
|
||||||
|
@ -830,23 +698,21 @@ bool SoundMacroState::advance(Voice& vox, float 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];
|
||||||
m_curVol = int32_t(orgVel ? m_initVel : m_curVel) * scale / 4096.f / 127.f;
|
vox.setVolume(int32_t(orgVel ? m_initVel : m_curVel) * scale / 4096.f / 127.f);
|
||||||
m_volDirty = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Mod2Vibrange:
|
case Op::Mod2Vibrange:
|
||||||
{
|
{
|
||||||
int8_t keys = cmd.m_data[0];
|
int8_t keys = cmd.m_data[0];
|
||||||
int8_t cents = cmd.m_data[1];
|
int8_t cents = cmd.m_data[1];
|
||||||
m_vibratoModLevel = keys * 100 + cents;
|
vox.setMod2VibratoRange(keys * 100 + cents);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::SetupTremolo:
|
case Op::SetupTremolo:
|
||||||
{
|
{
|
||||||
int16_t scale = *reinterpret_cast<int16_t*>(&cmd.m_data[0]);
|
int16_t scale = *reinterpret_cast<int16_t*>(&cmd.m_data[0]);
|
||||||
int16_t modScale = *reinterpret_cast<int16_t*>(&cmd.m_data[3]);
|
int16_t modScale = *reinterpret_cast<int16_t*>(&cmd.m_data[3]);
|
||||||
m_tremoloScale = scale / 4096.f;
|
vox.setTremolo(scale / 4096.f, modScale / 4096.f);
|
||||||
m_tremoloModScale = modScale / 4096.f;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::Return:
|
case Op::Return:
|
||||||
|
@ -868,7 +734,8 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
if (macroId == m_header.m_macroId)
|
if (macroId == m_header.m_macroId)
|
||||||
m_pc.push_back({m_pc.back().first, macroStep});
|
m_pc.push_back({m_pc.back().first, macroStep});
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(macroId, macroStep, true);
|
vox.loadSoundMacro(macroId, macroStep, m_ticksPerSec,
|
||||||
|
m_initKey, m_initVel, m_initMod, true);
|
||||||
|
|
||||||
m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
|
m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
|
||||||
m_header.swapBig();
|
m_header.swapBig();
|
||||||
|
@ -1128,8 +995,10 @@ bool SoundMacroState::advance(Voice& vox, float dt)
|
||||||
{
|
{
|
||||||
uint8_t number = cmd.m_data[0];
|
uint8_t number = cmd.m_data[0];
|
||||||
int16_t period = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
int16_t period = *reinterpret_cast<int16_t*>(&cmd.m_data[1]);
|
||||||
if (number <= 1)
|
if (number == 0)
|
||||||
m_lfoPeriods[number] = period / 1000.f;
|
vox.setLFO1Period(period / 1000.f);
|
||||||
|
else if (number == 1)
|
||||||
|
vox.setLFO2Period(period / 1000.f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Op::SetKeygroup:
|
case Op::SetKeygroup:
|
||||||
|
@ -1338,7 +1207,8 @@ void SoundMacroState::keyoffNotify(Voice& vox)
|
||||||
if (m_keyoffTrap.macroId == m_header.m_macroId)
|
if (m_keyoffTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_keyoffTrap.macroStep;
|
m_pc.back().second = m_keyoffTrap.macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(m_keyoffTrap.macroId, m_keyoffTrap.macroStep);
|
vox.loadSoundMacro(m_keyoffTrap.macroId, m_keyoffTrap.macroStep,
|
||||||
|
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1353,7 +1223,8 @@ void SoundMacroState::sampleEndNotify(Voice& vox)
|
||||||
if (m_sampleEndTrap.macroId == m_header.m_macroId)
|
if (m_sampleEndTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_sampleEndTrap.macroStep;
|
m_pc.back().second = m_sampleEndTrap.macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep);
|
vox.loadSoundMacro(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep,
|
||||||
|
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1366,7 +1237,8 @@ void SoundMacroState::messageNotify(Voice& vox, int32_t val)
|
||||||
if (m_messageTrap.macroId == m_header.m_macroId)
|
if (m_messageTrap.macroId == m_header.m_macroId)
|
||||||
m_pc.back().second = m_messageTrap.macroStep;
|
m_pc.back().second = m_messageTrap.macroStep;
|
||||||
else
|
else
|
||||||
vox.loadSoundMacro(m_messageTrap.macroId, m_messageTrap.macroStep);
|
vox.loadSoundMacro(m_messageTrap.macroId, m_messageTrap.macroStep,
|
||||||
|
m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
316
lib/Voice.cpp
316
lib/Voice.cpp
|
@ -1,8 +1,12 @@
|
||||||
#include "amuse/Voice.hpp"
|
#include "amuse/Voice.hpp"
|
||||||
#include "amuse/Submix.hpp"
|
#include "amuse/Submix.hpp"
|
||||||
#include "amuse/IBackendVoice.hpp"
|
#include "amuse/IBackendVoice.hpp"
|
||||||
|
#include "amuse/IBackendVoiceAllocator.hpp"
|
||||||
#include "amuse/AudioGroup.hpp"
|
#include "amuse/AudioGroup.hpp"
|
||||||
|
#include "amuse/Common.hpp"
|
||||||
|
#include "amuse/Engine.hpp"
|
||||||
#include "amuse/dsp.h"
|
#include "amuse/dsp.h"
|
||||||
|
#include <cmath>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
|
@ -29,6 +33,27 @@ Voice::Voice(Engine& engine, const AudioGroup& group, ObjectId oid, int vid, boo
|
||||||
m_submix->m_activeVoices.insert(this);
|
m_submix->m_activeVoices.insert(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::_reset()
|
||||||
|
{
|
||||||
|
m_curVol = 1.f;
|
||||||
|
m_curReverbVol = 0.f;
|
||||||
|
m_curPan = 0.f;
|
||||||
|
m_curSpan = 0.f;
|
||||||
|
m_pitchSweep1 = 0;
|
||||||
|
m_pitchSweep1Times = 0;
|
||||||
|
m_pitchSweep2 = 0;
|
||||||
|
m_pitchSweep2Times = 0;
|
||||||
|
m_envelopeTime = -1.f;
|
||||||
|
m_panningTime = -1.f;
|
||||||
|
m_vibratoLevel = 0;
|
||||||
|
m_vibratoModLevel = 0;
|
||||||
|
m_vibratoPeriod = 0.f;
|
||||||
|
m_tremoloScale = 0.f;
|
||||||
|
m_tremoloModScale = 0.f;
|
||||||
|
m_lfoPeriods[0] = 0.f;
|
||||||
|
m_lfoPeriods[1] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
bool Voice::_checkSamplePos()
|
bool Voice::_checkSamplePos()
|
||||||
{
|
{
|
||||||
if (m_curSamplePos >= m_lastSamplePos)
|
if (m_curSamplePos >= m_lastSamplePos)
|
||||||
|
@ -53,11 +78,157 @@ bool Voice::_checkSamplePos()
|
||||||
|
|
||||||
void Voice::_doKeyOff()
|
void Voice::_doKeyOff()
|
||||||
{
|
{
|
||||||
|
m_volAdsr.keyOff();
|
||||||
|
m_pitchAdsr.keyOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::_setTotalPitch(int32_t cents)
|
||||||
|
{
|
||||||
|
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
||||||
|
double ratio = std::pow(2.0, interval / 1200.0);
|
||||||
|
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
||||||
|
m_backendVoice->setPitchRatio(ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
Voice* Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
|
||||||
|
{
|
||||||
|
auto it = m_childVoices.emplace(m_childVoices.end(), m_engine, m_audioGroup,
|
||||||
|
m_engine.m_nextVid++, m_emitter, m_submix);
|
||||||
|
m_childVoices.back().m_backendVoice =
|
||||||
|
m_engine.getBackend().allocateVoice(m_childVoices.back(), sampleRate, dynamicPitch);
|
||||||
|
m_childVoices.back().m_engineIt = it;
|
||||||
|
return &m_childVoices.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Voice>::iterator Voice::_destroyVoice(Voice* voice)
|
||||||
|
{
|
||||||
|
voice->_destroy();
|
||||||
|
return m_childVoices.erase(voice->m_engineIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Voice::_advanceSample(int16_t& samp)
|
||||||
|
{
|
||||||
|
double dt = 1.0 / m_sampleRate;
|
||||||
|
m_voiceTime += dt;
|
||||||
|
bool refresh = false;
|
||||||
|
|
||||||
|
/* Process active envelope */
|
||||||
|
if (m_envelopeTime >= 0.0)
|
||||||
|
{
|
||||||
|
m_envelopeTime += dt;
|
||||||
|
float start = m_envelopeStart / 127.f;
|
||||||
|
float end = m_envelopeEnd / 127.f;
|
||||||
|
double t = std::max(0.0, std::min(1.0, m_envelopeTime / m_envelopeDur));
|
||||||
|
if (m_envelopeCurve)
|
||||||
|
t = (*m_envelopeCurve)[int(t*127.f)] / 127.f;
|
||||||
|
m_curVol = (start * (1.0f - t)) + (end * t);
|
||||||
|
|
||||||
|
/* Done with envelope */
|
||||||
|
if (m_envelopeTime > m_envelopeDur)
|
||||||
|
m_envelopeTime = -1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Factor in ADSR envelope state */
|
||||||
|
float totalVol = m_curVol * m_volAdsr.nextSample(m_sampleRate);
|
||||||
|
|
||||||
|
/* Apply tremolo */
|
||||||
|
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
|
||||||
|
{
|
||||||
|
float t = m_state.m_tremoloSel.evaluate(*this, m_state);
|
||||||
|
if (m_tremoloScale && m_tremoloModScale)
|
||||||
|
{
|
||||||
|
float fac = (1.0f - t) + (m_tremoloScale * t);
|
||||||
|
float modT = getModWheel() / 127.f;
|
||||||
|
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
||||||
|
totalVol *= fac * modFac;
|
||||||
|
}
|
||||||
|
else if (m_tremoloScale)
|
||||||
|
{
|
||||||
|
float fac = (1.0f - t) + (m_tremoloScale * t);
|
||||||
|
totalVol *= fac;
|
||||||
|
}
|
||||||
|
else if (m_tremoloModScale)
|
||||||
|
{
|
||||||
|
float modT = getModWheel() / 127.f;
|
||||||
|
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
||||||
|
totalVol *= modFac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Multiply sample with total volume */
|
||||||
|
samp = ClampFull<int16_t>(samp * totalVol);
|
||||||
|
|
||||||
|
/* Process active pan-sweep */
|
||||||
|
if (m_panningTime >= 0.f)
|
||||||
|
{
|
||||||
|
m_panningTime += dt;
|
||||||
|
float start = (m_panPos - 64) / 64.f;
|
||||||
|
float end = (m_panPos + m_panWidth - 64) / 64.f;
|
||||||
|
float t = std::max(0.f, std::min(1.f, m_panningTime / m_panningDur));
|
||||||
|
setPan((start * (1.0f - t)) + (end * t));
|
||||||
|
refresh = true;
|
||||||
|
|
||||||
|
/* Done with panning */
|
||||||
|
if (m_panningTime > m_panningDur)
|
||||||
|
m_panningTime = -1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process active span-sweep */
|
||||||
|
if (m_spanningTime >= 0.f)
|
||||||
|
{
|
||||||
|
m_spanningTime += dt;
|
||||||
|
float start = (m_spanPos - 64) / 64.f;
|
||||||
|
float end = (m_spanPos + m_spanWidth - 64) / 64.f;
|
||||||
|
float t = std::max(0.f, std::min(1.f, m_spanningTime / m_spanningDur));
|
||||||
|
setSurroundPan((start * (1.0f - t)) + (end * t));
|
||||||
|
refresh = true;
|
||||||
|
|
||||||
|
/* Done with spanning */
|
||||||
|
if (m_spanningTime > m_spanningDur)
|
||||||
|
m_spanningTime = -1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate total pitch */
|
||||||
|
int32_t totalPitch = m_curPitch;
|
||||||
|
bool pitchDirty = m_pitchDirty;
|
||||||
|
m_pitchDirty = false;
|
||||||
|
if (m_pitchEnv)
|
||||||
|
{
|
||||||
|
totalPitch = m_curPitch * m_pitchAdsr.nextSample(m_sampleRate);
|
||||||
|
pitchDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process pitch sweep 1 */
|
||||||
|
if (m_pitchSweep1Times)
|
||||||
|
{
|
||||||
|
m_pitchSweep1 += m_pitchSweep1Add;
|
||||||
|
--m_pitchSweep1Times;
|
||||||
|
pitchDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process pitch sweep 2 */
|
||||||
|
if (m_pitchSweep2Times)
|
||||||
|
{
|
||||||
|
m_pitchSweep2 += m_pitchSweep2Add;
|
||||||
|
--m_pitchSweep2Times;
|
||||||
|
pitchDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply total pitch */
|
||||||
|
if (pitchDirty)
|
||||||
|
{
|
||||||
|
_setTotalPitch(totalPitch + m_pitchSweep1 + m_pitchSweep2);
|
||||||
|
refresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* True if backend voice needs reconfiguration before next sample */
|
||||||
|
return refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
{
|
{
|
||||||
uint32_t samplesRem = samples;
|
uint32_t samplesRem = samples;
|
||||||
|
size_t samplesProc = 0;
|
||||||
if (m_curSample)
|
if (m_curSample)
|
||||||
{
|
{
|
||||||
uint32_t block = m_curSamplePos / 14;
|
uint32_t block = m_curSamplePos / 14;
|
||||||
|
@ -70,7 +241,16 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
&m_prev1, &m_prev2, rem,
|
&m_prev1, &m_prev2, rem,
|
||||||
std::min(samplesRem,
|
std::min(samplesRem,
|
||||||
m_lastSamplePos - block * 14));
|
m_lastSamplePos - block * 14));
|
||||||
m_curSamplePos += decSamples;
|
|
||||||
|
/* Per-sample processing */
|
||||||
|
for (int i=0 ; i<decSamples ; ++i)
|
||||||
|
{
|
||||||
|
++samplesProc;
|
||||||
|
++m_curSamplePos;
|
||||||
|
if (_advanceSample(data[i]))
|
||||||
|
return samplesProc;
|
||||||
|
}
|
||||||
|
|
||||||
samplesRem -= decSamples;
|
samplesRem -= decSamples;
|
||||||
data += decSamples;
|
data += decSamples;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +270,16 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
&m_prev1, &m_prev2,
|
&m_prev1, &m_prev2,
|
||||||
std::min(samplesRem,
|
std::min(samplesRem,
|
||||||
m_lastSamplePos - block * 14));
|
m_lastSamplePos - block * 14));
|
||||||
m_curSamplePos += decSamples;
|
|
||||||
|
/* Per-sample processing */
|
||||||
|
for (int i=0 ; i<decSamples ; ++i)
|
||||||
|
{
|
||||||
|
++samplesProc;
|
||||||
|
++m_curSamplePos;
|
||||||
|
if (_advanceSample(data[i]))
|
||||||
|
return samplesProc;
|
||||||
|
}
|
||||||
|
|
||||||
samplesRem -= decSamples;
|
samplesRem -= decSamples;
|
||||||
data += decSamples;
|
data += decSamples;
|
||||||
|
|
||||||
|
@ -108,12 +297,42 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
Voice* Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
int Voice::maxVid() const
|
||||||
{
|
{
|
||||||
|
int maxVid = m_vid;
|
||||||
|
for (const Voice& vox : m_childVoices)
|
||||||
|
maxVid = std::max(maxVid, vox.maxVid());
|
||||||
|
return maxVid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, bool pushPc)
|
Voice* Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
|
||||||
{
|
{
|
||||||
|
Voice* vox = _allocateVoice(32000.0, true);
|
||||||
|
vox->loadSoundMacro(macroId, macroStep, 1000.f, m_state.m_initKey + addNote,
|
||||||
|
m_state.m_initVel, m_state.m_initMod);
|
||||||
|
return vox;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Voice::loadSoundMacro(ObjectId macroId, int macroStep, float ticksPerSec,
|
||||||
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||||
|
bool pushPc)
|
||||||
|
{
|
||||||
|
const unsigned char* macroData = m_audioGroup.getPool().soundMacro(macroId);
|
||||||
|
if (!macroData)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_state.m_pc.empty())
|
||||||
|
m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!pushPc)
|
||||||
|
m_state.m_pc.clear();
|
||||||
|
m_state.m_pc.push_back({macroData, macroStep});
|
||||||
|
m_state.m_header = *reinterpret_cast<const SoundMacroState::Header*>(macroData);
|
||||||
|
m_state.m_header.swapBig();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::keyOff()
|
void Voice::keyOff()
|
||||||
|
@ -133,6 +352,10 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
||||||
m_curSample = m_audioGroup.getSample(sampId);
|
m_curSample = m_audioGroup.getSample(sampId);
|
||||||
if (m_curSample)
|
if (m_curSample)
|
||||||
{
|
{
|
||||||
|
_reset();
|
||||||
|
m_sampleRate = m_curSample->first.m_sampleRate;
|
||||||
|
m_curPitch = m_curSample->first.m_pitch;
|
||||||
|
m_pitchDirty = true;
|
||||||
m_backendVoice->stop();
|
m_backendVoice->stop();
|
||||||
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
||||||
m_backendVoice->start();
|
m_backendVoice->start();
|
||||||
|
@ -154,18 +377,57 @@ void Voice::stopSample()
|
||||||
|
|
||||||
void Voice::setVolume(float vol)
|
void Voice::setVolume(float vol)
|
||||||
{
|
{
|
||||||
|
m_curVol = vol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPanning(float pan)
|
void Voice::setPan(float pan)
|
||||||
{
|
{
|
||||||
|
m_curPan = pan;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setSurroundPanning(float span)
|
void Voice::setSurroundPan(float span)
|
||||||
{
|
{
|
||||||
|
m_curSpan = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
||||||
|
{
|
||||||
|
m_envelopeTime = m_voiceTime;
|
||||||
|
m_envelopeDur = dur;
|
||||||
|
m_envelopeStart = m_curVol;
|
||||||
|
m_envelopeEnd = vol;
|
||||||
|
m_envelopeCurve = envCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
|
||||||
|
{
|
||||||
|
m_envelopeTime = m_voiceTime;
|
||||||
|
m_envelopeDur = dur;
|
||||||
|
m_envelopeStart = 0.f;
|
||||||
|
m_envelopeEnd = m_curVol;
|
||||||
|
m_envelopeCurve = envCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::startPanning(double dur, uint8_t panPos, uint8_t panWidth)
|
||||||
|
{
|
||||||
|
m_panningTime = m_voiceTime;
|
||||||
|
m_panningDur = dur;
|
||||||
|
m_panPos = panPos;
|
||||||
|
m_panWidth = panWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::startSpanning(double dur, uint8_t spanPos, uint8_t spanWidth)
|
||||||
|
{
|
||||||
|
m_spanningTime = m_voiceTime;
|
||||||
|
m_spanningDur = dur;
|
||||||
|
m_spanPos = spanPos;
|
||||||
|
m_spanWidth = spanWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPitchKey(int32_t cents)
|
void Voice::setPitchKey(int32_t cents)
|
||||||
{
|
{
|
||||||
|
m_curPitch = cents;
|
||||||
|
m_pitchDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setModulation(float mod)
|
void Voice::setModulation(float mod)
|
||||||
|
@ -186,20 +448,62 @@ void Voice::setDoppler(float doppler)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::setVibrato(int32_t level, int32_t modLevel, float period)
|
||||||
|
{
|
||||||
|
m_vibratoLevel = level;
|
||||||
|
m_vibratoModLevel = modLevel;
|
||||||
|
m_vibratoPeriod = period;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setMod2VibratoRange(int32_t modLevel)
|
||||||
|
{
|
||||||
|
m_vibratoModLevel = modLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setTremolo(float tremoloScale, float tremoloModScale)
|
||||||
|
{
|
||||||
|
m_tremoloScale = tremoloScale;
|
||||||
|
m_tremoloModScale = tremoloModScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchSweep1(uint8_t times, int16_t add)
|
||||||
|
{
|
||||||
|
m_pitchSweep1 = 0;
|
||||||
|
m_pitchSweep1Times = times;
|
||||||
|
m_pitchSweep1Add = add;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::setPitchSweep2(uint8_t times, int16_t add)
|
||||||
|
{
|
||||||
|
m_pitchSweep2 = 0;
|
||||||
|
m_pitchSweep2Times = times;
|
||||||
|
m_pitchSweep2Add = add;
|
||||||
|
}
|
||||||
|
|
||||||
void Voice::setReverbVol(float rvol)
|
void Voice::setReverbVol(float rvol)
|
||||||
{
|
{
|
||||||
|
m_curReverbVol = rvol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setAdsr(ObjectId adsrId)
|
void Voice::setAdsr(ObjectId adsrId)
|
||||||
{
|
{
|
||||||
|
const ADSR* adsr = m_audioGroup.getPool().tableAsAdsr(adsrId);
|
||||||
|
m_volAdsr.reset(adsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
|
void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
|
||||||
{
|
{
|
||||||
|
m_sampleRate = hz + fine / 65536.0;
|
||||||
|
m_backendVoice->setPitchRatio(1.0);
|
||||||
|
m_backendVoice->resetSampleRate(m_sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
|
||||||
{
|
{
|
||||||
|
const ADSR* adsr = m_audioGroup.getPool().tableAsAdsr(adsrId);
|
||||||
|
m_pitchAdsr.reset(adsr);
|
||||||
|
m_pitchEnvRange = cents;
|
||||||
|
m_pitchEnv = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
void Voice::setPitchWheelRange(int8_t up, int8_t down)
|
||||||
|
|
Loading…
Reference in New Issue