comparison tools/ppl.c @ 652:d9888292f971

Rename again.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 16 Apr 2013 06:04:22 +0300
parents utils/ppl.c@e2ac08228a0f
children 3d813c81f33c
comparison
equal deleted inserted replaced
651:e2ac08228a0f 652:d9888292f971
1 /*
2 * Cyrbe Pasci Player - A simple SDL-based UI for XM module playing
3 * Programmed and designed by Matti 'ccr' Hamalainen
4 * (C) Copyright 2012 Tecnic Software productions (TNSP)
5 *
6 * Please read file 'COPYING' for information on license and distribution.
7 */
8 #include <SDL.h>
9 #include "dmlib.h"
10
11 #include "jss.h"
12 #include "jssmod.h"
13 #include "jssmix.h"
14 #include "jssplr.h"
15
16 #include "dmargs.h"
17 #include "dmimage.h"
18 #include "dmtext.h"
19
20 #include "setupfont.h"
21
22
23 struct
24 {
25 BOOL exitFlag;
26 SDL_Surface *screen;
27 SDL_Event event;
28 int optScrWidth, optScrHeight, optVFlags, optScrDepth;
29
30 int actChannel;
31 BOOL pauseFlag;
32
33 JSSModule *mod;
34 JSSMixer *dev;
35 JSSPlayer *plr;
36 SDL_AudioSpec afmt;
37 } engine;
38
39 struct
40 {
41 Uint32 boxBg, inboxBg, box1, box2, viewDiv, activeRow, activeChannel;
42 } col;
43
44
45 DMBitmapFont *font = NULL;
46
47 char *optFilename = NULL;
48 int optOutFormat = JSS_AUDIO_S16,
49 optOutChannels = 2,
50 optOutFreq = 48000,
51 optMuteOChannels = -1,
52 optStartOrder = 0;
53 BOOL optUsePlayTime = FALSE;
54 size_t optPlayTime;
55
56
57 DMOptArg optList[] =
58 {
59 { 0, '?', "help", "Show this help", OPT_NONE },
60 { 1, 'v', "verbose", "Be more verbose", OPT_NONE },
61 { 2, 0, "fs", "Fullscreen", OPT_NONE },
62 { 3, 'w', "window", "Initial window size/resolution -w 640x480", OPT_ARGREQ },
63
64 { 4, '1', "16bit", "16-bit output", OPT_NONE },
65 { 5, '8', "8bit", "8-bit output", OPT_NONE },
66 { 6, 'm', "mono", "Mono output", OPT_NONE },
67 { 7, 's', "stereo", "Stereo output", OPT_NONE },
68 { 8, 'f', "freq", "Output frequency", OPT_ARGREQ },
69
70 { 9, 'M', "mute", "Mute other channels than #", OPT_ARGREQ },
71 { 10, 'o', "order", "Start from order #", OPT_ARGREQ },
72 { 11, 't', "time", "Play for # seconds", OPT_ARGREQ },
73 };
74
75 const int optListN = sizeof(optList) / sizeof(optList[0]);
76
77
78 void argShowHelp()
79 {
80 dmPrintBanner(stdout, dmProgName, "[options] <module>");
81 dmArgsPrintHelp(stdout, optList, optListN);
82 }
83
84
85 BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
86 {
87 switch (optN) {
88 case 0:
89 argShowHelp();
90 exit(0);
91 break;
92
93 case 1:
94 dmVerbosity++;
95 break;
96
97 case 2:
98 engine.optVFlags |= SDL_FULLSCREEN;
99 break;
100
101 case 3:
102 {
103 int w, h;
104 if (sscanf(optArg, "%dx%d", &w, &h) == 2)
105 {
106 if (w < 320 || h < 200 || w > 3200 || h > 3200)
107 {
108 dmError("Invalid width or height: %d x %d\n", w, h);
109 return FALSE;
110 }
111 engine.optScrWidth = w;
112 engine.optScrHeight = h;
113 }
114 else
115 {
116 dmError("Invalid size argument '%s'.\n", optArg);
117 return FALSE;
118 }
119 }
120 break;
121
122 case 4:
123 optOutFormat = JSS_AUDIO_S16;
124 break;
125
126 case 5:
127 optOutFormat = JSS_AUDIO_U8;
128 break;
129
130 case 6:
131 optOutChannels = JSS_AUDIO_MONO;
132 break;
133
134 case 7:
135 optOutChannels = JSS_AUDIO_STEREO;
136 break;
137
138 case 8:
139 optOutFreq = atoi(optArg);
140 break;
141
142 case 9:
143 optMuteOChannels = atoi(optArg);
144 break;
145
146 case 10:
147 optStartOrder = atoi(optArg);
148 break;
149
150 case 11:
151 optPlayTime = atoi(optArg);
152 optUsePlayTime = TRUE;
153 break;
154
155 default:
156 dmError("Unknown option '%s'.\n", currArg);
157 return FALSE;
158 }
159
160 return TRUE;
161 }
162
163
164 BOOL argHandleFile(char *currArg)
165 {
166 if (!optFilename)
167 optFilename = currArg;
168 else
169 {
170 dmError("Too many filename arguments '%s'\n", currArg);
171 return FALSE;
172 }
173
174 return TRUE;
175 }
176
177
178 void dmDrawBMTextConstQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt)
179 {
180 const char *ptr = fmt;
181 DMUnscaledBlitFunc blit = NULL;
182
183 while (*ptr)
184 {
185 int ch = *ptr++;
186 SDL_Surface *glyph;
187
188 if (ch == '_')
189 {
190 xc += 4;
191 continue;
192 }
193
194 if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL)
195 {
196 if (blit == NULL)
197 blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode);
198
199 blit(glyph, xc, yc, screen);
200 xc += font->width;
201 }
202 else
203 xc += font->width;
204 }
205 }
206
207
208 void dmDrawBMTextVAQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, va_list ap)
209 {
210 char tmp[512];
211 vsnprintf(tmp, sizeof(tmp), fmt, ap);
212 dmDrawBMTextConstQ(screen, font, mode, xc, yc, tmp);
213 }
214
215
216 void dmDrawBMTextQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, ...)
217 {
218 va_list ap;
219
220 va_start(ap, fmt);
221 dmDrawBMTextVAQ(screen, font, mode, xc, yc, fmt, ap);
222 va_end(ap);
223 }
224
225
226 Uint32 dmCol(float r, float g, float b)
227 {
228 return dmMapRGB(engine.screen, 255.0f * r, 255.0f * g, 255.0f * b);
229 }
230
231
232 BOOL dmInitializeVideo()
233 {
234 SDL_FreeSurface(engine.screen);
235
236 engine.screen = SDL_SetVideoMode(
237 engine.optScrWidth, engine.optScrHeight, engine.optScrDepth,
238 engine.optVFlags | SDL_RESIZABLE | SDL_SWSURFACE | SDL_HWPALETTE);
239
240 if (engine.screen == NULL)
241 {
242 dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
243 return FALSE;
244 }
245
246 col.inboxBg = dmCol(0.6, 0.5, 0.2);
247 col.boxBg = dmCol(0.7, 0.6, 0.3);
248 col.box1 = dmCol(1.0, 0.9, 0.6);
249 col.box2 = dmCol(0.3, 0.3, 0.15);
250 col.viewDiv = dmCol(0,0,0);
251 col.activeRow = dmCol(0.5,0.4,0.1);
252 col.activeChannel = dmCol(0.6, 0.8, 0.2);
253
254 return TRUE;
255 }
256
257
258 void dmDisplayChn(SDL_Surface *screen, int x0, int y0, int x1, int y1, int nchannel, JSSChannel *chn)
259 {
260 int yh = y1 - y0 - 2;
261 if (yh < 10 || chn == NULL)
262 return;
263
264 int xc, ym = y0 + (y1 - y0) / 2, vol = FP_GETH(chn->chVolume);
265 int pitch = screen->pitch / sizeof(Uint32);
266 int len = FP_GETH(chn->chSize);
267 DMFixedPoint offs = chn->chPos;
268 Uint32 coln = dmCol(0.0, 0.8, 0.0), colx = dmCol(1.0, 0, 0);
269 Uint32 *pix = screen->pixels;
270 Sint16 *data = chn->chData;
271
272
273 dmFillBox3D(screen, x0, y0, x1, y1,
274 (chn->chMute ? dmCol(0.3,0.1,0.1) : dmCol(0,0,0)),
275 nchannel == engine.actChannel ? colx : col.box2,
276 nchannel == engine.actChannel ? colx : col.box1);
277
278 if (chn->chData == NULL || !chn->chPlaying)
279 return;
280
281 if (chn->chDirection)
282 {
283 for (xc = x0 + 1; xc < x1 - 1; xc++)
284 {
285 if (FP_GETH(offs) >= len)
286 break;
287 Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255);
288 pix[xc + val * pitch] = coln;
289 FP_ADD(offs, chn->chDeltaO);
290 }
291 }
292 else
293 {
294 for (xc = x0 + 1; xc < x1 - 1; xc++)
295 {
296 if (FP_GETH(offs) < 0)
297 break;
298 Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255);
299 pix[xc + val * pitch] = coln;
300 FP_SUB(offs, chn->chDeltaO);
301 }
302 }
303 }
304
305
306 void dmDisplayChannels(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSMixer *dev)
307 {
308 int nchannel, qx, qy,
309 qwidth = x1 - x0,
310 qheight = y1 - y0,
311 nwidth = jsetNChannels,
312 nheight = 1;
313
314 if (qheight < 40)
315 return;
316
317 while (qwidth / nwidth <= 60 && qheight / nheight >= 40)
318 {
319 nheight++;
320 nwidth /= nheight;
321 }
322
323 // fprintf(stderr, "%d x %d\n", nwidth, nheight);
324
325 if (qheight / nheight <= 40)
326 {
327 nwidth = qwidth / 60;
328 nheight = qheight / 40;
329 }
330
331 qwidth /= nwidth;
332 qheight /= nheight;
333
334 for (nchannel = qy = 0; qy < nheight && nchannel < jsetNChannels; qy++)
335 {
336 for (qx = 0; qx < nwidth && nchannel < jsetNChannels; qx++)
337 {
338 int xc = x0 + qx * qwidth,
339 yc = y0 + qy * qheight;
340
341 dmDisplayChn(screen, xc + 1, yc + 1,
342 xc + qwidth - 1, yc + qheight - 1,
343 nchannel, &dev->channels[nchannel]);
344
345 nchannel++;
346 }
347 }
348 }
349
350
351 static const char patNoteTable[12][3] =
352 {
353 "C-", "C#", "D-",
354 "D#", "E-", "F-",
355 "F#", "G-", "G#",
356 "A-", "A#", "B-"
357 };
358
359
360 #define jmpNMODEffectTable (36)
361 static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
362
363 static const char jmpHexTab[16] = "0123456789ABCDEF";
364
365 static inline char dmHexVal(int v)
366 {
367 return jmpHexTab[v & 15];
368 }
369
370 void dmPrintNote(SDL_Surface *screen, int xc, int yc, JSSNote *n)
371 {
372 char text[32];
373 char *ptr = text;
374
375 switch (n->note)
376 {
377 case jsetNotSet:
378 strcpy(ptr, "..._");
379 break;
380 case jsetNoteOff:
381 strcpy(ptr, "===_");
382 break;
383 default:
384 sprintf(ptr, "%s%i_",
385 patNoteTable[n->note % 12],
386 n->note / 12);
387 break;
388 }
389
390 ptr += 4;
391
392 if (n->instrument != jsetNotSet)
393 {
394 int v = n->instrument + 1;
395 *ptr++ = dmHexVal(v >> 4);
396 *ptr++ = dmHexVal(v);
397 }
398 else
399 {
400 *ptr++ = '.';
401 *ptr++ = '.';
402 }
403 *ptr++ = '_';
404
405 if (n->volume == jsetNotSet)
406 {
407 *ptr++ = '.';
408 *ptr++ = '.';
409 }
410 else
411 if (n->volume >= 0x00 && n->volume <= 0x40)
412 {
413 *ptr++ = dmHexVal(n->volume >> 4);
414 *ptr++ = dmHexVal(n->volume);
415 }
416 else
417 {
418 char c;
419 switch (n->volume & 0xf0)
420 {
421 case 0x50: c = '-'; break;
422 case 0x60: c = '+'; break;
423 case 0x70: c = '/'; break;
424 case 0x80: c = '\\'; break;
425 case 0x90: c = 'S'; break;
426 case 0xa0: c = 'V'; break;
427 case 0xb0: c = 'P'; break;
428 case 0xc0: c = '<'; break;
429 case 0xd0: c = '>'; break;
430 case 0xe0: c = 'M'; break;
431 default: c = '?'; break;
432 }
433 *ptr++ = c;
434 *ptr++ = dmHexVal(n->volume);
435 }
436 *ptr++ = '_';
437
438 if (n->effect >= 0 && n->effect < jmpNMODEffectTable)
439 *ptr++ = jmpMODEffectTable[n->effect];
440 else
441 *ptr++ = (n->effect == jsetNotSet ? '.' : '?');
442
443 if (n->param != jsetNotSet)
444 {
445 *ptr++ = dmHexVal(n->param >> 4);
446 *ptr++ = dmHexVal(n->param);
447 }
448 else
449 {
450 *ptr++ = '.';
451 *ptr++ = '.';
452 }
453
454 *ptr = 0;
455
456 dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT, xc, yc, text);
457 }
458
459
460 void dmDisplayPattern(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSPattern *pat, int row)
461 {
462 int cwidth = (font->width * 10 + 3 * 4 + 5),
463 lwidth = 6 + font->width * 3,
464 qy0 = y0 + font->height + 2,
465 qy1 = y1 - font->height - 2,
466 qwidth = ((x1 - x0 - lwidth) / cwidth),
467 qheight = ((qy1 - qy0 - 4) / (font->height + 1)),
468 nrow, nchannel, yc, choffs,
469 midrow = qheight / 2;
470
471 if (engine.actChannel < qwidth / 2)
472 choffs = 0;
473 else
474 if (engine.actChannel >= pat->nchannels - qwidth/2)
475 choffs = pat->nchannels - qwidth;
476 else
477 choffs = engine.actChannel - qwidth/2;
478
479 dmDrawBox3D(screen, x0 + lwidth, qy0, x1, qy1, col.box2, col.box1);
480
481 for (nchannel = 0; nchannel < qwidth; nchannel++)
482 {
483 int bx0 = x0 + lwidth + 1 + nchannel * cwidth,
484 bx1 = bx0 + cwidth;
485
486 if (engine.actChannel == nchannel + choffs)
487 {
488 dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.activeChannel);
489 }
490 else
491 {
492 dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.inboxBg);
493 }
494 }
495
496 yc = qy0 + 2 + (font->height + 1) * midrow;
497 dmFillRect(screen, x0 + lwidth + 1, yc - 1, x1 - 1, yc + font->height, col.activeRow);
498
499 for (nchannel = 0; nchannel < qwidth; nchannel++)
500 {
501 int bx0 = x0 + lwidth + 1 + nchannel * cwidth,
502 bx1 = bx0 + cwidth;
503
504 dmDrawVLine(screen, qy0 + 1, qy1 - 1, bx1, col.viewDiv);
505
506 if (jvmGetMute(engine.dev, nchannel + choffs))
507 {
508 dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT,
509 bx0 + (cwidth - font->width * 5) / 2, qy1 + 3, "MUTED");
510 }
511
512 dmDrawBMTextQ(screen, font, DMD_TRANSPARENT,
513 bx0 + (cwidth - font->width * 3) / 2, y0 + 1, "%3d",
514 nchannel + choffs);
515 }
516
517 for (nrow = 0; nrow < qheight; nrow++)
518 {
519 int crow = nrow - midrow + row;
520 yc = qy0 + 2 + (font->height + 1) * nrow;
521
522 if (crow >= 0 && crow < pat->nrows)
523 {
524 dmDrawBMTextQ(screen, font, DMD_TRANSPARENT, x0, yc, "%03d", crow);
525
526 for (nchannel = 0; nchannel < qwidth; nchannel++)
527 {
528 if (choffs + nchannel >= pat->nchannels)
529 break;
530
531 dmPrintNote(screen, x0 + lwidth + 4 + nchannel * cwidth, yc,
532 pat->data + (pat->nchannels * crow) + choffs + nchannel);
533 }
534 }
535 }
536 }
537
538
539 void audioCallback(void *userdata, Uint8 *stream, int len)
540 {
541 JSSMixer *d = (JSSMixer *) userdata;
542
543 if (d != NULL)
544 {
545 jvmRenderAudio(d, stream, len / jvmGetSampleSize(d));
546 }
547 }
548
549
550 void dmMuteChannels(BOOL mute)
551 {
552 int i;
553 for (i = 0; i < engine.mod->nchannels; i++)
554 jvmMute(engine.dev, i, mute);
555 }
556
557 int main(int argc, char *argv[])
558 {
559 BOOL initSDL = FALSE, audioInit = FALSE;
560 DMResource *file = NULL;
561 int result = -1;
562 BOOL muteState = FALSE;
563
564 memset(&engine, 0, sizeof(engine));
565
566 engine.optScrWidth = 640;
567 engine.optScrHeight = 480;
568 engine.optScrDepth = 32;
569
570 dmInitProg("CBP", "Cyrbe Basci Player", "0.1", NULL, NULL);
571
572 // Parse arguments
573 if (!dmArgsProcess(argc, argv, optList, optListN,
574 argHandleOpt, argHandleFile, TRUE))
575 exit(1);
576
577 // Open the files
578 if (optFilename == NULL)
579 {
580 dmError("No filename specified.\n");
581 return 1;
582 }
583
584 if ((file = dmf_create_stdio(optFilename, "rb")) == NULL)
585 {
586 int err = dmGetErrno();
587 dmError("Error opening file '%s', %d: (%s)\n",
588 optFilename, err, dmErrorStr(err));
589 return 1;
590 }
591
592 // Initialize miniJSS
593 jssInit();
594
595 // Read module file
596 dmMsg(1, "Reading file: %s\n", optFilename);
597 #ifdef JSS_SUP_XM
598 dmMsg(2, "* Trying XM...\n");
599 result = jssLoadXM(file, &engine.mod);
600 #endif
601 #ifdef JSS_SUP_JSSMOD
602 if (result != 0)
603 {
604 size_t bufgot, bufsize = dmfsize(file);
605 Uint8 *buf = dmMalloc(bufsize);
606 dmfseek(file, 0L, SEEK_SET);
607 dmMsg(2, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf);
608 if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize)
609 {
610 dmf_close(file);
611 dmError("Error reading file (not enough data %d), #%d: %s\n",
612 bufgot, dmferror(file), dmErrorStr(dmferror(file)));
613 goto error_exit;
614 }
615 result = jssLoadJSSMOD(buf, bufsize, &engine.mod);
616 dmFree(buf);
617 }
618 #endif
619 dmf_close(file);
620
621 if (result != DMERR_OK)
622 {
623 dmError("Error loading module file, %d: %s\n",
624 result, dmErrorStr(result));
625 goto error_exit;
626 }
627
628 // Try to convert it
629 if ((result = jssConvertModuleForPlaying(engine.mod)) != DMERR_OK)
630 {
631 dmError("Could not convert module for playing, %d: %s\n",
632 result, dmErrorStr(result));
633 goto error_exit;
634 }
635
636 // Get font
637 // file = dmf_create_stdio("fnsmall.fnt", "rb");
638 file = dmf_create_memio(NULL, "pplfont.fnt", engineSetupFont, sizeof(engineSetupFont));
639 if (file == NULL)
640 {
641 dmError("Error opening font file 'pplfont.fnt'.\n");
642 goto error_exit;
643 }
644 result = dmLoadBitmapFont(file, &font);
645 dmf_close(file);
646 if (result != DMERR_OK)
647 {
648 dmError("Could not load font from file, %d: %s\n",
649 result, dmErrorStr(result));
650 goto error_exit;
651 }
652
653 // Initialize SDL components
654 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
655 {
656 dmError("Could not initialize SDL: %s\n", SDL_GetError());
657 goto error_exit;
658 }
659 initSDL = TRUE;
660
661
662 // Initialize mixing device
663 dmMsg(2, "Initializing miniJSS mixer with: %d, %d, %d\n",
664 optOutFormat, optOutChannels, optOutFreq);
665
666 engine.dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO);
667 if (engine.dev == NULL)
668 {
669 dmError("jvmInit() returned NULL\n");
670 goto error_exit;
671 }
672
673 switch (optOutFormat)
674 {
675 case JSS_AUDIO_S16: engine.afmt.format = AUDIO_S16SYS; break;
676 case JSS_AUDIO_U16: engine.afmt.format = AUDIO_U16SYS; break;
677 case JSS_AUDIO_S8: engine.afmt.format = AUDIO_S8; break;
678 case JSS_AUDIO_U8: engine.afmt.format = AUDIO_U8; break;
679 default:
680 dmError("Unsupported audio format %d (could not set matching SDL format)\n",
681 optOutFormat);
682 goto error_exit;
683 }
684
685 engine.afmt.freq = optOutFreq;
686 engine.afmt.channels = optOutChannels;
687 engine.afmt.samples = optOutFreq / 16;
688 engine.afmt.callback = audioCallback;
689 engine.afmt.userdata = (void *) engine.dev;
690
691 // Open the audio device
692 if (SDL_OpenAudio(&engine.afmt, NULL) < 0)
693 {
694 dmError("Couldn't open SDL audio: %s\n",
695 SDL_GetError());
696 goto error_exit;
697 }
698 audioInit = TRUE;
699
700 // Initialize player
701 if ((engine.plr = jmpInit(engine.dev)) == NULL)
702 {
703 dmError("jmpInit() returned NULL\n");
704 goto error_exit;
705 }
706
707 jvmSetCallback(engine.dev, jmpExec, engine.plr);
708 jmpSetModule(engine.plr, engine.mod);
709 jmpPlayOrder(engine.plr, optStartOrder);
710 jvmSetGlobalVol(engine.dev, 64);
711
712 if (optMuteOChannels >= 0 && optMuteOChannels < engine.mod->nchannels)
713 {
714 dmMuteChannels(TRUE);
715 jvmMute(engine.dev, optMuteOChannels, FALSE);
716 engine.actChannel = optMuteOChannels;
717 muteState = TRUE;
718 }
719
720 // Initialize video
721 if (!dmInitializeVideo())
722 goto error_exit;
723
724 SDL_WM_SetCaption(dmProgDesc, dmProgName);
725
726 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
727
728 // okay, main loop here ... "play" module and print out info
729 SDL_LockAudio();
730 SDL_PauseAudio(0);
731 SDL_UnlockAudio();
732
733 int currTick, prevTick = 0, prevRow = -1;
734
735 while (!engine.exitFlag)
736 {
737 currTick = SDL_GetTicks();
738 BOOL force = (currTick - prevTick > 500), updated = FALSE;
739
740 while (SDL_PollEvent(&engine.event))
741 switch (engine.event.type)
742 {
743 case SDL_KEYDOWN:
744 switch (engine.event.key.keysym.sym)
745 {
746 case SDLK_ESCAPE:
747 engine.exitFlag = TRUE;
748 break;
749
750 case SDLK_SPACE:
751 engine.pauseFlag = !engine.pauseFlag;
752 SDL_PauseAudio(engine.pauseFlag);
753 break;
754
755 case SDLK_LEFT:
756 if (engine.actChannel > 0)
757 {
758 engine.actChannel--;
759 force = TRUE;
760 }
761 break;
762
763 case SDLK_RIGHT:
764 if (engine.actChannel < engine.mod->nchannels)
765 {
766 engine.actChannel++;
767 force = TRUE;
768 }
769 break;
770
771 case SDLK_m:
772 if (engine.event.key.keysym.mod & KMOD_SHIFT)
773 {
774 muteState = !muteState;
775 dmMuteChannels(muteState);
776 }
777 else
778 if (engine.event.key.keysym.mod & KMOD_CTRL)
779 {
780 dmMuteChannels(FALSE);
781 }
782 else
783 {
784 jvmMute(engine.dev, engine.actChannel, !jvmGetMute(engine.dev, engine.actChannel));
785 }
786 force = TRUE;
787 break;
788
789 case SDLK_PAGEUP:
790 JSS_LOCK(engine.dev);
791 JSS_LOCK(engine.plr);
792 jmpChangeOrder(engine.plr, dmClamp(engine.plr->order - 1, 0, engine.mod->norders));
793 JSS_UNLOCK(engine.plr);
794 JSS_UNLOCK(engine.dev);
795 force = TRUE;
796 break;
797
798 case SDLK_PAGEDOWN:
799 JSS_LOCK(engine.dev);
800 JSS_LOCK(engine.plr);
801 jmpChangeOrder(engine.plr, dmClamp(engine.plr->order + 1, 0, engine.mod->norders));
802 JSS_UNLOCK(engine.plr);
803 JSS_UNLOCK(engine.dev);
804 force = TRUE;
805 break;
806
807 case SDLK_f:
808 engine.optVFlags ^= SDL_FULLSCREEN;
809 if (!dmInitializeVideo())
810 goto error_exit;
811 force = TRUE;
812 break;
813
814 default:
815 break;
816 }
817
818 break;
819
820 case SDL_VIDEORESIZE:
821 engine.optScrWidth = engine.event.resize.w;
822 engine.optScrHeight = engine.event.resize.h;
823
824 if (!dmInitializeVideo())
825 goto error_exit;
826
827 break;
828
829 case SDL_VIDEOEXPOSE:
830 break;
831
832 case SDL_QUIT:
833 engine.exitFlag = TRUE;
834 break;
835 }
836
837
838 #if 1
839 JSS_LOCK(engine.plr);
840 JSSPattern *currPattern = engine.plr->pattern;
841 int currRow = engine.plr->row;
842 if (!engine.plr->isPlaying)
843 engine.exitFlag = TRUE;
844 JSS_UNLOCK(engine.plr);
845
846 if (currRow != prevRow || force)
847 {
848 prevRow = currRow;
849 force = TRUE;
850 }
851
852 // Draw frame
853 if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
854 {
855 dmError("Can't lock surface.\n");
856 goto error_exit;
857 }
858
859 if (force)
860 {
861 dmClearSurface(engine.screen, col.boxBg);
862
863 dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5, "%s v%s by ccr/TNSP - (c) Copyright 2012 TNSP", dmProgDesc, dmProgVersion);
864
865 dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12 + 11,
866 "Song: '%s'",
867 engine.mod->moduleName);
868
869 dmDisplayPattern(engine.screen, 5, 40,
870 engine.screen->w - 6, engine.screen->h * 0.8,
871 currPattern, currRow);
872
873 JSS_LOCK(engine.plr);
874 dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12,
875 "Tempo: %3d | Speed: %3d | Row: %3d/%-3d | Order: %3d/%-3d | Pattern: %3d/%-3d",
876 engine.plr->tempo, engine.plr->speed,
877 engine.plr->row, engine.plr->pattern->nrows,
878 engine.plr->order, engine.mod->norders,
879 engine.plr->npattern, engine.mod->npatterns);
880 JSS_UNLOCK(engine.plr);
881 updated = TRUE;
882 }
883
884 if (force || currTick - prevTick >= (engine.pauseFlag ? 100 : 20))
885 {
886 JSS_LOCK(engine.dev);
887 dmDisplayChannels(engine.screen, 5, engine.screen->h * 0.8 + 5,
888 engine.screen->w - 5, engine.screen->h - 5, engine.dev);
889 JSS_UNLOCK(engine.dev);
890 updated = TRUE;
891 }
892
893 if (force)
894 prevTick = currTick;
895
896 #endif
897 // Flip screen
898 if (SDL_MUSTLOCK(engine.screen) != 0)
899 SDL_UnlockSurface(engine.screen);
900
901 if (updated)
902 SDL_Flip(engine.screen);
903
904 SDL_Delay(engine.pauseFlag ? 100 : 30);
905 }
906
907 error_exit:
908 if (engine.screen)
909 SDL_FreeSurface(engine.screen);
910
911 dmMsg(0, "Audio shutdown.\n");
912 if (audioInit)
913 {
914 SDL_LockAudio();
915 SDL_PauseAudio(1);
916 SDL_UnlockAudio();
917 SDL_CloseAudio();
918 }
919
920 jmpClose(engine.plr);
921 jvmClose(engine.dev);
922 jssFreeModule(engine.mod);
923
924 dmFreeBitmapFont(font);
925
926 if (initSDL)
927 SDL_Quit();
928
929 jssClose();
930
931 return 0;
932 }