instrument_editor.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. #include "instrument_editor.h"
  2. #include "pattern_editor.h"
  3. #include "../macros.h"
  4. #include "opcode_description.h"
  5. #include <flizzer_tracker_icons.h>
  6. void draw_inst_flag(
  7. FlizzerTrackerApp* tracker,
  8. Canvas* canvas,
  9. uint8_t focus,
  10. uint8_t param,
  11. const char* text,
  12. uint8_t x,
  13. uint8_t y,
  14. uint16_t flags,
  15. uint16_t mask) {
  16. canvas_draw_icon(canvas, x, y - 5, ((flags & mask) ? &I_checkbox_checked : &I_checkbox_empty));
  17. canvas_draw_str(canvas, x + 6, y, text);
  18. if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) {
  19. if(text[strlen(text) - 1] == ':') {
  20. canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7);
  21. }
  22. else {
  23. canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7);
  24. }
  25. }
  26. if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) {
  27. if(text[strlen(text) - 1] == ':') {
  28. canvas_draw_frame(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7);
  29. }
  30. else {
  31. canvas_draw_frame(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7);
  32. }
  33. }
  34. }
  35. void draw_inst_text_one_digit(
  36. FlizzerTrackerApp* tracker,
  37. Canvas* canvas,
  38. uint8_t focus,
  39. uint8_t param,
  40. const char* text,
  41. uint8_t x,
  42. uint8_t y,
  43. uint8_t value) // text MUST end with semicolon
  44. {
  45. canvas_draw_str(canvas, x, y, text);
  46. char buffer[4];
  47. snprintf(buffer, sizeof(buffer), "%01X", (value & 0xF));
  48. canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer);
  49. if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) {
  50. canvas_draw_box(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7);
  51. }
  52. if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) {
  53. canvas_draw_frame(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7);
  54. }
  55. }
  56. void draw_inst_text_two_digits(
  57. FlizzerTrackerApp* tracker,
  58. Canvas* canvas,
  59. uint8_t focus,
  60. uint8_t param,
  61. const char* text,
  62. uint8_t x,
  63. uint8_t y,
  64. uint8_t value) // text MUST end with semicolon
  65. {
  66. canvas_draw_str(canvas, x, y, text);
  67. char buffer[4];
  68. snprintf(buffer, sizeof(buffer), "%02X", value);
  69. canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer);
  70. if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) {
  71. canvas_draw_box(
  72. canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7);
  73. }
  74. if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) {
  75. canvas_draw_frame(
  76. canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7);
  77. }
  78. }
  79. static const char* filter_types[] = {
  80. "NONE",
  81. "LOW",
  82. "HIGH",
  83. "BAND",
  84. "LOHI",
  85. "HIBD",
  86. "LOBD",
  87. "ALL",
  88. };
  89. static const char* instrument_editor_params_description[] = {
  90. "CURRENT INSTRUMENT",
  91. "CURRENT INSTRUMENT NAME",
  92. "INSTRUMENT BASE NOTE",
  93. "INSTRUMENT FINETUNE",
  94. "SLIDE SPEED",
  95. "SET PULSE WIDTH ON KEYDOWN",
  96. "PULSE WIDTH",
  97. "SET FILTER PARAMETERS ON KEYDOWN",
  98. "NOISE WAVEFORM",
  99. "PULSE WAVEFORM",
  100. "TRIANGLE WAVEFORM",
  101. "SAWTOOTH WAVEFORM",
  102. "METALLIC NOISE WAVEFORM",
  103. "SINE WAVEFORM",
  104. "ENVELOPE ATTACK",
  105. "ENVELOPE DECAY",
  106. "ENVELOPE SUSTAIN",
  107. "ENVELOPE RELEASE",
  108. "ENVELOPE VOLUME",
  109. "ENABLE FILTER",
  110. "FILTER CUTOFF FREQUENCY",
  111. "FILTER RESONANCE",
  112. "FILTER TYPE (NONE=OFF)",
  113. "ENABLE RING MODULATION",
  114. "RINGMOD SOURCE CHANNEL (F=SELF)",
  115. "ENABLE HARD SYNC",
  116. "HARDSYNC SOURCE CHANNEL (F=SELF)",
  117. "RETRIGGER INSTRUMENT ON SLIDE",
  118. "SYNC OSCILLATORS ON KEYDOWN",
  119. "ENABLE VIBRATO",
  120. "VIBRATO SPEED",
  121. "VIBRATO DEPTH",
  122. "VIBRATO DELAY (IN TICKS)",
  123. "ENABLE PWM",
  124. "PWM SPEED",
  125. "PWM DEPTH",
  126. "PWM DELAY (IN TICKS)",
  127. "DON'T RESTART PROGRAM ON KEYDOWN",
  128. "PROG.PERIOD (00 = PROGRAM OFF)",
  129. };
  130. void draw_instrument_view(Canvas* canvas, FlizzerTrackerApp* tracker) {
  131. SoundEngineChannel* se_channel = &tracker->sound_engine.channel[0];
  132. if(!(se_channel->flags & SE_ENABLE_GATE) && tracker->tracker_engine.song == NULL) {
  133. stop();
  134. tracker->tracker_engine.playing = false;
  135. tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
  136. }
  137. char buffer[30];
  138. Instrument* inst = tracker->song.instrument[tracker->current_instrument];
  139. uint8_t shift = tracker->inst_editor_shift;
  140. if(shift < 6) {
  141. snprintf(buffer, sizeof(buffer), "INST:%c", to_char(tracker->current_instrument));
  142. draw_generic_n_digit_field(
  143. tracker, canvas, EDIT_INSTRUMENT, INST_CURRENTINSTRUMENT, buffer, 0, 5 - shift, 1);
  144. snprintf(
  145. buffer,
  146. sizeof(buffer),
  147. "%s",
  148. tracker->song.instrument[tracker->current_instrument]->name);
  149. draw_generic_n_digit_field(
  150. tracker, canvas, EDIT_INSTRUMENT, INST_INSTRUMENTNAME, buffer, 4 * 7 - 1, 5 - shift, 1);
  151. }
  152. if(shift < 12) {
  153. snprintf(buffer, sizeof(buffer), "NOTE:%s", notename(inst->base_note));
  154. canvas_draw_str(canvas, 0, 11 - shift, buffer);
  155. if(tracker->editing && tracker->focus == EDIT_INSTRUMENT &&
  156. tracker->selected_param == INST_CURRENT_NOTE) {
  157. if(tracker->current_digit) {
  158. canvas_draw_box(canvas, 19 + 2 * 4, 5 - shift, 5, 7);
  159. }
  160. else {
  161. canvas_draw_box(canvas, 19, 5 - shift, 5 + 4, 7);
  162. }
  163. }
  164. if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT &&
  165. tracker->selected_param == INST_CURRENT_NOTE) {
  166. if(tracker->current_digit) {
  167. canvas_draw_frame(canvas, 19 + 2 * 4, 5 - shift, 5, 7);
  168. }
  169. else {
  170. canvas_draw_frame(canvas, 19, 5 - shift, 5 + 4, 7);
  171. }
  172. }
  173. snprintf(buffer, sizeof(buffer), "FINE:%+02d", inst->finetune);
  174. canvas_draw_str(canvas, 37, 11 - shift, buffer);
  175. if(tracker->editing && tracker->focus == EDIT_INSTRUMENT &&
  176. tracker->selected_param == INST_FINETUNE) {
  177. if(tracker->current_digit) {
  178. canvas_draw_box(canvas, 60 + 4, 5 - shift, 5, 7);
  179. }
  180. else {
  181. canvas_draw_box(canvas, 60, 5 - shift, 5, 7);
  182. }
  183. }
  184. if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT &&
  185. tracker->selected_param == INST_FINETUNE) {
  186. if(tracker->current_digit) {
  187. canvas_draw_frame(canvas, 60 + 4, 5 - shift, 5, 7);
  188. }
  189. else {
  190. canvas_draw_frame(canvas, 60, 5 - shift, 5, 7);
  191. }
  192. }
  193. }
  194. draw_inst_text_two_digits(
  195. tracker,
  196. canvas,
  197. EDIT_INSTRUMENT,
  198. INST_SLIDESPEED,
  199. "SL.SPD:",
  200. 0,
  201. 17 - shift,
  202. inst->slide_speed);
  203. draw_inst_flag(
  204. tracker, canvas, EDIT_INSTRUMENT, INST_SETPW, "PW:", 36, 17 - shift, inst->flags, TE_SET_PW);
  205. draw_inst_text_two_digits(
  206. tracker, canvas, EDIT_INSTRUMENT, INST_PW, "", 54, 17 - shift, inst->pw);
  207. draw_inst_flag(
  208. tracker,
  209. canvas,
  210. EDIT_INSTRUMENT,
  211. INST_SETCUTOFF,
  212. "CUT",
  213. 61,
  214. 17 - shift,
  215. inst->flags,
  216. TE_SET_CUTOFF);
  217. draw_inst_flag(
  218. tracker,
  219. canvas,
  220. EDIT_INSTRUMENT,
  221. INST_WAVE_NOISE,
  222. "N",
  223. 0,
  224. 23 - shift,
  225. inst->waveform,
  226. SE_WAVEFORM_NOISE);
  227. draw_inst_flag(
  228. tracker,
  229. canvas,
  230. EDIT_INSTRUMENT,
  231. INST_WAVE_PULSE,
  232. "P",
  233. 10,
  234. 23 - shift,
  235. inst->waveform,
  236. SE_WAVEFORM_PULSE);
  237. draw_inst_flag(
  238. tracker,
  239. canvas,
  240. EDIT_INSTRUMENT,
  241. INST_WAVE_TRIANGLE,
  242. "T",
  243. 20,
  244. 23 - shift,
  245. inst->waveform,
  246. SE_WAVEFORM_TRIANGLE);
  247. draw_inst_flag(
  248. tracker,
  249. canvas,
  250. EDIT_INSTRUMENT,
  251. INST_WAVE_SAWTOOTH,
  252. "S",
  253. 30,
  254. 23 - shift,
  255. inst->waveform,
  256. SE_WAVEFORM_SAW);
  257. draw_inst_flag(
  258. tracker,
  259. canvas,
  260. EDIT_INSTRUMENT,
  261. INST_WAVE_NOISE_METAL,
  262. "M",
  263. 40,
  264. 23 - shift,
  265. inst->waveform,
  266. SE_WAVEFORM_NOISE_METAL);
  267. draw_inst_flag(
  268. tracker,
  269. canvas,
  270. EDIT_INSTRUMENT,
  271. INST_WAVE_SINE,
  272. "SINE",
  273. 50,
  274. 23 - shift,
  275. inst->waveform,
  276. SE_WAVEFORM_SINE);
  277. draw_inst_text_two_digits(
  278. tracker, canvas, EDIT_INSTRUMENT, INST_ATTACK, "A:", 0, 29 - shift, inst->adsr.a);
  279. draw_inst_text_two_digits(
  280. tracker, canvas, EDIT_INSTRUMENT, INST_DECAY, "D:", 16, 29 - shift, inst->adsr.d);
  281. draw_inst_text_two_digits(
  282. tracker, canvas, EDIT_INSTRUMENT, INST_SUSTAIN, "S:", 32, 29 - shift, inst->adsr.s);
  283. draw_inst_text_two_digits(
  284. tracker, canvas, EDIT_INSTRUMENT, INST_RELEASE, "R:", 48, 29 - shift, inst->adsr.r);
  285. draw_inst_text_two_digits(
  286. tracker, canvas, EDIT_INSTRUMENT, INST_VOLUME, "V:", 64, 29 - shift, inst->adsr.volume);
  287. draw_inst_flag(
  288. tracker,
  289. canvas,
  290. EDIT_INSTRUMENT,
  291. INST_ENABLEFILTER,
  292. "FIL",
  293. 0,
  294. 35 - shift,
  295. inst->sound_engine_flags,
  296. SE_ENABLE_FILTER);
  297. draw_inst_text_two_digits(
  298. tracker,
  299. canvas,
  300. EDIT_INSTRUMENT,
  301. INST_FILTERCUTOFF,
  302. "CUT:",
  303. 20,
  304. 35 - shift,
  305. inst->filter_cutoff);
  306. draw_inst_text_two_digits(
  307. tracker,
  308. canvas,
  309. EDIT_INSTRUMENT,
  310. INST_FILTERRESONANCE,
  311. "RES:",
  312. 44,
  313. 35 - shift,
  314. inst->filter_resonance);
  315. snprintf(buffer, sizeof(buffer), "TYPE:%s", filter_types[inst->filter_type]);
  316. canvas_draw_str(canvas, 0, 41 - shift, buffer);
  317. if(tracker->editing && tracker->focus == EDIT_INSTRUMENT &&
  318. tracker->selected_param == INST_FILTERTYPE) {
  319. canvas_draw_box(
  320. canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7);
  321. }
  322. if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT &&
  323. tracker->selected_param == INST_FILTERTYPE) {
  324. canvas_draw_frame(
  325. canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7);
  326. }
  327. draw_inst_flag(
  328. tracker,
  329. canvas,
  330. EDIT_INSTRUMENT,
  331. INST_ENABLERINGMOD,
  332. "R:",
  333. 38,
  334. 41 - shift,
  335. inst->sound_engine_flags,
  336. SE_ENABLE_RING_MOD);
  337. draw_inst_text_one_digit(
  338. tracker, canvas, EDIT_INSTRUMENT, INST_RINGMODSRC, "", 52, 41 - shift, inst->ring_mod);
  339. draw_inst_flag(
  340. tracker,
  341. canvas,
  342. EDIT_INSTRUMENT,
  343. INST_ENABLEHARDSYNC,
  344. "H:",
  345. 56,
  346. 41 - shift,
  347. inst->sound_engine_flags,
  348. SE_ENABLE_HARD_SYNC);
  349. draw_inst_text_one_digit(
  350. tracker, canvas, EDIT_INSTRUMENT, INST_HARDSYNCSRC, "", 70, 41 - shift, inst->hard_sync);
  351. draw_inst_flag(
  352. tracker,
  353. canvas,
  354. EDIT_INSTRUMENT,
  355. INST_RETRIGGERONSLIDE,
  356. "SL.RETRIG",
  357. 0,
  358. 47 - shift,
  359. inst->flags,
  360. TE_RETRIGGER_ON_SLIDE);
  361. draw_inst_flag(
  362. tracker,
  363. canvas,
  364. EDIT_INSTRUMENT,
  365. INST_ENABLEKEYSYNC,
  366. "KSYNC",
  367. 44,
  368. 47 - shift,
  369. inst->sound_engine_flags,
  370. SE_ENABLE_KEYDOWN_SYNC);
  371. draw_inst_flag(
  372. tracker,
  373. canvas,
  374. EDIT_INSTRUMENT,
  375. INST_ENABLEVIBRATO,
  376. "VIB",
  377. 0,
  378. 53 - shift,
  379. inst->flags,
  380. TE_ENABLE_VIBRATO);
  381. draw_inst_text_two_digits(
  382. tracker,
  383. canvas,
  384. EDIT_INSTRUMENT,
  385. INST_VIBRATOSPEED,
  386. "S:",
  387. 20,
  388. 53 - shift,
  389. inst->vibrato_speed);
  390. draw_inst_text_two_digits(
  391. tracker,
  392. canvas,
  393. EDIT_INSTRUMENT,
  394. INST_VIBRATODEPTH,
  395. "D:",
  396. 36,
  397. 53 - shift,
  398. inst->vibrato_depth);
  399. draw_inst_text_two_digits(
  400. tracker,
  401. canvas,
  402. EDIT_INSTRUMENT,
  403. INST_VIBRATODELAY,
  404. "DEL:",
  405. 52,
  406. 53 - shift,
  407. inst->vibrato_delay);
  408. if(shift >= 6) {
  409. draw_inst_flag(
  410. tracker,
  411. canvas,
  412. EDIT_INSTRUMENT,
  413. INST_ENABLEPWM,
  414. "PWM",
  415. 0,
  416. 59 - shift,
  417. inst->flags,
  418. TE_ENABLE_PWM);
  419. draw_inst_text_two_digits(
  420. tracker, canvas, EDIT_INSTRUMENT, INST_PWMSPEED, "S:", 20, 59 - shift, inst->pwm_speed);
  421. draw_inst_text_two_digits(
  422. tracker, canvas, EDIT_INSTRUMENT, INST_PWMDEPTH, "D:", 36, 59 - shift, inst->pwm_depth);
  423. draw_inst_text_two_digits(
  424. tracker,
  425. canvas,
  426. EDIT_INSTRUMENT,
  427. INST_PWMDELAY,
  428. "DEL:",
  429. 52,
  430. 59 - shift,
  431. inst->pwm_delay);
  432. }
  433. if(shift >= 12) {
  434. draw_inst_flag(
  435. tracker,
  436. canvas,
  437. EDIT_INSTRUMENT,
  438. INST_PROGRESTART,
  439. "NO PROG.RESTART",
  440. 0,
  441. 65 - shift,
  442. inst->flags,
  443. TE_PROG_NO_RESTART);
  444. }
  445. draw_inst_text_two_digits(
  446. tracker,
  447. canvas,
  448. EDIT_INSTRUMENT,
  449. INST_PROGRAMEPERIOD,
  450. "P.PERIOD:",
  451. 81,
  452. 56,
  453. inst->program_period);
  454. canvas_draw_line(canvas, 0, 57, 127, 57);
  455. canvas_draw_line(canvas, 79, 0, 79, 56);
  456. canvas_draw_line(canvas, 80, 49, 127, 49);
  457. if(tracker->focus == EDIT_INSTRUMENT) {
  458. canvas_draw_str(
  459. canvas, 0, 64, instrument_editor_params_description[tracker->selected_param]);
  460. }
  461. }
  462. char command_get_char(uint16_t command) {
  463. if((command >> 8) < 36) {
  464. return to_char_array[(command >> 8)];
  465. }
  466. if(command == TE_PROGRAM_END) {
  467. return ':';
  468. }
  469. if((command & 0xff00) == TE_PROGRAM_JUMP) {
  470. return '^';
  471. }
  472. if((command & 0xff00) == TE_PROGRAM_LOOP_END) {
  473. return '>';
  474. }
  475. if((command & 0xff00) == TE_PROGRAM_LOOP_BEGIN) {
  476. return '<';
  477. }
  478. return '?';
  479. }
  480. void draw_program_step(Canvas* canvas, uint8_t y, FlizzerTrackerApp* tracker, uint8_t index) {
  481. char buffer[15];
  482. Instrument* inst = tracker->song.instrument[tracker->current_instrument];
  483. uint16_t opcode = inst->program[index];
  484. if(opcode != TE_PROGRAM_NOP) {
  485. if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO) {
  486. if((opcode & 0xff) != 0xf0 && (opcode & 0xff) != 0xf1) {
  487. snprintf(
  488. buffer,
  489. sizeof(buffer),
  490. "%01X %c%02X %s",
  491. index,
  492. command_get_char(opcode & 0x7fff),
  493. (opcode & 0xff),
  494. notename(my_min(
  495. 12 * 7 + 11,
  496. (opcode & 0xff) +
  497. tracker->song.instrument[tracker->current_instrument]->base_note)));
  498. }
  499. else {
  500. snprintf(
  501. buffer,
  502. sizeof(buffer),
  503. "%01X %c%02X %s",
  504. index,
  505. command_get_char(opcode & 0x7fff),
  506. (opcode & 0xff),
  507. notename((opcode & 0xff)));
  508. }
  509. }
  510. else if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO_ABS) {
  511. snprintf(
  512. buffer,
  513. sizeof(buffer),
  514. "%01X %c%02X F.%s",
  515. index,
  516. command_get_char(opcode & 0x7fff),
  517. (opcode & 0xff),
  518. notename(opcode & 0xff));
  519. }
  520. else {
  521. snprintf(
  522. buffer,
  523. sizeof(buffer),
  524. "%01X %c%02X %s",
  525. index,
  526. command_get_char(opcode & 0x7fff),
  527. (opcode & 0xff),
  528. get_opcode_description(opcode, true) ? get_opcode_description(opcode, true) : "");
  529. }
  530. if(opcode & 0x8000) {
  531. if(index == 0) {
  532. canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 3);
  533. canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 4);
  534. }
  535. if(index > 0 && !(inst->program[index - 1] & 0x8000)) {
  536. canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 3);
  537. canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 4);
  538. }
  539. if(index > 0 && (inst->program[index - 1] & 0x8000)) {
  540. canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 5);
  541. }
  542. }
  543. else {
  544. if(index > 0 && (inst->program[index - 1] & 0x8000)) {
  545. canvas_draw_line(canvas, 84 + 4 * 4 + 2, y - 3, 84 + 4 * 4 + 2, y - 5);
  546. canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 2);
  547. }
  548. }
  549. }
  550. else {
  551. snprintf(buffer, sizeof(buffer), "%01X ---", index);
  552. }
  553. canvas_draw_str(canvas, 81, y, buffer);
  554. }
  555. void draw_instrument_program_view(Canvas* canvas, FlizzerTrackerApp* tracker) {
  556. Instrument* inst = tracker->song.instrument[tracker->current_instrument];
  557. for(uint8_t i = tracker->program_position;
  558. i < my_min(INST_PROG_LEN, tracker->program_position + 8);
  559. i++) {
  560. draw_program_step(canvas, 6 + 6 * i - tracker->program_position * 6, tracker, i);
  561. if(i == tracker->current_program_step && tracker->focus == EDIT_PROGRAM) {
  562. if(tracker->editing) {
  563. canvas_draw_box(
  564. canvas,
  565. 80 + 8 + tracker->current_digit * 4,
  566. 6 * i - tracker->program_position * 6,
  567. 5,
  568. 7);
  569. }
  570. else {
  571. canvas_draw_box(canvas, 80, 6 * i - tracker->program_position * 6, 5, 7);
  572. }
  573. }
  574. }
  575. // draw arrow pointing at current program step
  576. for(uint8_t i = 0; i < SONG_MAX_CHANNELS; i++) {
  577. if(tracker->tracker_engine.channel[i].instrument == inst &&
  578. (tracker->tracker_engine.channel[i].channel_flags & TEC_PROGRAM_RUNNING) &&
  579. (tracker->tracker_engine.sound_engine->channel[i].flags & SE_ENABLE_GATE)) {
  580. if(tracker->tracker_engine.channel[i].program_tick >= tracker->program_position &&
  581. tracker->tracker_engine.channel[i].program_tick < tracker->program_position + 8) {
  582. canvas_draw_str(
  583. canvas,
  584. 85,
  585. 6 * tracker->tracker_engine.channel[i].program_tick -
  586. tracker->program_position * 6 + 6,
  587. ">");
  588. break;
  589. }
  590. }
  591. }
  592. if(tracker->focus == EDIT_PROGRAM) {
  593. uint16_t opcode = (inst->program[tracker->current_program_step] & 0x7fff);
  594. canvas_draw_str(
  595. canvas,
  596. 0,
  597. 64,
  598. get_opcode_description(opcode, false) ? get_opcode_description(opcode, false) : "");
  599. }
  600. }