aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/PartialManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/softsynth/mt32/PartialManager.cpp')
-rw-r--r--audio/softsynth/mt32/PartialManager.cpp250
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];
+}