Modplayer wip

This commit is contained in:
ABelliqueux 2021-10-26 18:24:12 +02:00
parent ec76da6b79
commit 097597d75e
19 changed files with 1176 additions and 72 deletions

BIN
HIT/shine.hit Normal file

Binary file not shown.

BIN
HIT/shine.mod Normal file

Binary file not shown.

BIN
HIT/shine.o Normal file

Binary file not shown.

View File

@ -10,5 +10,8 @@ TARGET = hello_str
SRCS = hello_str.c \
src/str.c \
src/mod.c \
src/modplayer.c \
HIT/shine.hit \
include common.mk

26
STR/avi2str.scr Normal file
View File

@ -0,0 +1,26 @@
Avi2strMdecV(
.\idle_loop_mc.avi,
.\idle_loop_mc.str,
x2, # CD-ROM speed
15fps, # Frame rate
2, # Number of channels
2, # MDEC version
FALSE # Leap sector
);
Avi2strMdecV(
.\transition_mc.avi,
.\transition_mc.str,
x2, # CD-ROM speed
15fps, # Frame rate
2, # Number of channels
2, # MDEC version
FALSE # Leap sector
);
Pack2ch(
.\menu.str, FALSE, # Output file name(Fn), Subheader(Sub)
.\idle_loop_mc.str, FALSE, 0, 0, # ch0; Fn,sub,TermType,TermLength
.\transition_mc.str, FALSE, 0, 0 # ch1; Fn sub,TermType,TermLength
);

BIN
STR/menu.str Normal file

Binary file not shown.

BIN
VAG/select.wav Normal file

Binary file not shown.

BIN
VAG/switch.wav Normal file

Binary file not shown.

View File

@ -51,3 +51,8 @@ endef
# convert VAG files to bin
%.o: %.vag
$(call OBJCOPYME)
# convert HIT to bin
%.o: %.hit
$(call OBJCOPYME)

View File

@ -14,9 +14,10 @@
// CODEC library
#include <libpress.h>
// printf
#include "../nolibgs_hello_worlds/thirdparty/nugget/common/syscalls/syscalls.h"
//~ #include "../nolibgs_hello_worlds/thirdparty/nugget/common/syscalls/syscalls.h"
// str playback
#include "src/str.h"
#include "src/mod.h"
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
@ -109,6 +110,7 @@ void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
//~ checkMusic();
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
@ -153,55 +155,6 @@ void drawBG(void)
addPrim(ot[db], poly); // add poly to the Ordering table
nextpri += sizeof(POLY_FT4); // increment nextpri address with size of a POLY_F4 struct
}
int main() {
curStr = &(menu[0]);
// Set pointers to the relevant buffer addresses
u_long * curVLCptr = &VlcBuff[0];
u_short * curIMGptr = &ImgBuff[0];
init();
PadInit(0);
VSyncCallback(checkPad);
// Init CDrom system
CdInit();
// Init MDEC and load STR
initSTR(curStr);
// Set Channel
StSetChannel( curStr->channel );
// Main loop
while (1) {
// Only display background STR if drawMenu is set
if (drawMenu)
{
SetDispMask(1);
drawBG();
}
// While end of str is not reached, play it
if (curStr->endPlayback == 1)
{
// Replay STR
resetSTR(curStr);
}
while ( curStr->endPlayback == 0)
{
playSTR(&curStr);
if ( !curStr->channel )
{
FntPrint("%s", menu_items[0].title);
if ( sectorHeader->frameCount > 5 )
{
FntFlush(-1);
} else if ( (sectorHeader->frameCount % 2) && sectorHeader->frameCount < 5 )
{
FntFlush(-1);
}
}
display();
}
}
return 0;
}
void checkPad(void)
{
u_short pad;
@ -222,4 +175,74 @@ void checkPad(void)
{
oldPad = pad;
}
}
int main() {
curStr = &(menu[0]);
// Set pointers to the relevant buffer addresses
u_long * curVLCptr = &VlcBuff[0];
u_short * curIMGptr = &ImgBuff[0];
init();
PadInit(0);
VSyncCallback(checkPad);
// Init CDrom system
CdInit();
// Init MDEC and load STR
initSTR(curStr);
// Set Channel
StSetChannel( curStr->channel );
// Main loop
//~ loadMod();
printf("Loading MOD:\'%s\'\n", HITFILE);
// We are going to use timer1 and its hblank counter to tell us when
// we need to call MOD_Poll again. For this, we need timer1 to be
// counting hblanks instead of the system clock.
COUNTERS[1].mode = 0x0100;
MOD_Load((struct MODFileFormat*)HITFILE);
printf("%02d Channels, %02d Orders\n", MOD_Channels, MOD_SongLength);
unsigned row = 0xffffffff;
unsigned order = 0xffffffff;
unsigned pattern = 0xffffffff;
//~ uint16_t s_nextCounter = 0;
// Giving our initial counter a proper value.
s_nextCounter = COUNTERS[1].value + MOD_hblanks;
while (1)
//~ while (VSync(-1))
{
playMod(row, order, pattern);
checkMusic();
// Only display background STR if drawMenu is set
if (drawMenu)
{
SetDispMask(1);
drawBG();
}
// While end of str is not reached, play it
if (curStr->endPlayback == 1)
{
// Replay STR
resetSTR(curStr);
}
if ( curStr->endPlayback == 0)
{
playSTR(&curStr);
if ( !curStr->channel )
{
// Display title
FntPrint("%s", menu_items[0].title);
// Flickering text
if ( sectorHeader->frameCount > 5 )
{
FntFlush(-1);
} else if ( (sectorHeader->frameCount % 2) && sectorHeader->frameCount < 5 )
{
FntFlush(-1);
}
}
}
//~ playMod(row, order, pattern);
//~ waitVSync();
display();
}
return 0;
}

View File

@ -86,7 +86,7 @@
<!-- Stores system.txt as system.cnf -->
<file name="system.cnf" type="data" source="system.cnf"/>
<file name="SCES_313.37" type="data" source="hello_str.ps-exe"/>
<file name="MENU.STR" type="str" source="render/menu.str"/>
<file name="MENU.STR" type="str" source="STR/menu.str"/>
<dummy sectors="1024"/>

View File

@ -1,5 +0,0 @@
Pack2ch(
z:\home\arthus\build\psxdev\nolibgs_demo\render\bg_only_i.str, FALSE, # Output file name(Fn), Subheader(Sub)
z:\home\arthus\build\psxdev\nolibgs_demo\render\bg_only_1_mc.str, FALSE, 0, 2, # ch0; Fn,sub,TermType,TermLength
z:\home\arthus\build\psxdev\nolibgs_demo\render\bg_only_mc.str, FALSE, 0, 2 # ch1; Fn sub,TermType,TermLength
);

91
src/mod.c Normal file
View File

@ -0,0 +1,91 @@
/*
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 "mod.h"
uint16_t s_nextCounter = 0;
// Function to call periodically if we can't use interrupts for some reason.
// If the code is running slower than the normal vsync speed, then you should
// call this during your frame computation code, in order to make sure you're
// not missing any tick.
void checkMusic() {
if (((int16_t)(s_nextCounter - COUNTERS[1].value)) <= 0) {
printf("cnt: %d\n", s_nextCounter);
MOD_Poll();
s_nextCounter += MOD_hblanks;
}
}
void waitVSync() {
int wasLocked = enterCriticalSection();
uint32_t imask = IMASK;
IMASK = imask | IRQ_VBLANK;
while ((IREG & IRQ_VBLANK) == 0) {
// Since our vsync is a kludge, we can't use the root counter IRQ
// mechanism to call MOD_Poll, and so we have to poll here, during
// vsync, for when our timer has passed the target value manually.
// We *could* set up the timer properly using the target value system,
// but this might make a few emulators sad. Beside, this is a perfectly
// valid and common solution.
checkMusic();
}
IREG &= ~IRQ_VBLANK;
IMASK = imask;
if (!wasLocked) leaveCriticalSection();
}
void playMod(unsigned row, unsigned order, unsigned pattern)
{
if (row != MOD_CurrentRow || order != MOD_CurrentOrder || pattern != MOD_CurrentPattern) {
row = MOD_CurrentRow;
order = MOD_CurrentOrder;
pattern = MOD_CurrentPattern;
printf("Row: %02d, Order: %02d, Pattern: %02d\n", row, order, pattern);
}
}
//~ void loadMod() {
//~ printf("Loading MOD:\'%s\'\n", HITFILE);
//~ // We are going to use timer1 and its hblank counter to tell us when
//~ // we need to call MOD_Poll again. For this, we need timer1 to be
//~ // counting hblanks instead of the system clock.
//~ COUNTERS[1].mode = 0x0100;
//~ MOD_Load((struct MODFileFormat*)HITFILE);
//~ printf("%02d Channels, %02d Orders\n", MOD_Channels, MOD_SongLength);
//~ unsigned row = 0xffffffff;
//~ unsigned order = 0xffffffff;
//~ unsigned pattern = 0xffffffff;
//~ // Giving our initial counter a proper value.
//~ s_nextCounter = COUNTERS[1].value + MOD_hblanks;
//~ while (1) {
//~ playMod(row, order, pattern);
//~ waitVSync();
//~ }
//~ }

15
src/mod.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#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_shine_hit_start[];
#define HITFILE _binary_HIT_shine_hit_start
#define printf ramsyscall_printf
extern uint16_t s_nextCounter;
void checkMusic();
void waitVSync();
void loadMod();
void playMod();

806
src/modplayer.c Normal file
View File

@ -0,0 +1,806 @@
/*
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 <stddef.h>
#include <stdint.h>
#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;
}
static 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);
}

150
src/modplayer.h Normal file
View File

@ -0,0 +1,150 @@
/*
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 <stdint.h>
// 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();

View File

@ -45,16 +45,16 @@ void resetSTR(STR * str)
void switchStrCh(STR ** str)
{
// Switch current STR channel
ramsyscall_printf("p0: %p - %d - ", *str, (*str)->channel);
printf("p0: %p - %d - ", *str, (*str)->channel);
sectorHeader->frameCount = 0;
*str = &menu[!((*str)->channel)];
ramsyscall_printf("p1: %p\n", *str);
printf("p1: %p\n", *str);
StSetChannel( (*str)->channel );
(*str)->endPlayback = 1;
}
void playSTR(STR ** str)
{
//~ ramsyscall_printf("Frame %d / %d,ch: %d, p: %p, m0: %p, m1: %p\n", sectorHeader->frameCount, (*str)->length, (*str)->channel, str, &menu[0], &menu[1]);
//~ printf("Frame %d / %d,ch: %d, p: %p, m0: %p, m1: %p\n", sectorHeader->frameCount, (*str)->length, (*str)->channel, str, &menu[0], &menu[1]);
// Use this area to draw the slices
RECT curSlice = { STR_POS_X,
STR_POS_Y,
@ -139,15 +139,4 @@ void playSTR(STR ** str)
drawMenu = 1;
}
}
//~ if ( (!(*str)->channel) )
//~ {
//~ FntPrint("Hello menu!\n");
//~ if ( sectorHeader->frameCount > 5 )
//~ {
//~ FntFlush(-1);
//~ } else if ( (sectorHeader->frameCount % 2) && sectorHeader->frameCount < 5 )
//~ {
//~ FntFlush(-1);
//~ }
//~ }
}

View File

@ -10,13 +10,14 @@
// CODEC library
#include <libpress.h>
// printf
#include "../../nolibgs_hello_worlds/thirdparty/nugget/common/syscalls/syscalls.h"
//~ #include "../../nolibgs_hello_worlds/thirdparty/nugget/common/syscalls/syscalls.h"
//~ #define printf ramsyscall_printf
#define SCREENXRES 320
#define SCREENYRES 240
#define STR_POS_X SCREENXRES
#define STR_POS_Y 0
// Ring Buffer size (reduce if flickering occurs)
#define RING_SIZE 16
#define RING_SIZE 24
#define PPW 1
#define DCT_MODE 2
typedef struct STR {