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