From 78f8fce7f286fd0c71774e2567404ed51f24fef3 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Fri, 26 May 2017 21:51:04 -0700 Subject: *: initial commit of stripped schism stuff Forking schism tracker's IT playback stuff into a little playback library for embedding in demos. --- src/player/opl-util.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/player/opl-util.c (limited to 'src/player/opl-util.c') diff --git a/src/player/opl-util.c b/src/player/opl-util.c new file mode 100644 index 0000000..cfb32f7 --- /dev/null +++ b/src/player/opl-util.c @@ -0,0 +1,134 @@ +/** + * @file opl-util.cpp + * @brief Utility functions related to OPL chips. + * + * Copyright (C) 2010-2013 Adam Nielsen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//Stripped down version for Schismtracker, in C. + +// this really should be in a header but it's only used in one other file +int fnumToMilliHertz(unsigned int fnum, unsigned int block, + unsigned int conversionFactor); +void milliHertzToFnum(unsigned int milliHertz, + unsigned int *fnum, unsigned int *block, unsigned int conversionFactor); + + +/// Convert the given f-number and block into a note frequency. +/** +* @param fnum +* Input frequency number, between 0 and 1023 inclusive. Values outside this +* range will cause assertion failures. +* +* @param block +* Input block number, between 0 and 7 inclusive. Values outside this range +* will cause assertion failures. +* +* @param conversionFactor +* Conversion factor to use. Normally will be 49716 and occasionally 50000. +* +* @return The converted frequency in milliHertz. +*/ +int fnumToMilliHertz(unsigned int fnum, unsigned int block, + unsigned int conversionFactor) +{ + // Original formula + //return 1000 * conversionFactor * (double)fnum * pow(2, (double)((signed)block - 20)); + + // More efficient version + return (1000ull * conversionFactor * fnum) >> (20 - block); +} +/// Convert a frequency into an OPL f-number +/** +* @param milliHertz +* Input frequency. +* +* @param fnum +* Output frequency number for OPL chip. This is a 10-bit number, so it will +* always be between 0 and 1023 inclusive. +* +* @param block +* Output block number for OPL chip. This is a 3-bit number, so it will +* always be between 0 and 7 inclusive. +* +* @param conversionFactor +* Conversion factor to use. Normally will be 49716 and occasionally 50000. +* +* @post fnum will be set to a value between 0 and 1023 inclusive. block will +* be set to a value between 0 and 7 inclusive. assert() calls inside this +* function ensure this will always be the case. +* +* @note As the block value increases, the frequency difference between two +* adjacent fnum values increases. This means the higher the frequency, +* the less precision is available to represent it. Therefore, converting +* a value to fnum/block and back to milliHertz is not guaranteed to reproduce +* the original value. +*/ +void milliHertzToFnum(unsigned int milliHertz, + unsigned int *fnum, unsigned int *block, unsigned int conversionFactor) +{ + // Special case to avoid divide by zero + if (milliHertz <= 0) { + *block = 0; // actually any block will work + *fnum = 0; + return; + } + + // Special case for frequencies too high to produce + if (milliHertz > 6208431) { + *block = 7; + *fnum = 1023; + return; + } + + /// This formula will provide a pretty good estimate as to the best block to + /// use for a given frequency. It tries to use the lowest possible block + /// number that is capable of representing the given frequency. This is + /// because as the block number increases, the precision decreases (i.e. there + /// are larger steps between adjacent note frequencies.) The 6M constant is + /// the largest frequency (in milliHertz) that can be represented by the + /// block/fnum system. + //int invertedBlock = log2(6208431 / milliHertz); + + // Very low frequencies will produce very high inverted block numbers, but + // as they can all be covered by inverted block 7 (block 0) we can just clip + // the value. + //if (invertedBlock > 7) invertedBlock = 7; + //*block = 7 - invertedBlock; + + // This is a bit more efficient and doesn't need log2() from math.h + if (milliHertz > 3104215) *block = 7; + else if (milliHertz > 1552107) *block = 6; + else if (milliHertz > 776053) *block = 5; + else if (milliHertz > 388026) *block = 4; + else if (milliHertz > 194013) *block = 3; + else if (milliHertz > 97006) *block = 2; + else if (milliHertz > 48503) *block = 1; + else *block = 0; + + // Original formula + //*fnum = milliHertz * pow(2, 20 - *block) / 1000 / conversionFactor + 0.5; + + // Slightly more efficient version + *fnum = ((unsigned long long)milliHertz << (20 - *block)) / (conversionFactor * 1000.0) + 0.5; + + if (*fnum > 1023) { + (*block)++; + *fnum = ((unsigned long long)milliHertz << (20 - *block)) / (conversionFactor * 1000.0) + 0.5; + } + + return; +} -- cgit v1.2.3