Advertisement
Guest User

3kr near-alpha w/ interface

a guest
Aug 14th, 2016
139
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 27.44 KB | None | 0 0
  1. #include <pc.h>
  2. #include <stdio.h>
  3. #include <conio.h>
  4. /*
  5.     ! 3kr [near-alpha] -- a tracker for the OPL3 synth chip found on a bunch
  6.       of SoundBlaster cards from the 90s
  7.     ! the tracker interface or anything substantial isn't here yet, but you
  8.       could probably figure out how to work with the chip yourself from this
  9.     ! haven't tested it on real hardware yet, only DOSBox so far
  10.    
  11.     BUGS:
  12.         * none known, it's all merely unfinished
  13.    
  14.     Currently implemented:
  15.         * an unfinished pattern editor without saving or song playing
  16.         * it does play sound when you hit on the keys though
  17.     Still to-do (in descending order of implementation):
  18.         * interface
  19.         * play routine
  20.         * file saving
  21.         * refactor (code's pretty messy+ugly and littered with dead functions)
  22.        
  23.     System requirements are probably like a 386 with 8MB RAM with VGA
  24.     running DOS 5 or better.
  25.     I'm guessing thoughm and would probably say go for a 486, 16MB RAM, DOS 6.
  26.     Not that it matters here in 2016, where even if you ran this on real HW
  27.     it'd probably be a Pentium II machine with 24MB RAM or something, haha.
  28.    
  29.     As a DJGPP program, it needs CWSDPMI (or another compatible DPMI extender.
  30.     Build with DJGPP, nothing special.
  31.     Ctrl+q is exit.
  32.     There aren't immediately obvious bugs as far as I've seen now.
  33.     Everything is merely unfinished.
  34. */
  35.  
  36. /*
  37. Copyright (c) 2016, B.M.Deeal (null1024) <[email protected]>
  38.  
  39. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
  40.  
  41. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  42. */
  43.  
  44. typedef struct {
  45.     unsigned char data[4000]; /* Exact size of 80x25 screen buffer. */
  46.     int x, y, fg, bg;
  47. } Display;
  48.  
  49. /* Edit the attribute of a character cell. */
  50. void dputAttr(Display *display, int x, int y, int fg, int bg) {
  51.     unsigned char result_color;
  52.     result_color=(fg & 0x0F) | ((bg<<4) & 0xF0);
  53.     display->data[x*2+y*160+1] = result_color;
  54. }
  55. /* Just dumps a character into a cell. */
  56. void dputChar(Display *display, int x, int y, char c) {
  57.     display->data[x*2+y*160] = c;
  58. }
  59.  
  60. /* Put a bunch of characters onto the screen at a specific position. */
  61. void dputString(Display *display, int x, int y, char *str) {
  62.     int ii, current=0;
  63.     if (y >= 25) { return; }
  64.     for (ii=x; ii<80; ii++) {
  65.         if (str[current] == '\0') { return; }
  66.         dputChar(display, ii, y, str[current]);
  67.         current++;
  68.     }
  69. }
  70.  
  71. /* Print a line to the screen buffer, doesn't bother with scrolling.*/
  72. /* Supports \n for newline. */
  73. void dputLine(Display *display, char *str) {
  74.     int current=0, push;
  75.     while (1) {
  76.         push=1;
  77.         if (str[current] == '\0') { return; }
  78.         if (str[current] == '\n' || display->x >= 80) {
  79.             display->y++;
  80.             display->x=0;
  81.             push=0;
  82.         }
  83.         if (display->y >= 25) { return; }
  84.         if (str[current] != '\n') {
  85.             dputChar(display, display->x, display->y, str[current]);
  86.             dputAttr(display, display->x, display->y,
  87.                      display->fg, display->bg);
  88.         }
  89.         if (push) {
  90.             display->x++;
  91.         }
  92.         current++;
  93.     }
  94. }
  95.  
  96. /* Empty the buffer, set the background color. */
  97. void dclearScr(Display *display, int fg, int bg) {
  98.     int ii;
  99.     unsigned char result_color;
  100.     result_color=(fg & 0x0F) | ((bg<<4) & 0xF0);
  101.     display->fg=fg;
  102.     display->bg=bg;
  103.     for (ii=0; ii<=3998; ii+=2) {
  104.         display->data[ii]=' ';
  105.         display->data[ii+1]=result_color;
  106.     }
  107. }
  108.  
  109. int opl_base=0x220; /* todo: read this from BLASTER environment variable */
  110.  
  111. /* The operator register offsets are right next to each other in this table, so */
  112. /* you'd pick which one by doing (channel*2+odd). */
  113. /* IIRC, odd entries are actually the carrier.*/
  114. int reg_tbl[] = {0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x08, 0x0B, 0x09,
  115.                  0x0C, 0x0A, 0x0D, 0x10, 0x13, 0x11, 0x14, 0x12, 0x15};
  116.  
  117. /* Notes starting from C, increasing chromatically. */
  118. int note_tbl[] = {343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647};
  119.  
  120. /* Name for each note. */
  121. char *note_text_tbl[12] = {
  122.     "C-", "C#", "D-",
  123.     "D#", "E-", "F-",
  124.     "F#", "G-", "G#",
  125.     "A-", "A#", "B-",
  126. };
  127.  
  128. /* Generic OPL3 write function. */
  129. void writeOPL3(int reg, int data, int bank) {
  130.     /* Select given register bank: */
  131.     if (bank=1) { bank=3; } else { bank=1; }
  132.     /* Select register, write to register: */
  133.     outp(opl_base, reg);
  134.     inp(opl_base); inp(opl_base); /* Reads for delay, but not enough for OPL2. */
  135.     outp(opl_base+bank, (unsigned int)data);
  136.     inp(opl_base); inp(opl_base);
  137. }
  138.  
  139. /* Resets the test bits, enables waveform select. */
  140. void resetTest(void) {
  141.     writeOPL3(0x01,0b00100000,0);
  142. }
  143.  
  144. /* For writing to operator settings (adsr and the lot). */
  145. /* op is 0 to 35. */
  146. void writeOp(unsigned int op, int reg, int data) {
  147.     int bank=0;
  148.     resetTest();
  149.     if (op >= 18) { /* bank switching */
  150.         bank=1;
  151.         op-=18;
  152.     }
  153.     writeOPL3(reg+reg_tbl[op], data, bank);
  154. }
  155.  
  156. /* For writing to channel settings (frequency, etc). */
  157. void writeCh(int ch, int reg, int data) {
  158.     int bank=0;
  159.     resetTest();
  160.     if (ch >= 9) { /* Bank switching. */
  161.         bank = 1;
  162.         ch -= 9;
  163.     }
  164.     writeOPL3(reg+ch, data, bank);
  165. }
  166.  
  167. /* Clear ADSR settings, volume, waveform. */
  168. void clearADSR(void) {
  169.     int ii;
  170.     for (ii=0; ii < 36; ii++) {
  171.         writeOp(ii, 0x20, 0); /* Tremolo, vibrato, sustain, etc. */
  172.         writeOp(ii, 0x40, 0); /* Volume. */
  173.         writeOp(ii, 0x60, 0x0F); /* Attack, decay. */
  174.         writeOp(ii, 0x80, 0xFF); /* Sustain, release. */
  175.         writeOp(ii, 0xE0, 0); /* Waveform. */
  176.     }
  177. }
  178.  
  179. /* Set things up to play audio. */
  180. void initOPL3(void) {
  181.     int ii;
  182.     writeOPL3(0x05,0b00000001,1); /* Enable opl3 mode. */
  183.     clearADSR();
  184.     /* Set key on for all channels, clear frequency, enable FM. */
  185.     for (ii=0; ii < 18; ii++) {
  186.         writeCh(ii, 0xB0, 0b00100000);
  187.         writeCh(ii, 0xC0, 0b00110000);
  188.     }
  189. }
  190.  
  191. /* Clean things up as best I know how so other programs get a "clean" state. */
  192. void endOPL3(void) {
  193.     int ii;
  194.     clearADSR();
  195.     /* set keyoff for all channels */
  196.     for (ii=0; ii < 18; ii++) {
  197.         writeCh(ii, 0xb0, 0);
  198.     }
  199.     writeOPL3(0x05,0x00,1); /* disable opl3 mode */
  200.     writeOPL3(0x01,0x00,0); /* disable waveform select*/
  201. }
  202.  
  203. /* Very basic test note player. */
  204. /* Here mostly for documentation purposes. */
  205. void playTestNote(int note, int channel) {
  206.     unsigned char low_note, high_note, key_on, key_off;
  207.     low_note=(unsigned char)note_tbl[note];
  208.     high_note=((unsigned char)(note_tbl[note]>>8)) & 0b00000011;
  209.     key_on = 0b00101000 | high_note;
  210.     /* Key off to start next note: */
  211.     writeCh(channel,0xB0,0);
  212.     /* Attack, decay: */
  213.     writeOp(channel*2  ,0x60,0xF1);
  214.     writeOp(channel*2+1,0x60,0xF1);
  215.     /* Sustain, release: */
  216.     writeOp(channel*2  ,0x80,0xF0);
  217.     writeOp(channel*2+1,0x80,0xF0);
  218.     /* Low bytes of note: */
  219.     writeCh(channel,0xA0,low_note);
  220.     /* Pitch volume divider, volume: */
  221.     writeOp(channel*2  ,0x40,0x00);
  222.     writeOp(channel*2+1,0x40,0x00);
  223.     /* High bytes of note, play it: */
  224.     writeCh(channel,0xB0,key_on);
  225. }
  226.  
  227. /* Test program -- made this while I was figuring things out. */
  228. int testProg(void) {
  229.     int key;
  230.     initOPL3();
  231.     clrscr();
  232.     cprintf("press 1-9...");
  233.     do {
  234.         key=getch();
  235.         if (key >= 48 && key <= 57) {
  236.             playTestNote(key-48,1);
  237.         }
  238.         if (key == ' ') {
  239.             /* do a keyoff to start a new note? */
  240.             writeCh(0,0xB0,0b00000000);
  241.             /* key on, octave, frequency */
  242.             writeCh(0,0xB0,0b00110110);
  243.             /* attack, decay */
  244.             writeOp(0,0x60,0xF3);
  245.             writeOp(1,0x60,0xF4);
  246.             /* sustain, release */
  247.             writeOp(0,0x80,0xF0);
  248.             writeOp(1,0x80,0xF0);
  249.             /* volume divider, volume */
  250.             writeOp(0,0x40,0x00);
  251.             writeOp(1,0x40,0x00);
  252.         }
  253.     } while (key != 'q');
  254.     endOPL3();
  255.     return 0;
  256. }
  257. /* Beginning work on keyboard input -- tests keycodes. */
  258. /* Should probably jettison this off into another program. */
  259. int keyCheck(void) {
  260.     int key, ext_key;
  261.     while(1) {
  262.         key=-1;
  263.         ext_key=-1;
  264.         if (kbhit()) {
  265.             key=getch();
  266.             if (key == 0) {
  267.                 ext_key=getch();
  268.             }
  269.         }
  270.         if (key=='q') { break; }
  271.         if (key!=-1) {
  272.             printf("key:%d\n", key);
  273.         }
  274.         if (ext_key!=-1) {
  275.             printf("extended key:%d\n", ext_key);
  276.         }
  277.     }
  278. }
  279.  
  280. /* Key structure for readKey. */
  281. /* Values are -1 if not there. */
  282. typedef struct {
  283.     int key;
  284.     int ext;
  285. } Key;
  286.  
  287. /* Extended key codes: */
  288. int key_up = 72;
  289. int key_down = 80;
  290. int key_left = 75;
  291. int key_right = 77;
  292. int key_shift_tab = 15;
  293. /* Normal key codes: */
  294. int key_esc = 27;
  295. int key_tab = 9; /* Shift-tab is extended key 15. */
  296. int key_ctrlQ = 17; /* Quit. */
  297. int key_ctrlS = 19; /* Save. */
  298. int key_ctrlO = 15; /* Load. */
  299. int key_F1=59;
  300. int key_F2=60;
  301. int key_F3=61;
  302. int key_F4=62;
  303. int key_F5=63;
  304. int key_F6=64;
  305.  
  306. /* Currently typed note relative to the current octave. */
  307. typedef struct {
  308.     int note;
  309.     int octave;
  310. } RelativeNote;
  311.  
  312. /* Converts typed keys to notes. */
  313. /* Makes me wish I had dictionaries in C. */
  314. RelativeNote keyToNote(int c) {
  315.     RelativeNote result;
  316.     switch (c) {
  317.         /* Bottom keyboard row: */
  318.         case 'z': result.note= 0; result.octave=0; break;
  319.         case 's': result.note= 1; result.octave=0; break;
  320.         case 'x': result.note= 2; result.octave=0; break;
  321.         case 'd': result.note= 3; result.octave=0; break;
  322.         case 'c': result.note= 4; result.octave=0; break;
  323.         case 'v': result.note= 5; result.octave=0; break;
  324.         case 'g': result.note= 6; result.octave=0; break;
  325.         case 'b': result.note= 7; result.octave=0; break;
  326.         case 'h': result.note= 8; result.octave=0; break;
  327.         case 'n': result.note= 9; result.octave=0; break;
  328.         case 'j': result.note=10; result.octave=0; break;
  329.         case 'm': result.note=11; result.octave=0; break;
  330.         /* Top keyboard row: */
  331.         case 'q': result.note= 0; result.octave=1; break;
  332.         case '2': result.note= 1; result.octave=1; break;
  333.         case 'w': result.note= 2; result.octave=1; break;
  334.         case '3': result.note= 3; result.octave=1; break;
  335.         case 'e': result.note= 4; result.octave=1; break;
  336.         case 'r': result.note= 5; result.octave=1; break;
  337.         case '5': result.note= 6; result.octave=1; break;
  338.         case 't': result.note= 7; result.octave=1; break;
  339.         case '6': result.note= 8; result.octave=1; break;
  340.         case 'y': result.note= 9; result.octave=1; break;
  341.         case '7': result.note=10; result.octave=1; break;
  342.         case 'u': result.note=11; result.octave=1; break;
  343.         /* Extra notes: */
  344.         case 'i': result.note= 0; result.octave=2; break;
  345.         case '9': result.note= 1; result.octave=2; break;
  346.         case 'o': result.note= 2; result.octave=2; break;
  347.         case '0': result.note= 3; result.octave=2; break;
  348.         case 'p': result.note= 4; result.octave=2; break;
  349.         /* Key isn't recognized: */
  350.         default: result.note=-1; result.octave=-1;
  351.     }
  352.     return result;
  353. }
  354.  
  355. /* Why isn't a function like this part of conio.h, anyway? */
  356. Key readKey(void) {
  357.     Key result;
  358.     result.key=-1;
  359.     result.ext=-1;
  360.     /* Check key: */
  361.     if (kbhit()) {
  362.         result.key=getch();
  363.         /* Check extended keycode: */
  364.         if (result.key == 0) {
  365.             result.ext=getch();
  366.         }
  367.     }
  368.     return result;
  369. }
  370.  
  371. /* All of the data for a single tick of a track: */
  372. typedef struct {
  373.     int note; /* 0-11, 12+ for no note. */
  374.     int octave; /* IIRC, it's 0-7. */
  375.     int instrument /* 255 for none. */;
  376.     int volume; /* 64+ for no volume. */
  377.     int effect; /* 0 for maybe arp. */
  378.     int effect_args; /* 00 is usually effect memory. */
  379.                      /* With a 0 effect, it does nothing. */
  380. } NoteData;
  381.  
  382. /* All 64 items in a track. Might add a per-channel volume option maybe. */
  383. typedef struct {
  384.     NoteData data[64];
  385. } Track;
  386.  
  387. /* Pretty much everything you see on the "tracks" screen: */
  388. typedef struct {
  389.     Track track[9];
  390. } Order;
  391.  
  392. /* Default values for an empty order. */
  393. /* Just sets all columns to out of bounds values. */
  394. void initOrder(Order *order) {
  395.     int ii, jj;
  396.     for (ii=0; ii<9; ii++) {
  397.         for (jj=0; jj<64; jj++) {
  398.             order->track[ii].data[jj].note=12;
  399.             order->track[ii].data[jj].octave=3;
  400.             order->track[ii].data[jj].instrument=255;
  401.             order->track[ii].data[jj].volume=64;
  402.             order->track[ii].data[jj].effect=0;
  403.             order->track[ii].data[jj].effect_args=0;
  404.         }
  405.     }
  406. }
  407.  
  408. /* This is pretty much the bulk of the song data: */
  409. typedef struct {
  410.     Order data[255];
  411.     int list[255];
  412.     char name[50];
  413. } OrderList;
  414.  
  415. /* Gonna use this to hold copy data if I bother implementing that: */
  416. typedef struct {
  417.     Track track[9];
  418.     /* Area of what to copy in the buffer: */
  419.     int height;
  420.     int width;
  421.     int x;
  422.     int y;
  423. } CopyBuffer;
  424.  
  425. /* Each FM operator's data is here. */
  426. /* 3kr doesn't bother using all of the parameters because I'm lazy. */
  427. typedef struct {
  428.     /* All except volume and wave are 0-15. */
  429.     int volume; /* 0-63, 63 is loudest. */
  430.     int attack;
  431.     int decay;
  432.     int wave; /* 0-7. */
  433.     int modulation;
  434.     int vibrato; /* On/off. */
  435. } Operator;
  436.  
  437. /* Half of a paired instrument: */
  438. typedef struct {
  439.     Operator op[2];
  440.     int finetune; /* -127 to 128 range. */
  441.     int fm; /* on/off switch. */
  442.     int feedback; /* 0-7 range. */
  443.     int panning; /* 0-3 range, mute, left, right, both. */
  444.     int delay; /* Not sure if I'm gonna implement this... */
  445. } ChanBank;
  446.  
  447. /* The full instrument as will be used in the tracker: */
  448. typedef struct {
  449.     char name[9]; /* 8 character instrument name. */
  450.     ChanBank bank[2];
  451. } Instrument;
  452.  
  453. /* Set some defaults for each bank. */
  454. /* I should make a second function for the second operator pair's defaults. */
  455. void initChanBank(ChanBank *bank) {
  456.     bank->op[0].volume=40;
  457.     bank->op[0].attack=15;
  458.     bank->op[0].decay=2;
  459.     bank->op[0].wave=0;
  460.     bank->op[0].modulation=0;
  461.     bank->op[0].vibrato=0;
  462.     bank->op[1].volume=63;
  463.     bank->op[1].attack=15;
  464.     bank->op[1].decay=1;
  465.     bank->op[1].wave=0;
  466.     bank->op[1].modulation=0;
  467.     bank->op[1].vibrato=0;
  468.     bank->feedback=0;
  469.     bank->finetune=0;
  470.     bank->fm=1;
  471.     bank->delay=0;
  472. }
  473.  
  474. /* At this moment, channel is just 0-17, might probably stay that way. */
  475. /* Hardware channels are paired side-by side in 3kr.
  476. /* (eg, 0+1, 2+3, 4+5, etc) */
  477. /* So, we get 9 4-op software channels with "fancy" features. */
  478. /* TODO: deal with delay if I ever bother implementing that. */
  479. /* Also, add a volume argument, and the rest of the operator stuff (wave, */
  480. /* feedback) */
  481. /* TODO: refactor a bit to be neater. */
  482. void playChanBank(ChanBank *bank, int channel, int note, int octave) {
  483.     unsigned char atk_dec, low_note, high_note, temp;
  484.     unsigned int final_note;
  485.     /* Key-off, force new note: */
  486.     writeCh(channel,0xB0,0x00);
  487.     /* Attack, decay: */
  488.     atk_dec = (bank->op[0].attack<<4) & 0xF0;
  489.     atk_dec |= (bank->op[0].decay) & 0x0F;
  490.     writeOp(channel*2  ,0x60,atk_dec);
  491.     atk_dec = (bank->op[1].attack<<4) & 0xF0;
  492.     atk_dec |= (bank->op[1].decay) & 0x0F;
  493.     writeOp(channel*2+1,0x60,atk_dec);
  494.     /* Ignoring sustain/release completely because I'm lazy: */
  495.     writeOp(channel*2  ,0x80,0xF0);
  496.     writeOp(channel*2+1,0x80,0xF0);
  497.     /* Feedback, panning, FM enable: */
  498.     temp = (bank->feedback & 0b00000111)<<1;
  499.     temp |= (bank->panning & 0b00000011)<<4;
  500.     temp |= (!bank->fm & 0b00000001);
  501.     writeCh(channel, 0xC0, temp);
  502.     /* Waveform: */
  503.     writeOp(channel*2  , 0xE0, (bank->op[0].wave) & 0b00000111);
  504.     writeOp(channel*2+1, 0xE0, (bank->op[0].wave) & 0b00000111);
  505.     /* TODO: do volume right, needs to be modified based on channel volume. */
  506.     writeOp(channel*2  ,0x40,63-bank->op[0].volume);
  507.     writeOp(channel*2+1,0x40,63-bank->op[1].volume);
  508.     /* Write note data: */
  509.     final_note = note_tbl[note]+bank->finetune;
  510.     low_note = final_note; /* Just the low 8 bits, should go in fine. */
  511.     octave = (octave & 0b00000111)<<2; /* Adjust octave for bit field. */
  512.     high_note = (final_note>>8) & 0b00000011; /* Top 2 bits of frequency. */
  513.     high_note |= 0b00100000 | octave; /* Key-on, octave. */
  514.     /* With these, the note gets played: */
  515.     writeCh(channel, 0xA0, low_note);
  516.     writeCh(channel, 0xB0, high_note);
  517. }
  518.  
  519. /* This is here just because testEditor is still here. */
  520. void drawTestEditor(ChanBank *test_bank) {
  521.     clrscr();
  522.     cprintf("modulator\r\n");
  523.     cprintf(" vol: %d\r\n", test_bank->op[0].volume);
  524.     cprintf(" atk: %d\r\n", test_bank->op[0].attack);
  525.     cprintf(" dec: %d\r\n", test_bank->op[0].decay);
  526.     cprintf("carrier\r\n");
  527.     cprintf(" vol: %d\r\n", test_bank->op[1].volume);
  528.     cprintf(" atk: %d\r\n", test_bank->op[1].attack);
  529.     cprintf(" dec: %d\r\n", test_bank->op[1].decay);
  530. }
  531.  
  532. /* Lazy beta instrument editor: */
  533. void testEditor(void) {
  534.     int op=0, pos=1;
  535.     /*
  536.     pos goes from
  537.         volume 1
  538.         attack 2
  539.         decay 3
  540.     */
  541.     ChanBank test_bank;
  542.     Key key;
  543.     initChanBank(&test_bank);
  544.     clrscr();
  545.     initOPL3();
  546.     drawTestEditor(&test_bank);
  547.     gotoxy(1,2);
  548.     cprintf(">");
  549.     while (1) {
  550.         key=readKey();
  551.         if (key.key != -1) {
  552.             if (key.key==key_esc) { /* exit */
  553.                 break;
  554.             }
  555.             if (key.key==' ') { /* play test note */
  556.                 playChanBank(&test_bank, 0, 0, 5);
  557.             }
  558.             if (key.key==key_tab) { /* switch operators */
  559.                 if (op==0) { op=1; } else { op=0; }
  560.             }
  561.             /* movement */
  562.             if (key.ext==key_up) {
  563.                 pos--;
  564.                 if (pos < 1) { pos=1; }
  565.             }
  566.             if (key.ext==key_down) {
  567.                 pos++;
  568.                 if (pos > 3) { pos=3; }
  569.             }
  570.             /* adjust settings */
  571.             if (key.ext==key_left) {
  572.                 if (pos == 1) {
  573.                     test_bank.op[op].volume--;
  574.                     if (test_bank.op[op].volume < 0)
  575.                         { test_bank.op[op].volume=0; }
  576.                 }
  577.                 if (pos == 2) {
  578.                     test_bank.op[op].attack--;
  579.                     if (test_bank.op[op].attack < 0)
  580.                         { test_bank.op[op].attack=0; }
  581.                 }
  582.                 if (pos == 3) {
  583.                     test_bank.op[op].decay--;
  584.                     if (test_bank.op[op].decay < 0)
  585.                         { test_bank.op[op].decay=0; }
  586.                 }
  587.             }
  588.             if (key.ext==key_right) {
  589.                 if (pos == 1) {
  590.                     test_bank.op[op].volume++;
  591.                     if (test_bank.op[op].volume > 63)
  592.                         { test_bank.op[op].volume=63; }
  593.                 }
  594.                 if (pos == 2) {
  595.                     test_bank.op[op].attack++;
  596.                     if (test_bank.op[op].attack > 15)
  597.                         { test_bank.op[op].attack=15; }
  598.                 }
  599.                 if (pos == 3) {
  600.                     test_bank.op[op].decay++;
  601.                     if (test_bank.op[op].decay > 15)
  602.                         { test_bank.op[op].decay=15; }
  603.                 }
  604.             }
  605.             /*update everything*/
  606.             drawTestEditor(&test_bank);
  607.             gotoxy(1,pos+op*4+1);
  608.             cprintf(">");
  609.         }
  610.     }
  611.     endOPL3();
  612. }
  613.  
  614. /* Copied right from <http://www.delorie.com/djgpp/doc/ug/graphics/vga.html> */
  615. /* -- but there really isn't any other way to do it. */
  616. void vsync(void) {
  617.     /* Wait until any previous retrace has ended: */
  618.     do {} while (inportb(0x3DA) & 8);
  619.     /* Wait until a new retrace has just begun: */
  620.     do {} while (!(inportb(0x3DA) & 8));
  621. }
  622.  
  623. /* Basic timing test function: */
  624. void timerTest(void) {
  625.     int num=0;
  626.     while (readKey().key != 'q') {
  627.         vsync();
  628.         num++;
  629.         gotoxy(1,1);
  630.         cprintf("%d\r\n", num);
  631.         cprintf("%2d, %d\r\n", num % 70, num / 70);
  632.     }
  633. }
  634.  
  635. /* !!! */
  636. /* Globals here, and all tracker UI code goes below! */
  637. int current_order=0;
  638. int current_row=0;
  639. int current_column=0;
  640. int current_section=0;
  641. int current_instrument=0;
  642. int current_octave=3;
  643. OrderList current_song;
  644. Display display;
  645.  
  646. /* Show the header -- the [xxxx] status and the F1/F2/F3 key indicators will be */
  647. /* overwritten by other routines. */
  648. /* TODO: give argument to put { } around key indicators, might as well do that */
  649. /* here. */
  650. void showHeader(void) {
  651.     display.x=0; display.y=0;
  652.     dputLine(&display, "[3kr] 9 channel OPL3 tracker | 2016 ");
  653.     dputLine(&display, "null1024----------------song status:[xxxx]\nkeys: '?' ");
  654.     dputLine(&display, "help, 'F1' tracks, 'F2' instruments, 'F3' order list\n");
  655. }
  656.  
  657. /* Each of the channel data areas gets drawn here. */
  658. /* active is 1-8: the whole note, 2 instrument nybbles, 2 volume nybbles, */
  659. /* and 3 effect nybbles. */
  660.  
  661. void drawBlock(int active, NoteData *data) {
  662.     /* Left border: */
  663.     char linebuffer[80];
  664.     display.fg=WHITE;
  665.     dputLine(&display, "|");
  666.     if (active) { display.bg=CYAN; } /* Current position highlight. */
  667.    
  668.     /* Note: */
  669.     if (active == 1) { display.fg=LIGHTRED; }
  670.     else { display.fg=YELLOW; }
  671.     if (data->note >= 12 || data->note < 0) {
  672.         dputLine(&display,"---");
  673.     } else {
  674.         snprintf(linebuffer, 80, "%s%d", note_text_tbl[data->note], data->octave);
  675.         dputLine(&display, linebuffer);
  676.     }
  677.    
  678.     /* Instrument: */
  679.     if (active == 2) { display.fg=LIGHTRED; }
  680.     else { display.fg=WHITE; }
  681.     dputLine(&display,".");
  682.     if (active == 3) { display.fg=LIGHTRED; }
  683.     else { display.fg=WHITE; }
  684.     dputLine(&display,".");
  685.    
  686.     /* Volume: */
  687.     if (active == 4) { display.fg=LIGHTRED; }
  688.     else { display.fg=YELLOW; }
  689.     dputLine(&display,".");
  690.     if (active == 5) { display.fg=LIGHTRED; }
  691.     else { display.fg=YELLOW; }
  692.     dputLine(&display,".");
  693.    
  694.     /* Effect: */
  695.     if (active == 6) { display.fg=LIGHTRED; }
  696.     else { display.fg=WHITE; }
  697.     dputLine(&display,":");
  698.     if (active == 7) { display.fg=LIGHTRED; }
  699.     else { display.fg=WHITE; }
  700.     dputLine(&display,":");
  701.     if (active == 8) { display.fg=LIGHTRED; }
  702.     else { display.fg=WHITE; }
  703.     dputLine(&display,":");
  704.    
  705.     /* Reset color settings: */
  706.     display.bg=DARKGRAY;
  707.     display.fg=WHITE;
  708. }
  709.  
  710. /* Draw everything. Re-written to use my screen-buffer copying functions. */
  711. void drawTrackerScreen(int x, int y, int section, Order *data) {
  712.     int ii, jj;
  713.     char linebuffer[80];
  714.     static int top = 0, left = 0;
  715.     /* Display scrolling bounds. Need to do channel scrolling next. */
  716.     while (y >= top+18) {
  717.         top++;
  718.     }
  719.    
  720.     while (y < top) {
  721.         top--;
  722.     }
  723.    
  724.     /* You can fit 18 rows on screen at 79x24 as is being used. */
  725.     display.x=0;
  726.     display.y=3;
  727.     for (ii=top; ii < top+18 && ii < 64; ii++) {
  728.         snprintf(linebuffer, 80, "%02X ", ii);
  729.         dputLine(&display, linebuffer);
  730.         /* Here you have 6 columns visible at once: */
  731.         for (jj=0; jj < 6; jj++) {
  732.             /* Marks which cursor position we're in. */
  733.             /* 0 if we're not in that spot, 1-8 if we are. */
  734.             drawBlock((ii == y && jj == x) * (section+1),
  735.                       &(data->track[jj].data[ii]));
  736.         }
  737.         dputLine(&display,"|     | |\n");
  738.     }
  739.     display.y=2;
  740.     /* put channel numbers */
  741.     for (ii=1; ii<=6; ii++) {
  742.         display.x=11*ii;
  743.         snprintf(linebuffer, 80, "%d", ii);
  744.         dputLine(&display, linebuffer);
  745.     }
  746. }
  747.  
  748. /* Alpha of actual tracker main editor screen. */
  749. /* Gonna need some major work. */
  750. void trackerScreen(void){
  751.     int ii, jj;
  752.     int x=0, y=0, section=0;
  753.     Key key;
  754.     Order order; /* TODO: remove. Should be done based on the global order */
  755.                  /* list and global current order. */
  756.     ChanBank test_bank; /* TODO: replace with full instrument */
  757.     RelativeNote note;
  758.     initOrder(&order);
  759.     initChanBank(&test_bank);
  760.     showHeader();
  761.     dputLine(&display,"   +--trackX--+--trackX--+--trackX--+--trackX--+");
  762.     dputLine(&display,"--trackX--+--trackX--+-----+-+\n");
  763.     drawTrackerScreen(x,y,section, &order);
  764.     ScreenUpdate(display.data);
  765.     while (1) {
  766.         key=readKey();
  767.         if (key.key != -1) {
  768.             /* Movement: */
  769.             if (key.key == key_tab) {
  770.                 x++;
  771.             }
  772.             if (key.ext == key_shift_tab) {
  773.                 x--;
  774.             }
  775.             if (key.ext == key_down) {
  776.                 y++;
  777.             }
  778.             if (key.ext == key_up) {
  779.                 y--;
  780.             }
  781.             if (key.ext == key_left) {
  782.                 section--;
  783.             }
  784.             if (key.ext == key_right) {
  785.                 section++;
  786.             }
  787.             /* Bounds checks: */
  788.             if (section < 0) {
  789.                 if (x > 0) {
  790.                     section = 7; x--;
  791.                 } else {
  792.                     section=0;
  793.                 }
  794.             }
  795.             /* Moving between channels: */
  796.             if (section > 7) {
  797.                 section = 0; x++;
  798.             }
  799.             /* Note entry: */
  800.             if (section == 0) {
  801.                 note = keyToNote(key.key);
  802.                 if (note.note != -1) {
  803.                     order.track[x].data[y].note=note.note;
  804.                     order.track[x].data[y].octave=note.octave;
  805.                     playChanBank(&test_bank, 0, note.note, note.octave+2);
  806.                     y++;
  807.                 }
  808.             }
  809.             /* Final bounds check: */
  810.             if (x < 0) { x=0; }
  811.             if (y < 0) { y=0; }
  812.             if (x > 8) { x=8; }
  813.             if (y > 63) { y=63; }
  814.             drawTrackerScreen(x,y,section, &order);
  815.             ScreenUpdate(display.data);
  816.             /* exit */
  817.             if (key.key == key_ctrlQ) {
  818.                 break;
  819.             }
  820.         }
  821.     }
  822. }
  823.  
  824. /* Cleanup display on exit: */
  825. void resetScreen(void) {
  826.     normvideo();
  827.     textcolor(LIGHTGRAY);
  828.     textbackground(BLACK);
  829.     _setcursortype(_NORMALCURSOR);
  830.     gotoxy(1,1);
  831.     cprintf("[settings reset]\r\n");
  832.     clrscr();
  833. }
  834.  
  835. /* Set up background color, disable cursor, etc: */
  836. void initScreen(void) {
  837.     clrscr();
  838.     intensevideo();
  839.     dclearScr(&display, WHITE, DARKGRAY);
  840.     _setcursortype(_NOCURSOR);
  841. }
  842.  
  843. /* Program starts here, but pretty much everything is done elsewhere. */
  844. int main(void) {
  845.     initScreen();
  846.     initOPL3();
  847.     trackerScreen();
  848.     /* keyCheck(); */
  849.     resetScreen();
  850.     endOPL3();
  851. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement