diff options
Diffstat (limited to 'audio/softsynth/mt32/PartialManager.cpp')
| -rw-r--r-- | audio/softsynth/mt32/PartialManager.cpp | 250 | 
1 files changed, 250 insertions, 0 deletions
diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp new file mode 100644 index 0000000000..42a3eaa179 --- /dev/null +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -0,0 +1,250 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 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 <cstring> + +#include "mt32emu.h" +#include "PartialManager.h" + +using namespace MT32Emu; + +PartialManager::PartialManager(Synth *useSynth, Part **useParts) { +	synth = useSynth; +	parts = useParts; +	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		partialTable[i] = new Partial(synth, i); +	} +} + +PartialManager::~PartialManager(void) { +	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		delete partialTable[i]; +	} +} + +void PartialManager::clearAlreadyOutputed() { +	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		partialTable[i]->alreadyOutputed = false; +	} +} + +bool PartialManager::shouldReverb(int i) { +	return partialTable[i]->shouldReverb(); +} + +bool PartialManager::produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength) { +	return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength); +} + +void PartialManager::deactivateAll() { +	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		partialTable[i]->deactivate(); +	} +} + +unsigned int PartialManager::setReserve(Bit8u *rset) { +	unsigned int pr = 0; +	for (int x = 0; x <= 8; x++) { +		numReservedPartialsForPart[x] = rset[x]; +		pr += rset[x]; +	} +	return pr; +} + +Partial *PartialManager::allocPartial(int partNum) { +	Partial *outPartial = NULL; + +	// Get the first inactive partial +	for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) { +		if (!partialTable[partialNum]->isActive()) { +			outPartial = partialTable[partialNum]; +			break; +		} +	} +	if (outPartial != NULL) { +		outPartial->activate(partNum); +	} +	return outPartial; +} + +unsigned int PartialManager::getFreePartialCount(void) { +	int count = 0; +	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		if (!partialTable[i]->isActive()) { +			count++; +		} +	} +	return count; +} + +// This function is solely used to gather data for debug output at the moment. +void PartialManager::getPerPartPartialUsage(unsigned int perPartPartialUsage[9]) { +	memset(perPartPartialUsage, 0, 9 * sizeof(unsigned int)); +	for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { +		if (partialTable[i]->isActive()) { +			perPartPartialUsage[partialTable[i]->getOwnerPart()]++; +		} +	} +} + +// Finds the lowest-priority part that is exceeding its reserved partial allocation and has a poly +// in POLY_Releasing, then kills its first releasing poly. +// Parts with higher priority than minPart are not checked. +// Assumes that getFreePartials() has been called to make numReservedPartialsForPart up-to-date. +bool PartialManager::abortFirstReleasingPolyWhereReserveExceeded(int minPart) { +	if (minPart == 8) { +		// Rhythm is highest priority +		minPart = -1; +	} +	for (int partNum = 7; partNum >= minPart; partNum--) { +		int usePartNum = partNum == -1 ? 8 : partNum; +		if (parts[usePartNum]->getActivePartialCount() > numReservedPartialsForPart[usePartNum]) { +			// This part has exceeded its reserved partial count. +			// If it has any releasing polys, kill its first one and we're done. +			if (parts[usePartNum]->abortFirstPoly(POLY_Releasing)) { +				return true; +			} +		} +	} +	return false; +} + +// Finds the lowest-priority part that is exceeding its reserved partial allocation and has a poly, then kills +// its first poly in POLY_Held - or failing that, its first poly in any state. +// Parts with higher priority than minPart are not checked. +// Assumes that getFreePartials() has been called to make numReservedPartialsForPart up-to-date. +bool PartialManager::abortFirstPolyPreferHeldWhereReserveExceeded(int minPart) { +	if (minPart == 8) { +		// Rhythm is highest priority +		minPart = -1; +	} +	for (int partNum = 7; partNum >= minPart; partNum--) { +		int usePartNum = partNum == -1 ? 8 : partNum; +		if (parts[usePartNum]->getActivePartialCount() > numReservedPartialsForPart[usePartNum]) { +			// This part has exceeded its reserved partial count. +			// If it has any polys, kill its first (preferably held) one and we're done. +			if (parts[usePartNum]->abortFirstPolyPreferHeld()) { +				return true; +			} +		} +	} +	return false; +} + +bool PartialManager::freePartials(unsigned int needed, int partNum) { +	// CONFIRMED: Barring bugs, this matches the real LAPC-I according to information from Mok. + +	// BUG: There's a bug in the LAPC-I implementation:  +	// When allocating for rhythm part, or when allocating for a part that is using fewer partials than it has reserved, +	// held and playing polys on the rhythm part can potentially be aborted before releasing polys on the rhythm part. +	// This bug isn't present on MT-32. +	// I consider this to be a bug because I think that playing polys should always have priority over held polys, +	// and held polys should always have priority over releasing polys. + +	// NOTE: This code generally aborts polys in parts (according to certain conditions) in the following order: +	// 7, 6, 5, 4, 3, 2, 1, 0, 8 (rhythm) +	// (from lowest priority, meaning most likely to have polys aborted, to highest priority, meaning least likely) + +	if (needed == 0) { +		return true; +	} + +	// Note that calling getFreePartialCount() also ensures that numReservedPartialsPerPart is up-to-date +	if (getFreePartialCount() >= needed) { +		return true; +	} + +	// Note: These #ifdefs are temporary until we have proper "quirk" configuration. +	// Also, the MT-32 version isn't properly confirmed yet. +#ifdef MT32EMU_QUIRK_FREE_PARTIALS_MT32 +	// On MT-32, we bail out before even killing releasing partials if the allocating part has exceeded its reserve and is configured for priority-to-earlier-polys. +	if (parts[partNum]->getActiveNonReleasingPartialCount() + needed > numReservedPartialsForPart[partNum] && (synth->getPart(partNum)->getPatchTemp()->patch.assignMode & 1)) { +		return false; +	} +#endif + +	for (;;) { +#ifdef MT32EMU_QUIRK_FREE_PARTIALS_MT32 +		// Abort releasing polys in parts that have exceeded their partial reservation (working backwards from part 7, with rhythm last) +		if (!abortFirstReleasingPolyWhereReserveExceeded(-1)) { +			break; +		} +#else +		// Abort releasing polys in non-rhythm parts that have exceeded their partial reservation (working backwards from part 7) +		if (!abortFirstReleasingPolyWhereReserveExceeded(0)) { +			break; +		} +#endif +		if (getFreePartialCount() >= needed) { +			return true; +		} +	} + +	if (parts[partNum]->getActiveNonReleasingPartialCount() + needed > numReservedPartialsForPart[partNum]) { +		// With the new partials we're freeing for, we would end up using more partials than we have reserved. +		if (synth->getPart(partNum)->getPatchTemp()->patch.assignMode & 1) { +			// Priority is given to earlier polys, so just give up +			return false; +		} +		// Only abort held polys in the target part and parts that have a lower priority +		// (higher part number = lower priority, except for rhythm, which has the highest priority). +		for (;;) { +			if (!abortFirstPolyPreferHeldWhereReserveExceeded(partNum)) { +				break; +			} +			if (getFreePartialCount() >= needed) { +				return true; +			} +		} +		if (needed > numReservedPartialsForPart[partNum]) { +			return false; +		} +	} else { +		// At this point, we're certain that we've reserved enough partials to play our poly. +		// Check all parts from lowest to highest priority to see whether they've exceeded their +		// reserve, and abort their polys until until we have enough free partials or they're within +		// their reserve allocation. +		for (;;) { +			if (!abortFirstPolyPreferHeldWhereReserveExceeded(-1)) { +				break; +			} +			if (getFreePartialCount() >= needed) { +				return true; +			} +		} +	} + +	// Abort polys in the target part until there are enough free partials for the new one +	for (;;) { +		if (!parts[partNum]->abortFirstPolyPreferHeld()) { +			break; +		} +		if (getFreePartialCount() >= needed) { +			return true; +		} +	} + +	// Aww, not enough partials for you. +	return false; +} + +const Partial *PartialManager::getPartial(unsigned int partialNum) const { +	if (partialNum > MT32EMU_MAX_PARTIALS - 1) { +		return NULL; +	} +	return partialTable[partialNum]; +}  | 
