1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
|
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011-2016 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstddef>
#include "internals.h"
#include "Partial.h"
#include "Part.h"
#include "Poly.h"
#include "Synth.h"
#include "Tables.h"
#include "TVA.h"
#include "TVF.h"
#include "TVP.h"
namespace MT32Emu {
static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7};
static const Bit8u PAN_NUMERATOR_SLAVE[] = {0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7};
static const Bit32s PAN_FACTORS[] = {0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256};
Partial::Partial(Synth *useSynth, int useDebugPartialNum) :
synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) {
// Initialisation of tva, tvp and tvf uses 'this' pointer
// and thus should not be in the initializer list to avoid a compiler warning
tva = new TVA(this, &Ramp);
tvp = new TVP(this);
tvf = new TVF(this, &cutoffModifierRamp);
ownerPart = -1;
poly = NULL;
pair = NULL;
}
Partial::~Partial() {
delete tva;
delete tvp;
delete tvf;
}
// Only used for debugging purposes
int Partial::debugGetPartialNum() const {
return debugPartialNum;
}
// Only used for debugging purposes
Bit32u Partial::debugGetSampleNum() const {
return sampleNum;
}
int Partial::getOwnerPart() const {
return ownerPart;
}
bool Partial::isActive() const {
return ownerPart > -1;
}
const Poly *Partial::getPoly() const {
return poly;
}
void Partial::activate(int part) {
// This just marks the partial as being assigned to a part
ownerPart = part;
}
void Partial::deactivate() {
if (!isActive()) {
return;
}
ownerPart = -1;
if (poly != NULL) {
poly->partialDeactivated(this);
}
#if MT32EMU_MONITOR_PARTIALS > 2
synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum);
synth->printPartialUsage(sampleNum);
#endif
if (isRingModulatingSlave()) {
pair->la32Pair.deactivate(LA32PartialPair::SLAVE);
} else {
la32Pair.deactivate(LA32PartialPair::MASTER);
if (hasRingModulatingSlave()) {
pair->deactivate();
pair = NULL;
}
}
if (pair != NULL) {
pair->pair = NULL;
}
}
void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *usePatchCache, const MemParams::RhythmTemp *rhythmTemp, Partial *pairPartial) {
if (usePoly == NULL || usePatchCache == NULL) {
synth->printDebug("[Partial %d] *** Error: Starting partial for owner %d, usePoly=%s, usePatchCache=%s", debugPartialNum, ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", usePatchCache == NULL ? "*** NULL ***" : "OK");
return;
}
patchCache = usePatchCache;
poly = usePoly;
mixType = patchCache->structureMix;
structurePosition = patchCache->structurePosition;
Bit8u panSetting = rhythmTemp != NULL ? rhythmTemp->panpot : part->getPatchTemp()->panpot;
if (mixType == 3) {
if (structurePosition == 0) {
panSetting = PAN_NUMERATOR_MASTER[panSetting] << 1;
} else {
panSetting = PAN_NUMERATOR_SLAVE[panSetting] << 1;
}
// Do a normal mix independent of any pair partial.
mixType = 0;
pairPartial = NULL;
} else {
// Mok wanted an option for smoother panning, and we love Mok.
#ifndef INACCURATE_SMOOTH_PAN
// CONFIRMED by Mok: exactly bytes like this (right shifted?) are sent to the LA32.
panSetting &= 0x0E;
#endif
}
leftPanValue = synth->reversedStereoEnabled ? 14 - panSetting : panSetting;
rightPanValue = 14 - leftPanValue;
#if !MT32EMU_USE_FLOAT_SAMPLES
leftPanValue = PAN_FACTORS[leftPanValue];
rightPanValue = PAN_FACTORS[rightPanValue];
#endif
// SEMI-CONFIRMED: From sample analysis:
// Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways.
// Either partial pairs are added or subtracted, it depends on how the partial pairs are allocated.
// It seems that partials are grouped into quarters and if the partial pairs are allocated in different quarters the subtraction happens.
// Though, this matters little for the majority of timbres, it becomes crucial for timbres which contain several partials that sound very close.
// In this case that timbre can sound totally different depending of the way it is mixed up.
// Most easily this effect can be displayed with the help of a special timbre consisting of several identical square wave partials (3 or 4).
// Say, it is 3-partial timbre. Just play any two notes simultaneously and the polys very probably are mixed differently.
// Moreover, the partial allocator retains the last partial assignment it did and all the subsequent notes will sound the same as the last released one.
// The situation is better with 4-partial timbres since then a whole quarter is assigned for each poly. However, if a 3-partial timbre broke the normal
// whole-quarter assignment or after some partials got aborted, even 4-partial timbres can be found sounding differently.
// This behaviour is also confirmed with two more special timbres: one with identical sawtooth partials, and one with PCM wave 02.
// For my personal taste, this behaviour rather enriches the sounding and should be emulated.
// Also, the current partial allocator model probably needs to be refined.
if (debugPartialNum & 8) {
leftPanValue = -leftPanValue;
rightPanValue = -rightPanValue;
}
if (patchCache->PCMPartial) {
pcmNum = patchCache->pcm;
if (synth->controlROMMap->pcmCount > 128) {
// CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
if (patchCache->waveform > 1) {
pcmNum += 128;
}
}
pcmWave = &synth->pcmWaves[pcmNum];
} else {
pcmWave = NULL;
}
// CONFIRMED: pulseWidthVal calculation is based on information from Mok
pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + Tables::getInstance().pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth];
if (pulseWidthVal < 0) {
pulseWidthVal = 0;
} else if (pulseWidthVal > 255) {
pulseWidthVal = 255;
}
pair = pairPartial;
alreadyOutputed = false;
tva->reset(part, patchCache->partialParam, rhythmTemp);
tvp->reset(part, patchCache->partialParam);
tvf->reset(patchCache->partialParam, tvp->getBasePitch());
LA32PartialPair::PairType pairType;
LA32PartialPair *useLA32Pair;
if (isRingModulatingSlave()) {
pairType = LA32PartialPair::SLAVE;
useLA32Pair = &pair->la32Pair;
} else {
pairType = LA32PartialPair::MASTER;
la32Pair.init(hasRingModulatingSlave(), mixType == 1);
useLA32Pair = &la32Pair;
}
if (isPCM()) {
useLA32Pair->initPCM(pairType, &synth->pcmROMData[pcmWave->addr], pcmWave->len, pcmWave->loop);
} else {
useLA32Pair->initSynth(pairType, (patchCache->waveform & 1) != 0, pulseWidthVal, patchCache->srcPartial.tvf.resonance + 1);
}
if (!hasRingModulatingSlave()) {
la32Pair.deactivate(LA32PartialPair::SLAVE);
}
}
Bit32u Partial::getAmpValue() {
// SEMI-CONFIRMED: From sample analysis:
// (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc.
// This gives results within +/- 2 at the output (before any DAC bitshifting)
// when sustaining at levels 156 - 255 with no modifiers.
// (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255.
// This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces
// positive amps, so negative still needs to be explored, as well as lower levels.
//
// Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing.
// TODO: The tests above were performed using the float model, to be refined
Bit32u ampRampVal = 67117056 - ampRamp.nextValue();
if (ampRamp.checkInterrupt()) {
tva->handleInterrupt();
}
return ampRampVal;
}
Bit32u Partial::getCutoffValue() {
if (isPCM()) {
return 0;
}
Bit32u cutoffModifierRampVal = cutoffModifierRamp.nextValue();
if (cutoffModifierRamp.checkInterrupt()) {
tvf->handleInterrupt();
}
return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal;
}
bool Partial::hasRingModulatingSlave() const {
return pair != NULL && structurePosition == 0 && (mixType == 1 || mixType == 2);
}
bool Partial::isRingModulatingSlave() const {
return pair != NULL && structurePosition == 1 && (mixType == 1 || mixType == 2);
}
bool Partial::isPCM() const {
return pcmWave != NULL;
}
const ControlROMPCMStruct *Partial::getControlROMPCMStruct() const {
if (pcmWave != NULL) {
return pcmWave->controlROMPCMStruct;
}
return NULL;
}
Synth *Partial::getSynth() const {
return synth;
}
TVA *Partial::getTVA() const {
return tva;
}
void Partial::backupCache(const PatchCache &cache) {
if (patchCache == &cache) {
cachebackup = cache;
patchCache = &cachebackup;
}
}
bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, Bit32u length) {
if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
return false;
}
if (poly == NULL) {
synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum);
return false;
}
alreadyOutputed = true;
for (sampleNum = 0; sampleNum < length; sampleNum++) {
if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) {
deactivate();
break;
}
la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue());
if (hasRingModulatingSlave()) {
la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue());
if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) {
pair->deactivate();
if (mixType == 2) {
deactivate();
break;
}
}
}
// Although, LA32 applies panning itself, we assume here it is applied in the mixer, not within a pair.
// Applying the pan value in the log-space looks like a waste of unlog resources. Though, it needs clarification.
Sample sample = la32Pair.nextOutSample();
// FIXME: Sample analysis suggests that the use of panVal is linear, but there are some quirks that still need to be resolved.
#if MT32EMU_USE_FLOAT_SAMPLES
Sample leftOut = (sample * (float)leftPanValue) / 14.0f;
Sample rightOut = (sample * (float)rightPanValue) / 14.0f;
*(leftBuf++) += leftOut;
*(rightBuf++) += rightOut;
#else
// FIXME: Dividing by 7 (or by 14 in a Mok-friendly way) looks of course pointless. Need clarification.
// FIXME2: LA32 may produce distorted sound in case if the absolute value of maximal amplitude of the input exceeds 8191
// when the panning value is non-zero. Most probably the distortion occurs in the same way it does with ring modulation,
// and it seems to be caused by limited precision of the common multiplication circuit.
// From analysis of this overflow, it is obvious that the right channel output is actually found
// by subtraction of the left channel output from the input.
// Though, it is unknown whether this overflow is exploited somewhere.
Sample leftOut = Sample((sample * leftPanValue) >> 8);
Sample rightOut = Sample((sample * rightPanValue) >> 8);
*leftBuf = Synth::clipSampleEx(SampleEx(*leftBuf) + SampleEx(leftOut));
*rightBuf = Synth::clipSampleEx(SampleEx(*rightBuf) + SampleEx(rightOut));
leftBuf++;
rightBuf++;
#endif
}
sampleNum = 0;
return true;
}
bool Partial::shouldReverb() {
if (!isActive()) {
return false;
}
return patchCache->reverb;
}
void Partial::startAbort() {
// This is called when the partial manager needs to terminate partials for re-use by a new Poly.
tva->startAbort();
}
void Partial::startDecayAll() {
tva->startDecay();
tvp->startDecay();
tvf->startDecay();
}
} // namespace MT32Emu
|