Trakker/src/main.cpp

955 lines
28 KiB
C++
Raw Normal View History

2023-02-24 22:34:35 -05:00
#include <fstream>
#include <iostream>
#include <cstdlib>
2023-09-13 23:14:31 -04:00
#include <stdio.h>
2023-09-14 17:33:01 -04:00
#include <stdlib.h>
#include <cmath>
2023-09-14 01:57:21 -04:00
#include <map>
2023-02-24 22:34:35 -05:00
#include <alsa/asoundlib.h>
#include <xmp.h>
#include <ncurses.h>
2023-09-14 01:57:21 -04:00
#include "trakker.h"
2023-09-13 23:14:31 -04:00
#include "trakker_version.h"
2023-02-24 22:34:35 -05:00
#define SAMPLERATE 48000
#define BUFFER_SIZE 250000
static std::string note_name[] = { "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B " };
static std::string pages[] = { "1. Info", "2. Pattern", "3. Bars", "4. Piano Roll", "5. Config", "6. Help" };
2023-09-13 23:14:31 -04:00
static char* device = "default";
2023-02-24 22:34:35 -05:00
2023-09-14 17:33:01 -04:00
char* file;
bool colorMode = 0; // 0 - Auto, 1 - Monochrome, 2 - 8-bit, 3 - Full
int mtype = XMP_MODE_AUTO;
int smix = 70;
2023-09-13 23:14:31 -04:00
int display = 0;
int mode = 0;
int vol;
int hMin = 0; int hMax = 2048;
int hOffset = 0;
int vMin = 0; int vMax = 2048;
int vOffset = 0;
int looped = 0;
int prMin = 255;
int prMax = 0;
2023-09-13 23:14:31 -04:00
bool stopped;
bool loop;
bool ptnChans = true;
bool dynamicRoll = true;
bool ptnOrder = true;
std::map<int, char> efxtable;
std::map<int, bool> efxmemtable;
2023-09-13 23:14:31 -04:00
2023-02-24 22:34:35 -05:00
int main(int argc, char *argv[]) {
2023-09-14 17:33:01 -04:00
printf("TRAKKER %s (using XMP %s)\n", TRAKKER_VERSION, xmp_version);
for (int a = 1; a < argc; ++a) {
if (strcmp(argv[a], "-h") == 0) {
printf("\n");
printf("Help\n");
printf("-h Show this message.\n");
printf("-d <num> Start on the specified panel.\n");
printf("-c <num> Force terminal color mode.\n");
printf(" 0 Auto (default)\n");
printf(" 1 Monochrome\n");
printf(" 2 8bit\n");
printf(" 3 Full\n");
printf("-s <num> Stereo Seperation\n");
exit(0);
} else if (strcmp(argv[a], "-d") == 0) {
int newdisplay = atoi(argv[a+1])-1;
if (newdisplay > 5 || newdisplay < 0)
2023-09-14 17:33:01 -04:00
fprintf(stderr, "Display argument is invalid.\n");
else {
printf("Setting display to \"%s\"\n", pages[newdisplay].c_str());
2023-09-14 17:33:01 -04:00
display = newdisplay;
}
a++;
} else if (strcmp(argv[a], "-c") == 0) {
colorMode = atoi(argv[a+1]);
a++;
} else if (strcmp(argv[a], "-s") == 0) {
int newmix = atoi(argv[a+1]);
if (newmix > 100 || newmix < 0)
fprintf(stderr, "Stero seperation argument is invalid.\n");
else {
printf("Setting Stero seperation to %i%%\n", newmix);
smix = newmix;
}
a++;
} else if (strcmp(argv[a], "-t") == 0) {
char* newmode = argv[a+1];
if (strcmp(newmode, "mod") == 0) {
mtype = XMP_MODE_MOD;
} else if (strcmp(newmode, "noisetracker") == 0) {
mtype = XMP_MODE_NOISETRACKER;
} else if (strcmp(newmode, "protracker") == 0) {
mtype = XMP_MODE_PROTRACKER;
} else if (strcmp(newmode, "s3m") == 0) {
mtype = XMP_MODE_S3M;
} else if (strcmp(newmode, "st3") == 0) {
mtype = XMP_MODE_ST3;
} else if (strcmp(newmode, "st3gus") == 0) {
mtype = XMP_MODE_ST3GUS;
} else if (strcmp(newmode, "xm") == 0) {
mtype = XMP_MODE_XM;
} else if (strcmp(newmode, "ft2") == 0) {
mtype = XMP_MODE_FT2;
} else if (strcmp(newmode, "it") == 0) {
mtype = XMP_MODE_IT;
} else if (strcmp(newmode, "itsmp") == 0) {
mtype = XMP_MODE_ITSMP;
}
a++;
} else if (!file) {
file = argv[a];
} else {
fprintf(stderr, "Unknown argument: %s\n", argv[a]);
exit(EXIT_FAILURE);
}
}
2023-09-14 13:45:46 -04:00
int err;
2023-02-24 22:34:35 -05:00
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
2023-09-13 23:14:31 -04:00
fprintf(stderr, "Playback open error: %s\n", snd_strerror(err));
2023-02-24 22:34:35 -05:00
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 2, SAMPLERATE, 1, BUFFER_SIZE)) < 0) {
2023-09-13 23:14:31 -04:00
fprintf(stderr, "Playback open error: %s\n", snd_strerror(err));
2023-02-24 22:34:35 -05:00
exit(EXIT_FAILURE);
}
2023-09-13 23:14:31 -04:00
printf("%s initialized.\n", "ALSA");
2023-02-24 22:34:35 -05:00
2023-09-13 23:14:31 -04:00
xc = xmp_create_context();
2023-09-14 17:33:01 -04:00
if (xmp_load_module(xc, file) != 0) {
fprintf(stderr, "Failed to load Module: %s\n", file);
2023-09-13 23:14:31 -04:00
exit(EXIT_FAILURE);
2023-09-14 17:33:01 -04:00
} else printf("Loaded Module: \"%s\"\n", file);
2023-02-24 22:34:35 -05:00
2023-09-13 23:14:31 -04:00
struct xmp_module_info xmi;
struct xmp_frame_info xfi;
2023-02-24 22:34:35 -05:00
initscr();
2023-09-14 17:33:01 -04:00
start_color();
if ((has_colors() == TRUE && colorMode == 0) || colorMode >= 2) {
if ((can_change_color() == TRUE && colorMode == 0) || colorMode == 3) {
// Primary Background
init_pair(1, COLOR_BLACK, 8);
// Inactive Section
init_pair(2, 8, 7);
// Active Section
init_pair(3, 0, 12);
// Display
init_pair(4, 7, COLOR_BLACK);
// Display Row#
init_pair(5, 3, COLOR_BLACK);
// Display Playhead
init_pair(6, COLOR_WHITE, COLOR_BLUE);
// Display Playhead Row#
init_pair(7, COLOR_YELLOW, COLOR_BLUE);
// Display Stopped Playhead
init_pair(8, COLOR_WHITE, COLOR_RED);
// Display Stopped Playhead Row#
init_pair(9, COLOR_YELLOW, COLOR_RED);
// Display VU Bar Low
init_pair(10, COLOR_WHITE, COLOR_GREEN);
// Display VU Bar Medium
init_pair(11, COLOR_WHITE, COLOR_YELLOW);
// Display VU Bar High
init_pair(12, COLOR_WHITE, COLOR_RED);
// Inverted Text
init_pair(13, COLOR_WHITE, COLOR_BLACK);
2023-09-13 23:14:31 -04:00
} else {
// Primary Background
init_pair(1, COLOR_BLACK, 8);
// Inactive Section
init_pair(2, COLOR_BLACK, COLOR_WHITE);
// Active Section
init_pair(3, COLOR_BLACK, COLOR_CYAN);
// Display
init_pair(4, COLOR_WHITE, COLOR_BLACK);
// Display Row#
init_pair(5, COLOR_YELLOW, COLOR_BLACK);
// Display Playhead
init_pair(6, COLOR_WHITE, COLOR_BLUE);
// Display Playhead Row#
init_pair(7, COLOR_YELLOW, COLOR_BLUE);
// Display Stopped Playhead
init_pair(8, COLOR_WHITE, COLOR_RED);
// Display Stopped Playhead Row#
init_pair(9, COLOR_YELLOW, COLOR_RED);
// Display VU Bar Low
init_pair(10, COLOR_WHITE, COLOR_GREEN);
// Display VU Bar Medium
init_pair(11, COLOR_WHITE, COLOR_YELLOW);
// Display VU Bar High
init_pair(12, COLOR_WHITE, COLOR_RED);
// Inverted Text
init_pair(13, COLOR_WHITE, COLOR_BLACK);
2023-09-13 23:14:31 -04:00
}
} else {
// Primary Background
init_pair(1, COLOR_BLACK, COLOR_BLACK);
// Inactive Section
init_pair(2, COLOR_WHITE, COLOR_BLACK);
// Active Section
init_pair(3, COLOR_BLACK, COLOR_WHITE);
// Display
init_pair(4, COLOR_WHITE, COLOR_BLACK);
// Display Row#
init_pair(5, COLOR_WHITE, COLOR_BLACK);
// Display Playhead
init_pair(6, COLOR_BLACK, COLOR_WHITE);
// Display Playhead Row#
init_pair(7, COLOR_BLACK, COLOR_WHITE);
// Display Stopped Playhead
init_pair(8, COLOR_WHITE, COLOR_BLACK);
// Display Stopped Playhead Row#
init_pair(9, COLOR_WHITE, COLOR_BLACK);
// Display VU Bar Low
init_pair(10, COLOR_WHITE, COLOR_WHITE);
// Display VU Bar Medium
init_pair(11, COLOR_WHITE, COLOR_WHITE);
// Display VU Bar High
init_pair(12, COLOR_WHITE, COLOR_WHITE);
// Inverted Text
init_pair(13, COLOR_WHITE, COLOR_BLACK);
2023-02-24 22:34:35 -05:00
}
cbreak();
noecho();
curs_set(0);
2023-09-13 23:14:31 -04:00
bkgd(COLOR_PAIR(1));
refresh();
2023-02-24 22:34:35 -05:00
2023-09-13 23:14:31 -04:00
createWindows();
2023-02-24 22:34:35 -05:00
2023-09-13 23:14:31 -04:00
keypad(stdscr, TRUE);
2023-02-24 22:34:35 -05:00
2023-09-13 23:14:31 -04:00
int row, pos;
xmp_get_module_info(xc, &xmi);
2023-02-24 22:34:35 -05:00
row = pos = -1;
generateEffectsTable(xmi.mod->type);
2023-09-14 17:33:01 -04:00
xmp_set_player(xc, XMP_PLAYER_MODE, mtype);
2023-09-13 23:14:31 -04:00
xmp_start_player(xc, SAMPLERATE, 0);
2023-09-14 17:33:01 -04:00
xmp_set_player(xc, XMP_PLAYER_MIX, smix);
2023-02-24 22:34:35 -05:00
2023-09-13 23:14:31 -04:00
int key;
bool displayChanged;
2023-09-14 17:33:01 -04:00
displayChanged = true;
while (true) {
2023-09-14 13:45:46 -04:00
xmp_get_frame_info(xc, &xfi);
2023-09-13 23:14:31 -04:00
if (xmp_play_frame(xc) != 0 && !stopped) break;
if (xfi.loop_count > looped && !loop) break;
2023-09-14 13:45:46 -04:00
else looped = xfi.loop_count;
keys:
timeout(stopped?-1:0);
2023-09-13 23:14:31 -04:00
if ((key = getch()) != 0) {
vol = xmp_get_player(xc, XMP_PLAYER_VOLUME);
2023-02-24 22:34:35 -05:00
switch (key) {
case KEY_RESIZE:
2023-09-13 23:14:31 -04:00
destroyWindows();
createWindows();
displayChanged = true; // Update top section.
2023-02-24 22:34:35 -05:00
break;
2023-09-13 23:14:31 -04:00
case 'q':
goto end; break;
case KEY_LEFT: case 'a': // Move Channels Left
if (hOffset > hMin) hOffset-=1;
2023-02-24 22:34:35 -05:00
break;
case KEY_RIGHT: case 'd': // Move Channels Right
if (hOffset < hMax) hOffset+=1;
2023-02-24 22:34:35 -05:00
break;
case KEY_UP: case 'w': // Seek Up
if (vOffset > vMin) vOffset-=1;
2023-02-24 22:34:35 -05:00
break;
case KEY_DOWN: case 's': // Seek Down
if (vOffset < vMax) vOffset+=1;
break;
case 'A': // Move Channels Left (faster)
if (hOffset > hMin) hOffset-=5;
break;
case 'D': // Move Channels Right (faster)
if (hOffset < hMax) hOffset+=5;
break;
case 'W': // Seek Up (faster)
if (vOffset > vMin) vOffset-=5;
break;
case 'S': // Seek Down (faster)
if (vOffset < vMax) vOffset+=5;
break;
2023-09-14 01:57:21 -04:00
case KEY_PPAGE:
if (vOffset > vMin + LINES-6) vOffset-=(LINES-5);
else vOffset = vMin;
2023-09-14 01:57:21 -04:00
break;
case KEY_NPAGE:
if (vOffset < vMax - LINES-6) vOffset+=(LINES-5);
else vOffset = vMax;
2023-09-14 01:57:21 -04:00
break;
case 10: case ' ': // Pause/Play
2023-09-13 23:14:31 -04:00
xfi.time-=20;
stopped = !stopped;
2023-02-24 22:34:35 -05:00
break;
case '+': case '=':
2023-09-13 23:14:31 -04:00
if (vol < 200) xmp_set_player(xc, XMP_PLAYER_VOLUME, vol+=5);
2023-02-24 22:34:35 -05:00
break;
case '-':
2023-09-13 23:14:31 -04:00
if (vol > 0) xmp_set_player(xc, XMP_PLAYER_VOLUME, vol-=5);
2023-02-24 22:34:35 -05:00
break;
2023-09-14 17:33:01 -04:00
case '[':
if (smix > 0) smix-=5;
xmp_set_player(xc, XMP_PLAYER_MIX, smix);
break;
case ']':
if (smix < 100) smix+=5;
xmp_set_player(xc, XMP_PLAYER_MIX, smix);
break;
2023-02-24 22:34:35 -05:00
case 'l':
2023-09-13 23:14:31 -04:00
loop = !loop;
2023-02-24 22:34:35 -05:00
break;
2023-03-02 22:28:55 -05:00
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
2023-09-13 23:14:31 -04:00
display = key-49;
displayChanged = true;
2023-03-02 22:28:55 -05:00
break;
case 27: // ALT
if ((key = getch()) != 0) {
switch (key) {
case '1':
ptnChans = !ptnChans;
break;
case '2':
prMin = 255; prMax = 0;
dynamicRoll = !dynamicRoll;
break;
case '3':
ptnOrder = !ptnOrder;
break;
}
}
break;
case 'R':
hOffset = vOffset = 0;
break;
2023-02-24 22:34:35 -05:00
};
displayHeader(&xmi, &xfi);
2023-02-24 22:34:35 -05:00
}
2023-09-14 13:45:46 -04:00
if (displayChanged) {
werase(dis);
werase(tab);
hOffset = 0;
2023-09-13 23:14:31 -04:00
vOffset = 0;
2023-09-14 13:45:46 -04:00
move(0, 0);
for (int d = 0; d < sizeof(pages)/sizeof(*pages); d++) {
printw(" ");
chtype tpair;
if (display == d) tpair = COLOR_PAIR(3);
else tpair = COLOR_PAIR(2);
attron(tpair);
printw(" "); printw(pages[d].c_str()); printw(" ");
2023-09-14 13:45:46 -04:00
attroff(tpair);
printw(" ");
}
mode = 0;
wmove(tab, 0, 0);
displayChanged = false;
}
2023-09-13 23:14:31 -04:00
if (!stopped) {
if (xfi.pos != pos) {
pos = xfi.pos;
2023-02-25 00:03:32 -05:00
row = -1;
}
2023-09-13 23:14:31 -04:00
if (xfi.row != row) {
row = xfi.row;
displayHeader(&xmi, &xfi);
2023-02-24 22:34:35 -05:00
}
2023-09-13 23:14:31 -04:00
wrefresh(tab);
wrefresh(dis);
frames = snd_pcm_bytes_to_frames(handle, xfi.buffer_size);
if (snd_pcm_writei(handle, xfi.buffer, frames) < 0) {
snd_pcm_prepare(handle);
}
2023-02-25 00:03:32 -05:00
} else goto keys;
2023-02-24 22:34:35 -05:00
}
2023-09-13 23:14:31 -04:00
end:
2023-02-24 22:34:35 -05:00
clrtoeol();
refresh();
endwin();
2023-09-13 23:14:31 -04:00
xmp_end_player(xc);
xmp_release_module(xc);
2023-09-14 13:45:46 -04:00
xmp_free_context(xc);
2023-09-13 23:14:31 -04:00
if ((err = snd_pcm_drain(handle)) < 0) {
2023-02-24 22:34:35 -05:00
printf("snd_pcm_drain failed: %s\n", snd_strerror(err));
2023-09-13 23:14:31 -04:00
}
2023-02-24 22:34:35 -05:00
snd_pcm_close(handle);
2023-09-13 23:14:31 -04:00
printf("Goodbye!\n");
2023-02-24 22:34:35 -05:00
return 0;
}
2023-02-25 00:03:32 -05:00
2023-09-13 23:14:31 -04:00
void destroyWindows() {
delwin(tab);
delwin(dis);
2023-02-25 00:03:32 -05:00
}
2023-09-13 23:14:31 -04:00
void createWindows() {
tab = newwin(LINES-1, COLS, 1, 0);
wmove(tab, 0, 0);
wprintw(tab, "");
wbkgd(tab, COLOR_PAIR(3));
wrefresh(tab);
2023-09-13 23:14:31 -04:00
dis = newwin(LINES-4, COLS-2, 3, 1);
wmove(dis, 0, 0);
wprintw(dis, "");
wbkgd(dis, COLOR_PAIR(4));
wrefresh(dis);
}
void displayHeader(xmp_module_info *mi, xmp_frame_info *fi) {
2023-09-13 23:14:31 -04:00
werase(dis);
2023-09-14 01:57:21 -04:00
wclrtoeol(tab);
mvwaddstr(tab, 0, 1, mi->mod->name);
mvwprintw(
tab,
2023-09-13 23:14:31 -04:00
0, COLS-12,
"%02u:%02u/%02u:%02u",
((fi->time / 1000) / 60) % 60,
(fi->time / 1000) % 60,
((fi->total_time / 1000) / 60) % 60,
(fi->total_time / 1000) % 60
);
wmove(tab, 1, 0);
wclrtoeol(tab);
2023-09-13 23:14:31 -04:00
mvwprintw(tab, 1, COLS-10, "VOL: %i%%", vol);
mvwprintw(tab, 1, 1, "%i/%ibpm", fi->speed, fi->bpm);
mvwprintw(tab, LINES-2, 1, "%s", stopped?"STOPPED":"PLAYING");
2023-09-13 23:14:31 -04:00
if (display == 0) {
displayInfo(mi, fi);
2023-09-13 23:14:31 -04:00
} else if (display == 1) {
displayPatterns(mi, fi);
2023-09-13 23:14:31 -04:00
} else if (display == 2) {
displayVolumes(mi, fi);
2023-09-13 23:14:31 -04:00
} else if (display == 3) {
displayNoteRoll(mi, fi);
} else if (display == 4) {
displayPlayer();
2023-09-13 23:14:31 -04:00
} else {
displayHelp();
2023-09-13 23:14:31 -04:00
}
refresh();
2023-09-13 23:14:31 -04:00
wrefresh(dis);
wrefresh(tab);
}
void displayInfo(xmp_module_info *mi, xmp_frame_info *fi) {
2023-09-13 23:14:31 -04:00
wattron(dis, A_BOLD);
mvwaddstr(dis, 1-vOffset, 1, "Format:");
mvwaddstr(dis, 2-vOffset, 1, "Channels:");
mvwaddstr(dis, 4-vOffset, 1, "Instruments:");
mvwaddstr(dis, (6+mi->mod->ins)-vOffset, 1, "Samples:");
2023-09-13 23:14:31 -04:00
wattroff(dis, A_BOLD);
mvwprintw(dis, 1-vOffset, 16, mi->mod->type);
mvwprintw(dis, 2-vOffset, 16, "%i", mi->mod->chn);
mvwprintw(dis, 4-vOffset, 16, "%i", mi->mod->ins);
mvwprintw(dis, (6+mi->mod->ins)-vOffset, 16, "%i", mi->mod->smp);
2023-09-13 23:14:31 -04:00
2023-09-14 01:57:21 -04:00
for (int xi = 0; xi < mi->mod->ins; xi++) {
mvwprintw(dis, xi+5-vOffset, 1, "[%02X] %s", xi, mi->mod->xxi[xi].name);
2023-09-14 01:57:21 -04:00
}
for (int xs = 0; xs < mi->mod->smp; xs++) {
mvwprintw(dis, xs+(7+mi->mod->ins)-vOffset, 1, "[%02X] %s", xs, mi->mod->xxs[xs].name);
2023-09-13 23:14:31 -04:00
}
}
void displayPatterns(xmp_module_info *mi, xmp_frame_info *fi) {
2023-09-13 23:14:31 -04:00
int chnsize = 15;
if (ptnOrder) {
for (int j = 0; j < mi->mod->len; j++) {
if (mi->mod->xxo[j] == 0xFF) continue;
chtype patpair = (j == fi->pos)?COLOR_PAIR(6):COLOR_PAIR(4);
wattron(dis, patpair);
mvwprintw(dis, LINES-5, (COLS/2)+(j*3)-(fi->pos*3)-3, "%02X", mi->mod->xxo[j]);
wattroff(dis, patpair);
}
2023-09-13 23:14:31 -04:00
}
wattroff(tab, COLOR_PAIR(5));
for (int y = 0; y < LINES - (5-(!ptnOrder)); y++) {
if (ptnChans && y == 0) {
chtype chnvw = COLOR_PAIR(5);
chtype vollo = COLOR_PAIR(10);
chtype volmd = COLOR_PAIR(11);
chtype volhi = COLOR_PAIR(12);
int cxpos = 3;
wmove(dis, 0, cxpos);
for (int i = 0; i < mi->mod->chn; i++) {
struct xmp_channel_info cinfo = fi->channel_info[i];
2024-02-26 21:16:24 -05:00
int voll = (cinfo.volume * (256-cinfo.pan)/256);
if (voll >= 48) wattron(dis, volhi);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, volhi);
2024-02-26 21:16:24 -05:00
if (voll >= 32) wattron(dis, volmd);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, volmd);
2024-02-26 21:16:24 -05:00
if (voll >= 16) wattron(dis, vollo);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, vollo);
2024-02-26 21:16:24 -05:00
if (voll >= 8) wattron(dis, vollo);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, vollo);
if (voll > 0) wattron(dis, vollo);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, vollo);
wattron(dis, chnvw);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
char *cbuf = new char[2];
sprintf(cbuf, "%02X", i);
mvwaddch(dis, 0, (cxpos++)-hOffset, cbuf[0]);
mvwaddch(dis, 0, (cxpos++)-hOffset, cbuf[1]);
free(cbuf);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, chnvw);
2024-02-26 21:16:24 -05:00
int volr = (cinfo.volume * cinfo.pan/256);
if (volr > 0) wattron(dis, vollo);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, vollo);
2024-02-26 21:16:24 -05:00
if (volr >= 8) wattron(dis, vollo);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, vollo);
2024-02-26 21:16:24 -05:00
if (volr >= 16) wattron(dis, vollo);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, vollo);
2024-02-26 21:16:24 -05:00
if (volr >= 32) wattron(dis, volmd);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, volmd);
2024-02-26 21:16:24 -05:00
if (volr >= 48) wattron(dis, volhi);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
wattroff(dis, volhi);
mvwaddch(dis, 0, (cxpos++)-hOffset, ' ');
}
continue;
}
2023-02-25 00:03:32 -05:00
int trow = (fi->row - ((LINES - 2) / 2))+y;
2023-09-13 23:14:31 -04:00
if (trow > fi->num_rows-1 || trow < 0) { continue; }
chtype numpair = COLOR_PAIR((trow==fi->row)?(stopped?9:7):5);
chtype rowpair = COLOR_PAIR((trow==fi->row)?(stopped?8:6):4);
if (trow == fi->row) wattron(dis, A_BOLD);
wmove(dis, y, 0);
wattron(dis, numpair);
wprintw(dis, "%02X", trow);
wattroff(dis, numpair);
wattron(dis, rowpair);
for (int i = 0; i < mi->mod->chn+1; i++) {
if (i >= mi->mod->chn) {
mvwaddch(dis, y, ((i*chnsize)+2)-hOffset, '|');
2023-02-25 00:03:32 -05:00
break;
}
int track = mi->mod->xxp[fi->pattern]->index[i];
struct xmp_event event = mi->mod->xxt[track]->event[trow];
2023-09-13 23:14:31 -04:00
char *lnbuf = new char[chnsize+1];
char *note = new char[4];
char *ins = new char[3];
char *vol = new char[4];
char *efx = new char[4];
if (event.note > 0x80)
snprintf(note, 4, "===");
else if (event.note > 0)
snprintf(note, 4, "%s%d", note_name[event.note % 12].c_str(), event.note / 12);
2023-09-13 23:14:31 -04:00
else
snprintf(note, 4, "...");
if (event.ins > 0) snprintf(ins, 3, "%02X", event.ins);
else snprintf(ins, 3, "..");
if (event.vol != 0)
snprintf(vol, 4, "v%02X", event.vol-1);
else if (event.note > 0 && event.note <= 0x80)
2023-09-13 23:14:31 -04:00
snprintf(vol, 4, "v40");
else snprintf(vol, 4, "...");
2023-09-13 23:14:31 -04:00
char f1;
if ((efxmemtable[event.fxt] || event.fxp != 0) && (f1 = efxtable[event.fxt]) != NULL) snprintf(efx, 4, "%c%02X", f1, event.fxp);
2023-09-13 23:14:31 -04:00
else snprintf(efx, 4, "...");
sprintf(lnbuf, "|%s %s %s %s", note, ins, vol, efx);
for (int z = 0; z < chnsize; z++) {
if (((i*chnsize)+2+z)-hOffset < 2) continue;
mvwaddch(dis, y, ((i*chnsize)+2+z)-hOffset, lnbuf[z]);
2023-02-25 00:03:32 -05:00
}
2023-09-13 23:14:31 -04:00
free(lnbuf);
free(note);
free(ins);
free(vol);
free(efx);
2023-02-25 00:03:32 -05:00
}
2023-09-13 23:14:31 -04:00
wattroff(dis, rowpair);
if (trow == fi->row) wattroff(dis, A_BOLD);
2023-02-25 00:03:32 -05:00
}
}
void displayVolumes(xmp_module_info *mi, xmp_frame_info *fi) {
int chns = mi->mod->chn;
2023-09-13 23:14:31 -04:00
chtype no_pair = COLOR_PAIR(5);
for (int y = vOffset; y < chns; y++) {
if (y > (LINES - 4)+vOffset || y < 0) continue;
struct xmp_channel_info cinfo = fi->channel_info[y];
2023-09-13 23:14:31 -04:00
if (y > (LINES - 3)+vOffset) break;
wmove(dis, y-vOffset, 0);
int cvol = (cinfo.volume * (COLS - 5)) / (64 * (vol / 100));
wattron(dis, no_pair);
wprintw(dis, "%02X", y);
wattroff(dis, no_pair);
for (int c = 0; c < COLS - 5; c++) {
if (c < cvol) waddstr(dis, "#");
else waddstr(dis, " ");
}
}
}
void displayNoteRoll(xmp_module_info *mi, xmp_frame_info *fi) {
int ins = mi->mod->ins;
2023-09-13 23:14:31 -04:00
chtype no_pair = COLOR_PAIR(5);
2024-02-27 22:14:13 -05:00
if (dynamicRoll) {
for (int l = 0; l < (96/12); l++) {
int cnote = (l*12);
if (cnote < prMin && l == (prMin / 12)) {
wmove(dis, 0, 0);
wprintw(dis, "<%i", l);
} else if (cnote <= prMax && cnote >= prMin) {
wmove(dis, 0, (((cnote - prMin) * (COLS - 5)) / (prMax - prMin))+3);
wprintw(dis, "%i", l);
2024-02-27 19:02:49 -05:00
}
}
2024-02-27 22:14:13 -05:00
}
for (int y = vOffset; y < ins; y++) {
if (!dynamicRoll) { prMin = 0; prMax = 144; }
2024-02-27 22:25:07 -05:00
if (y > (LINES - 6)+vOffset || y < 0) continue;
2024-02-27 22:14:13 -05:00
wmove(dis, (y-vOffset)+1, 0);
2023-09-13 23:14:31 -04:00
wattron(dis, no_pair);
2024-02-27 22:14:13 -05:00
wprintw(dis, "%02X", y+1);
2023-09-13 23:14:31 -04:00
wattroff(dis, no_pair);
wclrtoeol(dis);
for (int c = 0; c < mi->mod->chn; c++) {
struct xmp_channel_info cinfo = fi->channel_info[c];
if (dynamicRoll) {
if (cinfo.note < prMin)
prMin = cinfo.note-1;
else if (cinfo.note > prMax && cinfo.note != 255)
prMax = cinfo.note+1;
}
int note = ((cinfo.note - prMin) * (COLS - 5)) / (dynamicRoll?(prMax - prMin):96);
if (cinfo.instrument != y) continue;
2024-02-27 22:14:13 -05:00
wmove(dis, (y-vOffset)+1, note+3);
if (cinfo.volume >= 32)
waddstr(dis, "#");
2024-02-27 22:14:13 -05:00
else if (cinfo.volume >= 16 && (char)mvwinch(dis, (y-vOffset)+1, note+3) != '#')
waddstr(dis, "=");
2024-02-27 22:14:13 -05:00
else if (cinfo.volume > 0 && ((char)mvwinch(dis, (y-vOffset)+1, note+3) != '-' || (char)mvwinch(dis, (y-vOffset)+1, note+3) != '#'))
waddstr(dis, "-");
}
2024-02-27 22:14:13 -05:00
wmove(dis, y, COLS-4);
}
}
void displayPlayer() {
wattron(dis, A_BOLD);
mvwaddstr(dis, 1-vOffset, 1, "Loop");
mvwaddstr(dis, 2-vOffset, 1, "Stereo Seperation");
mvwaddstr(dis, 3-vOffset, 1, "Color Mode");
mvwaddstr(dis, 5-vOffset, 1, "1. Pattern Channels");
mvwaddstr(dis, 6-vOffset, 1, "2. Dynamic Note Roll");
mvwaddstr(dis, 7-vOffset, 1, "3. Pattern Order");
wattroff(dis, A_BOLD);
mvwaddstr(dis, 1-vOffset, 20, loop?"[ #] YES":"[# ] NO");
mvwprintw(dis, 2-vOffset, 20, "%i%%", smix);
mvwprintw(dis, 3-vOffset, 20, "%i", colorMode);
mvwaddstr(dis, 5-vOffset, 22, ptnChans?"[ #] YES":"[# ] NO");
mvwaddstr(dis, 6-vOffset, 22, dynamicRoll?"[ #] YES":"[# ] NO");
mvwaddstr(dis, 7-vOffset, 22, ptnOrder?"[ #] YES":"[# ] NO");
}
void displayHelp() {
wattron(dis, A_BOLD);
mvwaddstr(dis, 1-vOffset, 2, " / ######## TRAKKER");
mvwaddstr(dis, 2-vOffset, 2, " / // / libxmp");
mvwaddstr(dis, 3-vOffset, 2, "/ // / ");
mvwaddstr(dis, 4-vOffset, 2, " // / by Nick G. (Wirlaburla)");
mvwaddstr(dis, 5-vOffset, 2, " // / Licensed under the GPLv3");
mvwaddstr(dis, 7-vOffset, 1, "Play/Pause........");
mvwaddstr(dis, 8-vOffset, 1, "Switch Tabs.......");
mvwaddstr(dis, 9-vOffset, 1, "Fast Shift........");
mvwaddstr(dis, 10-vOffset, 1, "Horizontal Shift..");
mvwaddstr(dis, 11-vOffset, 1, "Vertical Shift....");
mvwaddstr(dis, 12-vOffset, 1, "Volume............");
mvwaddstr(dis, 13-vOffset, 1, "Refresh...........");
mvwaddstr(dis, 14-vOffset, 1, "Vertical Scroll...");
mvwaddstr(dis, 15-vOffset, 1, "Stero Seperation..");
mvwaddstr(dis, 16-vOffset, 1, "Option Toggle.....");
wattroff(dis, A_BOLD);
mvwaddstr(dis, 1-vOffset, 25, TRAKKER_VERSION);
mvwaddstr(dis, 2-vOffset, 25, xmp_version);
mvwaddstr(dis, 7-vOffset, 20, "Space");
mvwaddstr(dis, 8-vOffset, 20, "Tab");
mvwaddstr(dis, 9-vOffset, 20, "Shift + W A S D");
mvwaddstr(dis, 10-vOffset, 20, "Left Arrow / Right Arrow OR A / D");
mvwaddstr(dis, 11-vOffset, 20, "Up Arrow / Down Arrow OR W / S");
mvwaddstr(dis, 12-vOffset, 20, "+ / -");
mvwaddstr(dis, 13-vOffset, 20, "R");
mvwaddstr(dis, 14-vOffset, 20, "Page-Up / Page-Down");
mvwaddstr(dis, 15-vOffset, 20, "[ / ]");
mvwaddstr(dis, 16-vOffset, 20, "Alt + 0-9");
}
void generateEffectsTable(char* type) {
if (isPartOf(type, "669")) {
addToEffects(96, 'A', true);
2023-09-14 13:45:46 -04:00
addToEffects(97, 'B', true);
addToEffects(98, 'C', true);
addToEffects(99, 'D', true);
addToEffects(100, 'E', true);
addToEffects(126, 'F', true);
} else if (isPartOf(type, "Farandole")) {
addToEffects(249, '1', true);
2023-09-14 13:45:46 -04:00
addToEffects(248, '2', true);
addToEffects(122, '3', true);
addToEffects(251, '4', true);
addToEffects(254, '5', true);
addToEffects(4, '6', true);
addToEffects(256, '7', true);
addToEffects(252, '8', true);
addToEffects(123, '9', true);
addToEffects(250, 'C', true);
addToEffects(15, 'F', true);
} else if (isPartOf(type, "Imago Orpheus")) {
addToEffects(1, '1', true);
2023-09-14 13:45:46 -04:00
addToEffects(2, '2', true);
addToEffects(3, '3', true);
addToEffects(4, '4', true);
addToEffects(5, '5', true);
addToEffects(6, '6', true);
addToEffects(7, '7', true);
addToEffects(8, '8', true);
addToEffects(9, '9', true);
addToEffects(10, 'A', true);
addToEffects(11, 'B', true);
addToEffects(12, 'C', true);
addToEffects(13, 'D', true);
addToEffects(14, 'E', true);
addToEffects(15, 'F', true);
addToEffects(16, 'G', true);
addToEffects(17, 'H', true);
addToEffects(18, 'I', true);
addToEffects(19, 'J', true);
addToEffects(20, 'K', true);
addToEffects(21, 'L', true);
addToEffects(22, 'M', true);
addToEffects(23, 'N', true);
addToEffects(24, 'O', true);
addToEffects(25, 'P', true);
addToEffects(26, 'Q', true);
addToEffects(27, 'R', true);
addToEffects(28, 'S', true);
addToEffects(29, 'T', true);
addToEffects(30, 'U', true);
addToEffects(31, 'V', true);
addToEffects(32, 'W', true);
addToEffects(33, 'X', true);
addToEffects(34, 'Y', true);
addToEffects(35, 'Z', true);
} else if (isPartOf(type, "S3M")) {
addToEffects(163, 'A', false);
2023-09-14 13:45:46 -04:00
addToEffects(11, 'B', true);
addToEffects(13, 'C', true);
addToEffects(10, 'D', true);
addToEffects(2, 'E', true);
addToEffects(1, 'F', true);
addToEffects(3, 'G', true);
addToEffects(4, 'H', true);
addToEffects(29, 'I', true);
addToEffects(180, 'J', true);
addToEffects(6, 'K', true);
addToEffects(5, 'L', true);
addToEffects(9, 'O', true);
addToEffects(27, 'Q', true);
addToEffects(7, 'R', true);
addToEffects(254, 'S', true);
addToEffects(171, 'T', false);
addToEffects(172, 'U', true);
addToEffects(16, 'V', true);
addToEffects(8, 'X', true);
addToEffects(141, 'X', true);
addToEffects(14, 'S', true);
} else if (isPartOf(type, "IT")) {
addToEffects(163, 'A', false);
addToEffects(11, 'B', true);
addToEffects(142, 'C', true);
addToEffects(10, 'D', true);
addToEffects(2, 'E', true);
addToEffects(1, 'F', true);
addToEffects(3, 'G', true);
addToEffects(4, 'H', true);
addToEffects(29, 'I', true);
addToEffects(180, 'J', true);
addToEffects(6, 'K', true);
addToEffects(5, 'L', true);
addToEffects(128, 'M', true);
addToEffects(129, 'N', true);
addToEffects(9, 'O', false); // OpenMPT doc states this should be true but there is zero effect at 00 (0*256=0)
addToEffects(137, 'P', true);
addToEffects(27, 'Q', true);
addToEffects(7, 'R', true);
addToEffects(254, 'S', true);
addToEffects(135, 'T', false);
addToEffects(172, 'U', true);
addToEffects(16, 'V', true);
addToEffects(17, 'W', true);
addToEffects(8, 'X', true);
addToEffects(138, 'Y', true);
addToEffects(141, 'S', true);
addToEffects(136, 'S', true);
addToEffects(14, 'S', true);
addToEffects(192, 'c', true);
addToEffects(193, 'd', true);
addToEffects(194, 'a', true);
addToEffects(195, 'b', true);
addToEffects(132, 'S', true);
addToEffects(139, 'S', true);
addToEffects(140, 'S', true);
} else if (isPartOf(type, "LIQ")) {
addToEffects(0, 'A', true);
addToEffects(171, 'B', true);
addToEffects(13, 'C', true);
addToEffects(2, 'D', true);
addToEffects(172, 'F', true);
addToEffects(11, 'J', true);
addToEffects(10, 'L', true);
addToEffects(14, 'M', true);
addToEffects(3, 'N', true);
addToEffects(9, 'O', true);
2023-09-14 13:45:46 -04:00
addToEffects(163, 'S', true);
addToEffects(7, 'T', true);
addToEffects(1, 'U', true);
addToEffects(4, 'V', true);
addToEffects(5, 'X', true);
addToEffects(6, 'Y', true);
} else if (isPartOf(type, "Oktalyzer")) {
addToEffects(1, '1', true);
addToEffects(2, '2', true);
addToEffects(112, '0', true);
addToEffects(113, '0', true);
addToEffects(114, '0', true);
addToEffects(115, '6', true);
addToEffects(116, '5', true);
addToEffects(156, '6', true);
addToEffects(11, 'B', true);
addToEffects(15, 'F', true);
2023-09-14 13:45:46 -04:00
addToEffects(157, '5', true);
addToEffects(12, 'C', true);
addToEffects(10, 'A', true);
addToEffects(174, 'E', true);
addToEffects(17, 'E', true);
addToEffects(0, '0', true);
} else if (isPartOf(type, "STX")) {
addToEffects(15, 'A', true);
addToEffects(11, 'B', true);
addToEffects(13, 'C', true);
addToEffects(10, 'D', true);
addToEffects(2, 'E', true);
addToEffects(1, 'F', true);
addToEffects(3, 'G', true);
addToEffects(4, 'H', true);
addToEffects(29, 'I', true);
addToEffects(0, 'J', true);
} else if (isPartOf(type, "Funk")) {
addToEffects(121, 'A', true);
addToEffects(120, 'B', true);
addToEffects(122, 'C', true);
addToEffects(123, 'D', true);
addToEffects(124, 'G', true);
addToEffects(125, 'H', true);
addToEffects(0, 'L', true);
addToEffects(12, 'N', true);
addToEffects(127, 'O', true);
addToEffects(14, 'O', true);
addToEffects(15, 'O', true);
2023-09-14 01:57:21 -04:00
} else {
addToEffects(0, '0', false);
addToEffects(1, '1', false);
addToEffects(2, '2', false);
addToEffects(3, '3', true);
addToEffects(4, '4', true);
addToEffects(5, '5', false);
addToEffects(6, '6', false);
addToEffects(7, '7', true);
addToEffects(8, '8', true);
addToEffects(9, '9', true);
addToEffects(10, 'A', false);
addToEffects(11, 'B', true);
addToEffects(12, 'C', true);
addToEffects(13, 'D', true);
addToEffects(14, 'E', false);
addToEffects(15, 'F', true);
addToEffects(16, 'G', true);
addToEffects(27, 'Q', true);
addToEffects(181, 'P', true);
addToEffects(17, 'H', true);
addToEffects(21, 'L', true);
addToEffects(164, 'c', true);
addToEffects(33, 'X', true);
addToEffects(20, 'K', true);
addToEffects(25, 'P', true);
addToEffects(29, 'T', true);
addToEffects(146, '4', true);
addToEffects(160, 'x', true);
addToEffects(161, 'x', true);
addToEffects(171, 'F', true);
2023-09-14 01:57:21 -04:00
}
}
// We do not care for previous effects (memory = true) because it's a tracker.
// 'mem' is only for discarding zero values.
void addToEffects(int id, char efx, bool mem) {
efxtable[id] = efx;
efxmemtable[id] = mem;
}
2023-09-14 01:57:21 -04:00
bool isPartOf(char* w1, char* w2) {
int i=0;
int j=0;
while(w1[i]!='\0'){
2023-09-14 13:45:46 -04:00
if(w1[i] == w2[j]) {
int init = i;
while (w1[i] == w2[j] && w2[j]!='\0') {
j++;
i++;
}
if(w2[j]=='\0') {
return true;
}
j=0;
}
i++;
}
2023-09-14 01:57:21 -04:00
return false;
2023-09-13 23:14:31 -04:00
}