1 /* OpenCP Module Player
2 * copyright (c) '94-'21 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3 *
4 * GMDPlay interface routines
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * revision history: (please note changes here)
21 * -nb980510 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
22 * -first release
23 * -ss040709 Stian Skjelstad <stian@nixia.no>
24 * -use compatible timing, and now cputime/clock()
25 */
26
27 #include "config.h"
28 #include <time.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include "types.h"
34 #include "boot/plinkman.h"
35 #include "boot/psetting.h"
36 #include "cpiface/cpiface.h"
37 #include "dev/deviwave.h"
38 #include "dev/mcp.h"
39 #include "filesel/filesystem.h"
40 #include "filesel/mdb.h"
41 #include "filesel/pfilesel.h"
42 #include "gmdpchan.h"
43 #include "gmdpdots.h"
44 #include "gmdplay.h"
45 #include "gmdptrak.h"
46 #include "stuff/compat.h"
47 #include "stuff/err.h"
48 #include "stuff/poutput.h"
49
50 #define _MAX_FNAME 8
51 #define _MAX_EXT 4
52
53 static int gmdActive;
54
55 static const char *modname;
56 static const char *composer;
57
58 static char currentmodname[_MAX_FNAME+1];
59 static char currentmodext[_MAX_EXT+1];
60
61 static time_t starttime;
62 static time_t pausetime;
63
64 static struct gmdmodule mod;
65 static char patlock;
66
gmdMarkInsSamp(uint8_t * ins,uint8_t * samp)67 static void gmdMarkInsSamp(uint8_t *ins, uint8_t *samp)
68 {
69 int i;
70 for (i=0; i<plNLChan; i++)
71 {
72 struct chaninfo ci;
73 mpGetChanInfo(i, &ci);
74
75 if (!mpGetMute(i)&&mpGetChanStatus(i)&&ci.vol)
76 {
77 ins[ci.ins]=((plSelCh==i)||(ins[ci.ins]==3))?3:2;
78 samp[ci.smp]=((plSelCh==i)||(samp[ci.smp]==3))?3:2;
79 }
80 }
81 }
82
mpLoadGen(struct gmdmodule * m,struct ocpfilehandle_t * file,int type)83 static int mpLoadGen(struct gmdmodule *m, struct ocpfilehandle_t *file, int type)
84 {
85 char secname[20];
86 const char *link;
87 const char *name;
88 int hnd;
89 struct gmdloadstruct *loadfn;
90 volatile uint8_t retval;
91
92 sprintf(secname, "filetype %d", type&0xff);
93
94 link=cfGetProfileString(secname, "ldlink", "");
95 name=cfGetProfileString(secname, "loader", "");
96
97 #ifdef LD_DEBUG
98 fprintf(stderr, " (%s) Trying to locate \"%s\", func \"%s\"\n", secname, link, name);
99 #endif
100
101 hnd=lnkLink(link);
102 if (hnd<=0)
103 {
104 #ifdef LD_DEBUG
105 fprintf(stderr, "Failed to locate ldlink \"%s\"\n", link);
106 #endif
107 return errSymMod;
108 }
109
110 loadfn=_lnkGetSymbol(name);
111 if (!loadfn)
112 {
113 #ifdef LD_DEBUG
114 fprintf(stderr, "Failed to locate loaded \"%s\"\n", name);
115 #endif
116 lnkFree(hnd);
117 return errSymSym;
118 }
119 #ifdef LD_DEBUG
120 fprintf(stderr, "Loading using %s-%s\n", link, name);
121 #endif
122 memset(m->composer, 0, sizeof(m->composer));
123 retval=loadfn->load(m, file);
124
125 lnkFree(hnd);
126
127 return retval;
128 }
129
130 void mcpSetFadePars(int i);
131
132 static time_t pausefadestart;
133 static uint8_t pausefaderelspeed;
134 static int8_t pausefadedirect;
135
startpausefade(void)136 static void startpausefade(void)
137 {
138 if (plPause)
139 starttime=starttime+dos_clock()-pausetime;
140
141 if (pausefadedirect)
142 {
143 if (pausefadedirect<0)
144 plPause=1;
145 pausefadestart=2*dos_clock()-DOS_CLK_TCK-pausefadestart;
146 } else
147 pausefadestart=dos_clock();
148
149 if (plPause)
150 {
151 plChanChanged=1;
152 mcpSet(-1, mcpMasterPause, plPause=0);
153 pausefadedirect=1;
154 } else
155 pausefadedirect=-1;
156 }
157
dopausefade(void)158 static void dopausefade(void)
159 {
160 int16_t i;
161 if (pausefadedirect>0)
162 {
163 i=((int32_t)dos_clock()-pausefadestart)*64/DOS_CLK_TCK;
164 if (i<0)
165 i=0;
166 if (i>=64)
167 {
168 i=64;
169 pausefadedirect=0;
170 }
171 } else {
172 i=64-((int32_t)dos_clock()-pausefadestart)*64/DOS_CLK_TCK;
173 if (i>=64)
174 i=64;
175 if (i<=0)
176 {
177 i=0;
178 pausefadedirect=0;
179 pausetime=dos_clock();
180 mcpSet(-1, mcpMasterPause, plPause=1);
181 plChanChanged=1;
182 mcpSetFadePars(64);
183 return;
184 }
185 }
186 pausefaderelspeed=i;
187 mcpSetFadePars(i);
188 }
189
gmdDrawGStrings(unsigned short (* buf)[CONSOLE_MAX_X])190 static void gmdDrawGStrings(unsigned short (*buf)[CONSOLE_MAX_X])
191 {
192 struct globinfo gi;
193 uint32_t tim;
194
195 mcpDrawGStrings(buf);
196
197 mpGetGlobInfo(&gi);
198
199 if (plPause)
200 tim=(pausetime-starttime)/DOS_CLK_TCK;
201 else
202 tim=(dos_clock()-starttime)/DOS_CLK_TCK;
203
204 if (plScrWidth<128)
205 {
206 memset(buf[2]+80, 0, (plScrWidth-80)*sizeof(uint16_t));
207
208 writestring(buf[1], 0, 0x09, " row: ../.. ord: .../... tempo: .. bpm: ... gvol: ..\xfa ", 58);
209 writenum(buf[1], 6, 0x0F, gi.currow, 16, 2, 0);
210 writenum(buf[1], 9, 0x0F, gi.patlen-1, 16, 2, 0);
211 writenum(buf[1], 18, 0x0F, gi.curpat, 16, 3, 0);
212 writenum(buf[1], 22, 0x0F, gi.patnum-1, 16, 3, 0);
213 writenum(buf[1], 34, 0x0F, gi.tempo, 16, 2, 1);
214 writenum(buf[1], 43, 0x0F, gi.speed, 10, 3, 1);
215 writenum(buf[1], 54, 0x0F, gi.globvol, 16, 2, 0);
216 writestring(buf[1], 56, 0x0F, (gi.globvolslide==fxGVSUp)?"\x18":(gi.globvolslide==fxGVSDown)?"\x19":" ", 1);
217
218 writestring(buf[2], 0, 0x09, " module \xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa.\xfa\xfa\xfa: ............................... time: ..:.. ", 80);
219 writestring(buf[2], 8, 0x0F, currentmodname, _MAX_FNAME);
220 writestring(buf[2], 16, 0x0F, currentmodext, _MAX_EXT);
221 writestring(buf[2], 22, 0x0F, modname, 31);
222 if (plPause)
223 writestring(buf[2], 58, 0x0C, "paused", 6);
224 writenum(buf[2], 74, 0x0F, (tim/60)%60, 10, 2, 1);
225 writestring(buf[2], 76, 0x0F, ":", 1);
226 writenum(buf[2], 77, 0x0F, tim%60, 10, 2, 0);
227 } else {
228 memset(buf[2]+128, 0, (plScrWidth-128)*sizeof(uint16_t));
229
230 writestring(buf[1], 0, 0x09, " row: ../.. order: .../... tempo: .. speed/bpm: ... global volume: ..\xfa ", 81);
231 writenum(buf[1], 9, 0x0F, gi.currow, 16, 2, 0);
232 writenum(buf[1], 12, 0x0F, gi.patlen-1, 16, 2, 0);
233 writenum(buf[1], 23, 0x0F, gi.curpat, 16, 3, 0);
234 writenum(buf[1], 27, 0x0F, gi.patnum-1, 16, 3, 0);
235 writenum(buf[1], 40, 0x0F, gi.tempo, 16, 2, 1);
236 writenum(buf[1], 55, 0x0F, gi.speed, 10, 3, 1);
237 writenum(buf[1], 76, 0x0F, gi.globvol, 16, 2, 0);
238 writestring(buf[1], 78, 0x0F, (gi.globvolslide==fxGVSUp)?"\x18":(gi.globvolslide==fxGVSDown)?"\x19":" ", 1);
239
240 writestring(buf[2], 0, 0x09, " module \xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa.\xfa\xfa\xfa: ............................... composer: ............................... time: ..:.. ", 132);
241 writestring(buf[2], 11, 0x0F, currentmodname, _MAX_FNAME);
242 writestring(buf[2], 19, 0x0F, currentmodext, _MAX_EXT);
243 writestring(buf[2], 25, 0x0F, modname, 31);
244 writestring(buf[2], 68, 0x0F, composer, 31);
245 if (plPause)
246 writestring(buf[2], 100, 0x0C, "playback paused", 15);
247 writenum(buf[2], 123, 0x0F, (tim/60)%60, 10, 2, 1);
248 writestring(buf[2], 125, 0x0F, ":", 1);
249 writenum(buf[2], 126, 0x0F, tim%60, 10, 2, 0);
250 }
251 }
252
253 #define dgetch() egetch()
254 #define dkbhit() ekbhit()
255 #define releaseslice() {} /* we don't have this feature one generic unixes other than nice stuff */
256
gmdProcessKey(unsigned short key)257 static int gmdProcessKey(unsigned short key)
258 {
259 uint16_t pat;
260 uint8_t row;
261 switch (key)
262 {
263 case KEY_ALT_K:
264 cpiKeyHelp(KEY_ALT_L, "Pattern lock toggle");
265 cpiKeyHelp('p', "Start/stop pause with fade");
266 cpiKeyHelp('P', "Start/stop pause with fade");
267 cpiKeyHelp(KEY_CTRL_UP, "Jump back (small)");
268 cpiKeyHelp(KEY_CTRL_DOWN, "Jump forward (small)");
269 cpiKeyHelp(KEY_CTRL_P, "Start/stop pause");
270 cpiKeyHelp('<', "Jump back (big)");
271 cpiKeyHelp(KEY_CTRL_LEFT, "Jump back (big)");
272 cpiKeyHelp('>', "Jump forward (big)");
273 cpiKeyHelp(KEY_CTRL_RIGHT, "Jump forward (big)");
274 mcpSetProcessKey(key);
275 if (mcpProcessKey)
276 mcpProcessKey(key);
277 return 0;
278 #if 0
279 case KEY_ALT_P:
280 /* case 0x1900: alt-p */
281 while (!dkbhit())
282 {
283 if (mcpIdle)
284 mcpIdle();
285 releaseslice();
286 }
287 while (dkbhit())
288 dgetch();
289 break;
290 #endif
291 case 'p': case 'P':
292 startpausefade();
293 break;
294 case KEY_CTRL_P:
295 pausefadedirect=0;
296 if (plPause)
297 starttime=starttime+dos_clock()-pausetime;
298 else
299 pausetime=dos_clock();
300 mcpSet(-1, mcpMasterPause, plPause^=1);
301 plChanChanged=1;
302 break;
303 /*
304 case 0x7700: //ctrl-home TODO keys
305 gmdInstClear();
306
307 mpSetPosition(0, 0);
308
309 if (plPause)
310 starttime=pausetime;
311 else
312 starttime=dos_clock();
313 break;*/
314 case '<':
315 case KEY_CTRL_LEFT:
316 /* case 0x7300: //ctrl-left */
317 mpGetPosition(&pat, &row);
318 mpSetPosition(pat-1, 0);
319 break;
320 case '>':
321 case KEY_CTRL_RIGHT:
322 /* case 0x7400: //ctrl-right */
323 mpGetPosition(&pat, &row);
324 mpSetPosition(pat+1, 0);
325 break;
326 case KEY_CTRL_UP:
327 /*case 0x8D00: // ctrl-up */
328 mpGetPosition(&pat, &row);
329 mpSetPosition(pat, row-8);
330 break;
331 case KEY_CTRL_DOWN:
332 /*case 0x9100: //ctrl-down */
333 mpGetPosition(&pat, &row);
334 mpSetPosition(pat, row+8);
335 break;
336 case KEY_ALT_L:
337 /* case 0x2600: alt-l */
338 patlock=!patlock;
339 mpLockPat(patlock);
340 break;
341 default:
342 if (mcpSetProcessKey(key))
343 return 1;
344 if (mcpProcessKey)
345 {
346 int ret=mcpProcessKey(key);
347 if (ret==2)
348 cpiResetScreen();
349 if (ret)
350 return 1;
351 }
352 }
353 return 1;
354 }
355
gmdCloseFile(void)356 static void gmdCloseFile(void)
357 {
358 gmdActive=0;
359 mpStopModule();
360 mpFree(&mod);
361 }
362
gmdIdle(void)363 static void gmdIdle(void)
364 {
365 mpSetLoop(fsLoopMods);
366 if (mcpIdle)
367 mcpIdle();
368 if (pausefadedirect)
369 dopausefade();
370 }
371
gmdLooped(void)372 static int gmdLooped(void)
373 {
374 return (!fsLoopMods&&mpLooped());
375 }
376
gmdOpenFile(struct moduleinfostruct * info,struct ocpfilehandle_t * file)377 static int gmdOpenFile (struct moduleinfostruct *info, struct ocpfilehandle_t *file)
378 {
379 uint64_t i;
380 int retval;
381
382 if (!mcpOpenPlayer)
383 return errGen;
384
385 if (!file)
386 return errFileOpen;
387
388 patlock=0;
389
390 strncpy(currentmodname, info->name, _MAX_FNAME);
391 strncpy(currentmodext, info->name + _MAX_FNAME, _MAX_EXT);
392
393 i = file->filesize (file);
394 fprintf(stderr, "loading %s%s (%uk)...\n", currentmodname, currentmodext, (unsigned int)(i>>10));
395
396 retval=mpLoadGen(&mod, file, info->modtype);
397
398 if (!retval)
399 {
400 int sampsize=0;
401 fprintf(stderr, "preparing samples (");
402 for (i=0; i<mod.sampnum; i++)
403 sampsize+=(mod.samples[i].length)<<(!!(mod.samples[i].type&mcpSamp16Bit));
404 fprintf(stderr, "%ik)...\n", sampsize>>10);
405
406 if (!mpReduceSamples(&mod))
407 retval=errAllocMem;
408 else if (!mpLoadSamples(&mod))
409 retval=errAllocSamp;
410 else {
411 mpReduceMessage(&mod);
412 mpReduceInstruments(&mod);
413 mpOptimizePatLens(&mod);
414 }
415 } else {
416 fprintf(stderr, "mpLoadGen failed\n");
417 mpFree(&mod);
418 return retval;
419 }
420
421 if (retval)
422 mpFree(&mod);
423
424 if (retval)
425 return retval;
426
427 if (plCompoMode)
428 mpRemoveText(&mod);
429 plNLChan=mod.channum;
430 modname=mod.name;
431 composer=mod.composer;
432 plPanType=!!(mod.options&MOD_MODPAN);
433
434 plIsEnd=gmdLooped;
435 plIdle=gmdIdle;
436 plProcessKey=gmdProcessKey;
437 plDrawGStrings=gmdDrawGStrings;
438 plSetMute=mpMute;
439 plGetLChanSample=mpGetChanSample;
440 plUseDots(gmdGetDots);
441 if (mod.message)
442 plUseMessage(mod.message);
443 gmdInstSetup(mod.instruments, mod.instnum, mod.modsamples, mod.modsampnum, mod.samples, mod.sampnum, ((info->modtype==mtS3M)||(info->modtype==mtPTM))?1:((info->modtype==mtDMF)||(info->modtype==mt669))?2:0, gmdMarkInsSamp);
444 gmdChanSetup(&mod);
445 gmdTrkSetup(&mod);
446
447 if (!plCompoMode)
448 {
449 if (!*modname)
450 modname=info->modname;
451 if (!*composer)
452 composer=info->composer;
453 } else
454 modname=info->comment;
455
456 mcpNormalize(1);
457 if (!mpPlayModule(&mod, file))
458 retval=errPlay;
459 plNPChan=mcpNChan;
460
461 plGetRealMasterVolume=mcpGetRealMasterVolume;
462 plGetMasterSample=mcpGetMasterSample;
463 plGetPChanSample=mcpGetChanSample;
464
465 if (retval)
466 {
467 mpFree(&mod);
468 return retval;
469 }
470
471 starttime=dos_clock();
472 plPause=0;
473 mcpSet(-1, mcpMasterPause, 0);
474 pausefadedirect=0;
475
476 gmdActive=1;
477
478 return errOk;
479 }
480
481 struct cpifaceplayerstruct gmdPlayer = {gmdOpenFile, gmdCloseFile};
482
483 char *dllinfo = "";
484 struct linkinfostruct dllextinfo = {.name = "playgmd", .desc = "OpenCP General Module Player (c) 1994-21 Niklas Beisert, Tammo Hinrichs, Stian Skjelstad", .ver = DLLVERSION, .size = 0};
485