diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d96a991 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/nugget"] + path = third_party/nugget + url = https://github.com/pcsx-redux/nugget.git diff --git a/Makefile b/Makefile index 21eac28..9abb004 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ TARGET = hello_str SRCS = hello_str.c \ src/str.c \ src/mod.c \ -src/modplayer.c \ +third_party/nugget/modplayer/modplayer.c \ HIT/SHIN1.HIT \ include common.mk diff --git a/src/mod.c b/src/mod.c index f191bdf..6af8aa2 100644 --- a/src/mod.c +++ b/src/mod.c @@ -7,6 +7,21 @@ typedef struct SpuVoiceVolume { SpuVoiceVolume volumeState[24] = {0}; +static void muteSPUvoices() { + for (unsigned i = 0; i < 24; i++) { + // Store current volume + SpuGetVoiceVolume(i, &(volumeState[i].volL), &(volumeState[i].volR) ); + // Mute + SpuSetVoiceVolume(i, 0, 0); + } +} + +static void restoreSPUvoices() { + for (unsigned i = 0; i < 24; i++) { + // Restore volume + SpuSetVoiceVolume(i, volumeState[i].volL, volumeState[i].volR ); + } +} // Playing a sound effect (aka mod note): https://discord.com/channels/642647820683444236/642848592754901033/898249196174458900 // Code by NicolasNoble : https://discord.com/channels/642647820683444236/663664210525290507/902624952715452436 void loadMod() { @@ -15,15 +30,7 @@ void loadMod() { printf("%02d Channels, %02d Orders\n", MOD_Channels, MOD_SongLength); } -void startMusic() { - ResetRCnt(RCntCNT1); - SetRCnt(RCntCNT1, MOD_hblanks, RCntMdINTR); - StartRCnt(RCntCNT1); - musicEvent = OpenEvent(RCntCNT1, EvSpINT, EvMdINTR, processMusic); - EnableEvent(musicEvent); -} - -long processMusic() { +static long processMusic() { uint32_t old_hblanks = MOD_hblanks; MOD_Poll(); uint32_t new_hblanks = MOD_hblanks; @@ -31,21 +38,22 @@ long processMusic() { return MOD_hblanks; } +void startMusic() { + ResetRCnt(RCntCNT1); + SetRCnt(RCntCNT1, MOD_hblanks, RCntMdINTR); + StartRCnt(RCntCNT1); + musicEvent = OpenEvent(RCntCNT1, EvSpINT, EvMdINTR, processMusic); + EnableEvent(musicEvent); + restoreSPUvoices(); +} + void pauseMusic() { - for (unsigned i = 0; i < 24; i++) { - // Store current volume - SpuGetVoiceVolume(i, &(volumeState[i].volL), &(volumeState[i].volR) ); - // Mute - SpuSetVoiceVolume(i, 0, 0); - } + muteSPUvoices(); DisableEvent(musicEvent); } void resumeMusic() { - for (unsigned i = 0; i < 24; i++) { - // Restore volume - SpuSetVoiceVolume(i, volumeState[i].volL, volumeState[i].volR ); - } + restoreSPUvoices(); EnableEvent(musicEvent); } diff --git a/src/mod.h b/src/mod.h index 8ee22d4..d33a3b7 100644 --- a/src/mod.h +++ b/src/mod.h @@ -1,18 +1,18 @@ #pragma once #include #include -#include "../../nugget/common/hardware/hwregs.h" -#include "../../nugget/common/hardware/irq.h" -#include "../../nugget/common/syscalls/syscalls.h" -#include "modplayer.h" - -extern const uint8_t _binary_HIT_SHIN1_HIT_start[]; -#define HITFILE _binary_HIT_SHIN1_HIT_start +#include "../third_party/nugget/common/hardware/hwregs.h" +#include "../third_party/nugget/common/hardware/irq.h" +#include "../third_party/nugget/common/syscalls/syscalls.h" #define printf ramsyscall_printf +// Mod Playback +#include "../third_party/nugget/modplayer/modplayer.h" +extern const uint8_t _binary_HIT_SHIN1_HIT_start[]; +#define HITFILE _binary_HIT_SHIN1_HIT_start extern long musicEvent; + void loadMod(); -long processMusic(); void startMusic(); void pauseMusic(); void resumeMusic(); diff --git a/src/modplayer.c b/src/modplayer.c deleted file mode 100644 index 7260517..0000000 --- a/src/modplayer.c +++ /dev/null @@ -1,806 +0,0 @@ -/* - -MIT License - -Copyright (c) 2021 PCSX-Redux authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -*/ - -#include "modplayer.h" - -#include -#include - -#include "../../nugget/common/hardware/dma.h" -#include "../../nugget/common/hardware/spu.h" -#include "../../nugget/common/syscalls/syscalls.h" - -/* This code is a reverse engineering of the file MODPLAY.BIN, located in the zip file - "Asm-Mod" from http://hitmen.c02.at/html/psx_tools.html, that has the CRC32 bb91769f. */ - -struct MODSampleData { - char name[22]; - union { - uint16_t length; - uint8_t lenarr[2]; - }; - uint8_t finetune; - uint8_t volume; - uint16_t repeatLocation; - uint16_t repeatLength; -}; - -struct MODFileFormat { - char title[20]; - struct MODSampleData samples[31]; - uint8_t songLength; - uint8_t padding; - uint8_t patternTable[128]; - uint8_t signature[4]; -}; - -struct SPUChannelData { - uint16_t note; - int16_t period; - uint16_t slideTo; - uint8_t slideSpeed; - uint8_t volume; - uint8_t sampleID; - int8_t vibrato; - uint8_t fx[4]; - uint16_t samplePos; -}; - -struct SpuInstrumentData { - uint16_t baseAddress; - uint8_t finetune; - uint8_t volume; -}; - -static struct SpuInstrumentData s_spuInstrumentData[31]; - -static void SPUInit() { - DPCR |= 0x000b0000; - SPU_VOL_MAIN_LEFT = 0x3800; - SPU_VOL_MAIN_RIGHT = 0x3800; - SPU_CTRL = 0; - SPU_KEY_ON_LOW = 0; - SPU_KEY_ON_HIGH = 0; - SPU_KEY_OFF_LOW = 0xffff; - SPU_KEY_OFF_HIGH = 0xffff; - SPU_RAM_DTC = 4; - SPU_VOL_CD_LEFT = 0; - SPU_VOL_CD_RIGHT = 0; - SPU_PITCH_MOD_LOW = 0; - SPU_PITCH_MOD_HIGH = 0; - SPU_NOISE_EN_LOW = 0; - SPU_NOISE_EN_HIGH = 0; - SPU_REVERB_EN_LOW = 0; - SPU_REVERB_EN_HIGH = 0; - SPU_VOL_EXT_LEFT = 0; - SPU_VOL_EXT_RIGHT = 0; - SPU_CTRL = 0x8000; -} - -static void SPUResetVoice(int voiceID) { - SPU_VOICES[voiceID].volumeLeft = 0; - SPU_VOICES[voiceID].volumeRight = 0; - SPU_VOICES[voiceID].sampleRate = 0; - SPU_VOICES[voiceID].sampleStartAddr = 0; - SPU_VOICES[voiceID].ad = 0x000f; - SPU_VOICES[voiceID].currentVolume = 0; - SPU_VOICES[voiceID].sampleRepeatAddr = 0; - SPU_VOICES[voiceID].sr = 0x0000; -} - -static void SPUUploadInstruments(uint32_t SpuAddr, const uint8_t* data, uint32_t size) { - uint32_t bcr = size >> 6; - if (size & 0x3f) bcr++; - bcr <<= 16; - bcr |= 0x10; - - SPU_RAM_DTA = SpuAddr >> 3; - SPU_CTRL = (SPU_CTRL & ~0x0030) | 0x0020; - while ((SPU_CTRL & 0x0030) != 0x0020) - ; - // original code erroneously was doing SBUS_DEV4_CTRL = SBUS_DEV4_CTRL; - SBUS_DEV4_CTRL &= ~0x0f000000; - DMA_CTRL[DMA_SPU].MADR = (uint32_t)data; - DMA_CTRL[DMA_SPU].BCR = bcr; - DMA_CTRL[DMA_SPU].CHCR = 0x01000201; - - while ((DMA_CTRL[DMA_SPU].CHCR & 0x01000000) != 0) - ; -} - -static void SPUUnMute() { SPU_CTRL = 0xc000; } - -static void SPUSetVoiceVolume(int voiceID, uint16_t left, uint16_t right) { - SPU_VOICES[voiceID].volumeLeft = left >> 2; - SPU_VOICES[voiceID].volumeRight = right >> 2; -} - -static void SPUSetStartAddress(int voiceID, uint32_t spuAddr) { SPU_VOICES[voiceID].sampleStartAddr = spuAddr >> 3; } - -static void SPUWaitIdle() { - do { - for (unsigned c = 0; c < 2045; c++) __asm__ volatile(""); - } while ((SPU_STATUS & 0x07ff) != 0); -} - -static void SPUKeyOn(uint32_t voiceBits) { - SPU_KEY_ON_LOW = voiceBits; - SPU_KEY_ON_HIGH = voiceBits >> 16; -} - -static void SPUSetVoiceSampleRate(int voiceID, uint16_t sampleRate) { SPU_VOICES[voiceID].sampleRate = sampleRate; } - -unsigned MOD_Check(const struct MODFileFormat* module) { - if (syscall_strncmp(module->signature, "HIT", 3) == 0) { - return module->signature[3] - '0'; - } else if (syscall_strncmp(module->signature, "HM", 2) == 0) { - return ((module->signature[2] - '0') * 10) + module->signature[3] - '0'; - } - return 0; -} - -unsigned MOD_Channels = 0; -unsigned MOD_SongLength = 0; -// original code keeps this one to the very beginning of the file, -// while this code keeps the pointer to the beginning of the order table -static const uint8_t* MOD_ModuleData = NULL; -unsigned MOD_CurrentOrder = 0; -unsigned MOD_CurrentPattern = 0; -unsigned MOD_CurrentRow = 0; -unsigned MOD_Speed = 0; -unsigned MOD_Tick = 0; -// this never seems to be updated in the original code, which is a -// mistake; the F command handler was all wrong -unsigned MOD_BPM = 0; -// original code keeps this one to the NEXT row, -// while this code keeps the pointer to the CURRENT row -const uint8_t* MOD_RowPointer = NULL; -int MOD_ChangeRowNextTick = 0; -unsigned MOD_NextRow = 0; -int MOD_ChangeOrderNextTick = 0; -unsigned MOD_NextOrder = 0; -uint8_t MOD_PatternDelay = 0; -unsigned MOD_LoopStart = 0; -unsigned MOD_LoopCount = 0; -int MOD_Stereo = 0; -uint32_t MOD_hblanks; - -// This function is now more of a helper to calculate the number of hsync -// values to wait until the next call to MOD_Poll. If the user wants to use -// another method, they will have to inspect MOD_BPM manually and make their -// own math based on their own timer. -static void MOD_SetBPM(unsigned bpm) { - MOD_BPM = bpm; - // The original code only uses 39000 here but the reality is a bit more - // complex than that, as not all clocks are exactly the same, depending - // on the machine's region, and the video mode selected. - - uint32_t status = GPU_STATUS; - int isPalConsole = *((const char*)0xbfc7ff52) == 'E'; - int isPal = (status & 0x00100000) != 0; - uint32_t base; - if (isPal && isPalConsole) { // PAL video on PAL console - base = 39062; // 312.5 * 125 * 50.000 / 50 or 314 * 125 * 49.761 / 50 - } else if (isPal && !isPalConsole) { // PAL video on NTSC console - base = 39422; // 312.5 * 125 * 50.460 / 50 or 314 * 125 * 50.219 / 50 - } else if (!isPal && isPalConsole) { // NTSC video on PAL console - base = 38977; // 262.5 * 125 * 59.393 / 50 or 263 * 125 * 59.280 / 50 - } else { // NTSC video on NTSC console - base = 39336; // 262.5 * 125 * 59.940 / 50 or 263 * 125 * 59.826 / 50 - } - MOD_hblanks = base / bpm; -} - -struct SPUChannelData s_channelData[24]; - -uint32_t MOD_Load(const struct MODFileFormat* module) { - SPUInit(); - MOD_Channels = MOD_Check(module); - - if (MOD_Channels == 0) return 0; - - uint32_t currentSpuAddress = 0x1010; - for (unsigned i = 0; i < 31; i++) { - s_spuInstrumentData[i].baseAddress = currentSpuAddress >> 4; - s_spuInstrumentData[i].finetune = module->samples[i].finetune; - s_spuInstrumentData[i].volume = module->samples[i].volume; - currentSpuAddress += module->samples[i].lenarr[0] * 0x100 + module->samples[i].lenarr[1]; - } - - MOD_SongLength = module->songLength; - - unsigned maxPatternID = 0; - for (unsigned i = 0; i < 128; i++) { - if (maxPatternID < module->patternTable[i]) maxPatternID = module->patternTable[i]; - } - - MOD_ModuleData = (const uint8_t*)&module->patternTable[0]; - - SPUUploadInstruments(0x1010, MOD_ModuleData + 4 + 128 + MOD_Channels * 0x100 * (maxPatternID + 1), - currentSpuAddress - 0x1010); - - MOD_CurrentOrder = 0; - MOD_CurrentPattern = module->patternTable[0]; - MOD_CurrentRow = 0; - MOD_Speed = 6; - MOD_Tick = 6; - MOD_RowPointer = MOD_ModuleData + 4 + 128 + MOD_CurrentPattern * MOD_Channels * 0x100; - // original code goes only up to MOD_Channels; let's reset all 24 - for (unsigned i = 0; i < 24; i++) SPUResetVoice(i); - MOD_ChangeRowNextTick = 0; - MOD_ChangeOrderNextTick = 0; - MOD_LoopStart = 0; - MOD_LoopCount = 0; - - // these two are erroneously missing from the original code, at - // least for being able to play more than one music - MOD_PatternDelay = 0; - syscall_memset(s_channelData, 0, sizeof(s_channelData)); - - SPUUnMute(); - - // this one is also missing, and is necessary, for being able to call MOD_Load - // after another song that changed the tempo previously - MOD_SetBPM(190); - - // the original code would do: - // return MOD_Channels; - // but we are returning the size for the MOD_Relocate call - return 4 + 128 + MOD_Channels * 0x100 * (maxPatternID + 1); -} - -void MOD_Silence() { - SPUInit(); - for (unsigned i = 0; i < 24; i++) { - SPUResetVoice(i); - } -} - -void MOD_Relocate(uint8_t* s1) { - if (MOD_ModuleData == s1) return; - unsigned maxPatternID = 0; - for (unsigned i = 0; i < 128; i++) { - if (maxPatternID < MOD_ModuleData[i]) maxPatternID = MOD_ModuleData[i]; - } - - size_t n = 4 + 128 + MOD_Channels * 0x100 * (maxPatternID + 1); - - const uint8_t* s2 = MOD_ModuleData; - size_t i; - - if (s1 < s2) { - for (i = 0; i < n; i++) *s1++ = *s2++; - } else if (s1 > s2) { - s1 += n; - s2 += n; - for (i = 0; i < n; i++) *--s1 = *--s2; - } - - MOD_ModuleData = s1; -} - -static const uint8_t MOD_SineTable[32] = { - 0x00, 0x18, 0x31, 0x4a, 0x61, 0x78, 0x8d, 0xa1, 0xb4, 0xc5, 0xd4, 0xe0, 0xeb, 0xf4, 0xfa, 0xfd, - 0xff, 0xfd, 0xfa, 0xf4, 0xeb, 0xe0, 0xd4, 0xc5, 0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x4a, 0x31, 0x18, -}; - -// C C# D D# E F F# G G# A A# B -const uint16_t MOD_PeriodTable[36 * 16] = { - 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, // octave 1 tune 0 - 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, // octave 2 tune 0 - 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, // octave 3 tune 0 - 850, 802, 757, 715, 674, 637, 601, 567, 535, 505, 477, 450, // octave 1 tune 1 - 425, 401, 379, 357, 337, 318, 300, 284, 268, 253, 239, 225, // octave 2 tune 1 - 213, 201, 189, 179, 169, 159, 150, 142, 134, 126, 119, 113, // octave 3 tune 1 - 844, 796, 752, 709, 670, 632, 597, 563, 532, 502, 474, 447, // octave 1 tune 2 - 422, 398, 376, 355, 335, 316, 298, 282, 266, 251, 237, 224, // octave 2 tune 2 - 211, 199, 188, 177, 167, 158, 149, 141, 133, 125, 118, 112, // octave 3 tune 2 - 838, 791, 746, 704, 665, 628, 592, 559, 528, 498, 470, 444, // octave 1 tune 3 - 419, 395, 373, 352, 332, 314, 296, 280, 264, 249, 235, 222, // octave 2 tune 3 - 209, 198, 187, 176, 166, 157, 148, 140, 132, 125, 118, 111, // octave 3 tune 3 - 832, 785, 741, 699, 660, 623, 588, 555, 524, 495, 467, 441, // octave 1 tune 4 - 416, 392, 370, 350, 330, 312, 294, 278, 262, 247, 233, 220, // octave 2 tune 4 - 208, 196, 185, 175, 165, 156, 147, 139, 131, 124, 117, 110, // octave 3 tune 4 - 826, 779, 736, 694, 655, 619, 584, 551, 520, 491, 463, 437, // octave 1 tune 5 - 413, 390, 368, 347, 328, 309, 292, 276, 260, 245, 232, 219, // octave 2 tune 5 - 206, 195, 184, 174, 164, 155, 146, 138, 130, 123, 116, 109, // octave 3 tune 5 - 820, 774, 730, 689, 651, 614, 580, 547, 516, 487, 460, 434, // octave 1 tune 6 - 410, 387, 365, 345, 325, 307, 290, 274, 258, 244, 230, 217, // octave 2 tune 6 - 205, 193, 183, 172, 163, 154, 145, 137, 129, 122, 115, 109, // octave 3 tune 6 - 814, 768, 725, 684, 646, 610, 575, 543, 513, 484, 457, 431, // octave 1 tune 7 - 407, 384, 363, 342, 323, 305, 288, 272, 256, 242, 228, 216, // octave 2 tune 7 - 204, 192, 181, 171, 161, 152, 144, 136, 128, 121, 114, 108, // octave 3 tune 7 - 907, 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, // octave 1 tune -8 - 453, 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, // octave 2 tune -8 - 226, 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, // octave 3 tune -8 - 900, 850, 802, 757, 715, 675, 636, 601, 567, 535, 505, 477, // octave 1 tune -7 - 450, 425, 401, 379, 357, 337, 318, 300, 284, 268, 253, 238, // octave 2 tune -7 - 225, 212, 200, 189, 179, 169, 159, 150, 142, 134, 126, 119, // octave 3 tune -7 - 894, 844, 796, 752, 709, 670, 632, 597, 563, 532, 502, 474, // octave 1 tune -6 - 447, 422, 398, 376, 355, 335, 316, 298, 282, 266, 251, 237, // octave 2 tune -6 - 223, 211, 199, 188, 177, 167, 158, 149, 141, 133, 125, 118, // octave 3 tune -6 - 887, 838, 791, 746, 704, 665, 628, 592, 559, 528, 498, 470, // octave 1 tune -5 - 444, 419, 395, 373, 352, 332, 314, 296, 280, 264, 249, 235, // octave 2 tune -5 - 222, 209, 198, 187, 176, 166, 157, 148, 140, 132, 125, 118, // octave 3 tune -5 - 881, 832, 785, 741, 699, 660, 623, 588, 555, 524, 494, 467, // octave 1 tune -4 - 441, 416, 392, 370, 350, 330, 312, 294, 278, 262, 247, 233, // octave 2 tune -4 - 220, 208, 196, 185, 175, 165, 156, 147, 139, 131, 123, 117, // octave 3 tune -4 - 875, 826, 779, 736, 694, 655, 619, 584, 551, 520, 491, 463, // octave 1 tune -3 - 437, 413, 390, 368, 347, 328, 309, 292, 276, 260, 245, 232, // octave 2 tune -3 - 219, 206, 195, 184, 174, 164, 155, 146, 138, 130, 123, 116, // octave 3 tune -3 - 868, 820, 774, 730, 689, 651, 614, 580, 547, 516, 487, 460, // octave 1 tune -2 - 434, 410, 387, 365, 345, 325, 307, 290, 274, 258, 244, 230, // octave 2 tune -2 - 217, 205, 193, 183, 172, 163, 154, 145, 137, 129, 122, 115, // octave 3 tune -2 - 862, 814, 768, 725, 684, 646, 610, 575, 543, 513, 484, 457, // octave 1 tune -1 - 431, 407, 384, 363, 342, 323, 305, 288, 272, 256, 242, 228, // octave 2 tune -1 - 216, 203, 192, 181, 171, 161, 152, 144, 136, 128, 121, 114, // octave 3 tune -1 -}; - -#define SETVOICESAMPLERATE(channel, newPeriod) \ - SPUSetVoiceSampleRate(channel, ((7093789 / (newPeriod * 2)) << 12) / 44100) -#define SETVOICEVOLUME(channel, volume) \ - volume <<= 8; \ - if (MOD_Stereo) { \ - int pan = (channel & 1) ^ (channel >> 1); \ - int16_t left = pan == 0 ? volume : 0; \ - int16_t right = pan == 0 ? 0 : volume; \ - SPUSetVoiceVolume(channel, left, right); \ - } else { \ - SPUSetVoiceVolume(channel, volume, volume); \ - } - -static void MOD_UpdateEffect() { - const uint8_t* rowPointer = MOD_RowPointer; - const unsigned channels = MOD_Channels; - for (unsigned channel = 0; channel < channels; channel++) { - uint8_t effectNibble23 = rowPointer[3]; - uint8_t effectNibble1 = rowPointer[2] & 0x0f; - uint8_t effectNibble2 = effectNibble23 & 0x0f; - uint8_t effectNibble3 = effectNibble23 >> 4; - - uint8_t arpeggioTick; - int32_t newPeriod; - int16_t volume; - uint16_t slideTo; - uint8_t fx; - uint32_t mutation; - int8_t newValue; - - struct SPUChannelData* const channelData = &s_channelData[channel]; - - switch (effectNibble1) { - case 0: // arpeggio - if (effectNibble23 == 0) break; - arpeggioTick = MOD_Tick; - arpeggioTick %= 3; - switch (arpeggioTick) { - case 0: - newPeriod = channelData->period; - break; - case 1: - newPeriod = MOD_PeriodTable[channelData->note + effectNibble3]; - break; - case 2: - newPeriod = MOD_PeriodTable[channelData->note + effectNibble2]; - break; - } - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 1: // portamento up - newPeriod = channelData->period; - newPeriod -= effectNibble23; - if (newPeriod < 108) newPeriod = 108; - channelData->period = newPeriod; - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 2: // portamento down - newPeriod = channelData->period; - newPeriod += effectNibble23; - if (newPeriod > 907) newPeriod = 907; - channelData->period = newPeriod; - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 5: - volume = channelData->volume; - if (effectNibble23 <= 0x10) { - volume -= effectNibble23; - if (volume < 0) volume = 0; - } else { - volume += effectNibble3; - if (volume > 63) volume = 63; - } - channelData->volume = volume; - SETVOICEVOLUME(channel, volume); - /* fall through */ - case 3: // glissando - newPeriod = channelData->period; - slideTo = channelData->slideTo; - if (newPeriod < slideTo) { - newPeriod += channelData->slideSpeed; - if (newPeriod > slideTo) newPeriod = slideTo; - } else if (newPeriod > slideTo) { - newPeriod -= channelData->slideSpeed; - if (newPeriod < slideTo) newPeriod = slideTo; - } - channelData->period = newPeriod; - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 6: - volume = channelData->volume; - if (effectNibble23 <= 0x10) { - volume -= effectNibble23; - if (volume < 0) volume = 0; - } else { - volume += effectNibble3; - if (volume > 63) volume = 63; - } - channelData->volume = volume; - SETVOICEVOLUME(channel, volume); - /* fall through */ - case 4: // vibrato - mutation = channelData->vibrato & 0x1f; - switch (channelData->fx[3] & 3) { - case 0: - case 3: // 3 is technically random - mutation = MOD_SineTable[mutation]; - break; - case 1: - if (channelData->vibrato < 0) { - mutation *= -8; - mutation += 0xff; - } else { - mutation *= 8; - } - break; - case 2: - mutation = 0xff; - break; - } - mutation *= channelData->fx[1] >> 4; - mutation >>= 7; - newPeriod = channelData->period; - if (channelData->vibrato < 0) { - newPeriod -= mutation; - } else { - newPeriod += mutation; - } - newValue = channelData->vibrato; - newValue += channelData->fx[1] & 0x0f; - if (newValue >= 32) newValue -= 64; - channelData->vibrato = newValue; - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 7: // tremolo - mutation = s_channelData[0].fx[0] & 0x1f; - switch (s_channelData[0].fx[3] & 3) { - case 0: - case 3: // 3 is technically random - mutation = MOD_SineTable[mutation]; - break; - case 1: - if (channelData->fx[0] & 0x80) { - mutation *= -8; - mutation += 0xff; - } else { - mutation *= 8; - } - break; - case 2: - mutation = 0xff; - break; - } - mutation *= channelData->fx[3] >> 4; - mutation >>= 6; - volume = channelData->volume; - if (channelData->fx[0] & 0x80) { - volume -= mutation; - } else { - volume += mutation; - } - newValue = channelData->fx[0] + (channelData->fx[2] & 0x0f); - if (newValue >= 32) newValue -= 64; - channelData->fx[0] = newValue; - if (volume > 63) volume = 63; - SETVOICEVOLUME(channel, volume); - break; - case 10: // volume slide - volume = channelData->volume; - if (effectNibble23 <= 0x10) { - volume -= effectNibble23; - if (volume < 0) volume = 0; - } else { - volume += effectNibble3; - if (volume > 63) volume = 63; - } - channelData->volume = volume; - SETVOICEVOLUME(channel, volume); - break; - case 14: // extended - switch (effectNibble3) { - case 9: // retrigger sample - // this doesn't look right, we probably want to reset the sample location - if ((MOD_Tick % effectNibble2) == 0) SPUKeyOn(1 << channel); - break; - case 12: // cut sample - if (MOD_Tick != effectNibble2) break; - channelData->volume = 0; - SPUSetVoiceVolume(channel, 0, 0); - } - break; - } - - rowPointer += 4; - } -} - -static void MOD_UpdateRow() { - const unsigned channels = MOD_Channels; - if (MOD_ChangeOrderNextTick) { - unsigned newOrder = MOD_NextOrder; - if (newOrder >= MOD_SongLength) newOrder = 0; - MOD_CurrentRow = 0; - MOD_CurrentOrder = newOrder; - MOD_CurrentPattern = MOD_ModuleData[newOrder]; - } - if (MOD_ChangeRowNextTick) { - unsigned newRow = (MOD_NextRow >> 4) * 10 + (MOD_NextRow & 0x0f); - if (newRow >= 64) newRow = 0; - MOD_CurrentRow = newRow; - if (MOD_ChangeOrderNextTick) { - if (++MOD_CurrentOrder >= MOD_SongLength) MOD_CurrentOrder = 0; - MOD_CurrentPattern = MOD_ModuleData[MOD_CurrentOrder]; - } - } - MOD_ChangeRowNextTick = 0; - MOD_ChangeOrderNextTick = 0; - MOD_RowPointer = - MOD_ModuleData + 128 + 4 + MOD_CurrentPattern * MOD_Channels * 0x100 + MOD_CurrentRow * channels * 4; - const uint8_t* rowPointer = MOD_RowPointer; - - for (unsigned channel = 0; channel < channels; channel++) { - int16_t volume; - struct SPUChannelData* const channelData = &s_channelData[channel]; - - uint8_t effectNibble1 = rowPointer[2]; - uint8_t effectNibble23 = rowPointer[3]; - uint16_t nibble0 = rowPointer[0]; - unsigned sampleID = (nibble0 & 0xf0) | (effectNibble1 >> 4); - uint8_t effectNibble2 = effectNibble23 & 0x0f; - uint8_t effectNibble3 = effectNibble23 >> 4; - unsigned period = ((nibble0 & 0x0f) << 8) | rowPointer[1]; - int32_t newPeriod; - uint8_t fx; - effectNibble1 &= 0x0f; - - if (effectNibble1 != 9) channelData->samplePos = 0; - if (sampleID != 0) { - channelData->sampleID = --sampleID; - volume = s_spuInstrumentData[sampleID].volume; - if (volume > 63) volume = 63; - channelData->volume = volume; - if (effectNibble1 != 7) { - SETVOICEVOLUME(channel, volume); - } - SPUSetStartAddress(channel, s_spuInstrumentData[sampleID].baseAddress << 4 + channelData->samplePos); - } - - if (period != 0) { - int periodIndex; - // original code erroneously does >= 0 - for (periodIndex = 35; periodIndex--; periodIndex > 0) { - if (MOD_PeriodTable[periodIndex] == period) break; - } - channelData->note = periodIndex + s_spuInstrumentData[channelData->sampleID].finetune * 36; - fx = channelData->fx[3]; - if ((fx & 0x0f) < 4) { - channelData->vibrato = 0; - } - if ((fx >> 4) < 4) { - channelData->fx[0] = 0; - } - if ((effectNibble1 != 3) && (effectNibble1 != 5)) { - SPUWaitIdle(); - SPUKeyOn(1 << channel); - channelData->period = MOD_PeriodTable[channelData->note]; - } - newPeriod = channelData->period; - SETVOICESAMPLERATE(channel, newPeriod); - } - - switch (effectNibble1) { - case 3: // glissando - if (effectNibble23 != 0) { - channelData->slideSpeed = effectNibble23; - } - if (period != 0) { - channelData->slideTo = MOD_PeriodTable[channelData->note]; - } - break; - case 4: // vibrato - if (effectNibble3 != 0) { - fx = channelData->fx[1]; - fx &= ~0x0f; - fx |= effectNibble3; - channelData->fx[1] = fx; - } - if (effectNibble2 != 0) { - fx = channelData->fx[1]; - fx &= ~0xf0; - fx |= effectNibble3 << 4; - channelData->fx[1] = fx; - } - break; - case 7: // tremolo - if (effectNibble3 != 0) { - fx = channelData->fx[2]; - fx &= ~0x0f; - fx |= effectNibble3; - channelData->fx[2] = fx; - } - if (effectNibble2 != 0) { - fx = channelData->fx[2]; - fx &= ~0xf0; - fx |= effectNibble2 << 4; - channelData->fx[2] = fx; - } - break; - case 9: // sample jump - if (effectNibble23 != 0) { - uint16_t newSamplePos = effectNibble23; - channelData->samplePos = newSamplePos << 7; - } - break; - case 11: // order jump - if (!MOD_ChangeOrderNextTick) { - MOD_ChangeOrderNextTick = 1; - MOD_NextOrder = effectNibble23; - } - break; - case 12: // set volume - volume = effectNibble23; - if (volume > 64) volume = 63; - channelData->volume = volume; - SETVOICEVOLUME(channel, volume); - break; - case 13: // pattern break - if (!MOD_ChangeRowNextTick) { - MOD_ChangeRowNextTick = 1; - MOD_NextRow = effectNibble23; - } - break; - case 14: // extended - switch (effectNibble3) { - case 1: // fineslide up - newPeriod = channelData->period; - newPeriod -= effectNibble2; - channelData->period = newPeriod; - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 2: // fineslide down - newPeriod = channelData->period; - newPeriod += effectNibble2; - channelData->period = newPeriod; - SETVOICESAMPLERATE(channel, newPeriod); - break; - case 4: // set vibrato waveform - fx = channelData->fx[3]; - fx &= ~0x0f; - fx |= effectNibble2; - channelData->fx[3] = fx; - break; - case 5: // set finetune value - s_spuInstrumentData[sampleID].finetune = effectNibble2; - break; - case 6: // loop pattern - if (MOD_LoopCount-- == 0) { - MOD_LoopCount = effectNibble2; - } - if (MOD_LoopCount != 0) { - MOD_CurrentRow = MOD_LoopStart; - } - break; - case 7: // set tremolo waveform - fx = channelData->fx[3]; - fx &= ~0xf0; - fx |= effectNibble2 << 4; - channelData->fx[3] = fx; - break; - case 10: // fine volume up - volume = channelData->volume; - volume += effectNibble2; - if (volume > 63) volume = 63; - channelData->volume = volume; - SETVOICEVOLUME(channel, volume); - break; - case 11: // fine volume down - volume = channelData->volume; - volume -= effectNibble2; - if (volume < 0) volume = 0; - channelData->volume = volume; - SETVOICEVOLUME(channel, volume); - break; - case 14: // delay pattern - MOD_PatternDelay = effectNibble2; - break; - } - break; - case 15: // set speed - // the original code here is very wrong with regards to - // how to interpret the command; also it was very opinionated - // about using timer1 for its clock source - if (effectNibble23 == 0) break; - if (effectNibble23 < 32) { - MOD_Speed = effectNibble23; - } else { - MOD_SetBPM(effectNibble23); - } - break; - } - - rowPointer += 4; - } -} - -void MOD_Poll() { - // the original code is getting the delay pattern wrong here, and - // isn't processing them as actual line delays, rather as a sort - // of ticks delay, and was basically going too fast - uint8_t newPatternDelay = MOD_PatternDelay; - if (++MOD_Tick < MOD_Speed) { - MOD_UpdateEffect(); - } else { - MOD_Tick = 0; - if (newPatternDelay-- == 0) { - MOD_UpdateRow(); - newPatternDelay = MOD_PatternDelay; - // I don't think the original code was handling this properly... - if (++MOD_CurrentRow >= 64 || MOD_ChangeRowNextTick) { - MOD_CurrentRow = 0; - if (++MOD_CurrentOrder >= MOD_SongLength) { - MOD_CurrentOrder = 0; - } - MOD_CurrentPattern = MOD_ModuleData[MOD_CurrentOrder]; - } - } else { - MOD_UpdateEffect(); - } - } - MOD_PatternDelay = newPatternDelay; -} - -void MOD_PlayNote(unsigned channel, unsigned sampleID, unsigned note, int16_t volume) { - if (volume < 0) volume = 0; - if (volume > 63) volume = 63; - struct SPUChannelData* const channelData = &s_channelData[channel]; - channelData->samplePos = 0; - SPUSetVoiceVolume(channel, volume << 8, volume << 8); - SPUSetStartAddress(channel, s_spuInstrumentData[sampleID].baseAddress << 4 + channelData->samplePos); - SPUWaitIdle(); - SPUKeyOn(1 << channel); - channelData->note = note = note + s_spuInstrumentData[sampleID].finetune * 36; - int32_t newPeriod = channelData->period = MOD_PeriodTable[note]; - SETVOICESAMPLERATE(channel, newPeriod); -} diff --git a/src/modplayer.h b/src/modplayer.h deleted file mode 100644 index 990a623..0000000 --- a/src/modplayer.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - -MIT License - -Copyright (c) 2021 PCSX-Redux authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -*/ - -#pragma once -#define printf ramsyscall_printf -#include - -// Once MOD_Load returns, these values will be valid. -// Unless specified, consider them read only, but -// modifying them might be doable if you know what you are doing. -extern unsigned MOD_Channels; -extern unsigned MOD_SongLength; -extern unsigned MOD_CurrentOrder; -extern unsigned MOD_CurrentPattern; -extern unsigned MOD_CurrentRow; -extern unsigned MOD_Speed; -extern unsigned MOD_Tick; -extern unsigned MOD_BPM; -extern unsigned MOD_LoopStart; -extern unsigned MOD_LoopCount; -extern uint8_t MOD_PatternDelay; - -// This is a pointer to the current row that's -// being played. Used for decoding. The number -// of relevant bytes for a row is 4 * MOD_Channels. -extern const uint8_t* MOD_RowPointer; - -// These four are fine to change outside of MOD_Poll. -// The first two are booleans, and the next two are the values -// you need them to be set at when MOD_Poll is called next. -// If you need immediate row / pattern change, also set -// MOD_Tick to MOD_Speed. -extern int MOD_ChangeRowNextTick; -extern int MOD_ChangeOrderNextTick; -extern unsigned MOD_NextRow; -extern unsigned MOD_NextOrder; - -// This can be used to decode MOD_RowPointer. -extern const uint16_t MOD_PeriodTable[]; - -// Internal HIT file structure, but conformant to -// http://www.aes.id.au/modformat.html -struct MODFileFormat; - -// Returns the number of channel from this module, -// or 0 if the module is invalid. -unsigned MOD_Check(const struct MODFileFormat* module); - -// Loads the specified module and gets it ready for -// playback. Returns the number of bytes needed if -// relocation is desired. The pointer has to be -// aligned to a 4-bytes boundary. Will also setup -// the SPU. -uint32_t MOD_Load(const struct MODFileFormat* module); - -// Call this function periodically to play sound. The -// frequency at which this is called will determine the -// actual playback speed of the module. Most modules will -// not change the default tempo, which requires calling -// MOD_Poll 50 times per second, or exactly the vertical -// refresh rate in PAL. Preferably call this from timer1's -// IRQ however, and look up MOD_hblanks to decide of the -// next target value to use. -// To pause or stop playback, simply stop calling this -// function. The internal player doesn't need any -// sort of cleanup, and switching to another song simply -// requires calling MOD_Load with a new file. -void MOD_Poll(); - -// New APIs from the original code from there on. - -// Defaults to 0. This is a boolean indicating if we -// want the volume settings to be monaural or the same -// as the original Amiga's Paula chip. -extern int MOD_Stereo; - -// Indicates the number of hblank ticks to wait before -// calling MOD_Poll. This value may or may not change -// after a call to MOD_Poll, if the track requested a -// tempo change. -extern uint32_t MOD_hblanks; - -// It is possible to reclaim memory from the initial call -// to MOD_Load, in case the module was loaded from an -// external source. The number of bytes needed for the -// player will be returned by MOD_Load. Call MOD_Relocate -// with a new memory buffer that has at least this many bytes. -// Caller is responsible for managing the memory. -// It is fine to reuse the same buffer as the original input, -// if you wish to simply realloc it after relocating it, -// provided your realloc implementation guarantees that the -// shrunk buffer will remain at the same location. -// -// For example, this pseudo-code is valid: -// bool load_mod_file(File mod_file) { -// void * buffer = malloc(file_size(mod_file)); -// readfile(mod_file, buffer); -// uint32_t size = MOD_Load(buffer); -// if (size == 0) { -// free(buffer); -// return false; -// } -// MOD_Relocate(buffer); -// void * newbuffer = realloc(buffer, size); -// if (newbuffer != buffer) { -// free(newbuffer); -// return false; -// } -// return true; -// } -void MOD_Relocate(uint8_t* buffer); - -// Plays an arbitrary note from the MOD's samples bank. -// The volume will always be centered, so the sample will -// be monaural. The voiceID ideally should be set to a -// value that is less than MOD_Channels. Remember the PS1 -// has 24 channels total, so voiceID can be between 0 and 23. -// The note is a value between 0 and 35. The exact note played -// is on the normal 12-notes, C, C#, D, ... scale, and there -// are three octaves available, which gives the 12*3=36 -// interval value of the note argument. The volume argument -// is between 0 and 63. You can simulate KeyOff by simply -// setting the volume of the voice to 0. -void MOD_PlayNote(unsigned voiceID, unsigned sampleID, unsigned note, int16_t volume); - -// Added API to reset the SPU and silence everything. -void MOD_Silence(); diff --git a/third_party/nugget b/third_party/nugget new file mode 160000 index 0000000..09a2cf6 --- /dev/null +++ b/third_party/nugget @@ -0,0 +1 @@ +Subproject commit 09a2cf622fabaaac629c7277cdc50b017cd81503