1 /* OpenCP Module Player
2  * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3  * copyright (c) '04-'20 Stian Skjelstad <stian.skjelstad@gmail.com>
4  *
5  * MPPlay interface routines
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #include "config.h"
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include "types.h"
28 #include "boot/plinkman.h"
29 #include "boot/psetting.h"
30 #include "cpiface/cpiface.h"
31 #include "dev/deviplay.h"
32 #include "dev/player.h"
33 #include "filesel/filesystem.h"
34 #include "filesel/mdb.h"
35 #include "filesel/pfilesel.h"
36 #include "mpplay.h"
37 #include "stuff/compat.h"
38 #include "stuff/err.h"
39 #include "stuff/poutput.h"
40 #include "stuff/sets.h"
41 
42 #define _MAX_FNAME 8
43 #define _MAX_EXT 4
44 
45 static uint32_t mpeglen;
46 static uint32_t mpegrate;
47 static time_t starttime;
48 static time_t pausetime;
49 static char currentmodname[_MAX_FNAME+1];
50 static char currentmodext[_MAX_EXT+1];
51 static char *modname;
52 static char *composer;
53 static int16_t vol;
54 static int16_t bal;
55 static int16_t pan;
56 static char srnd;
57 
58 static uint32_t amp;
59 static int16_t speed;
60 static int16_t reverb;
61 static int16_t chorus;
62 static char finespeed=8;
63 
64 static time_t pausefadestart;
65 static uint8_t pausefaderelspeed;
66 static int8_t pausefadedirect;
67 
startpausefade(void)68 static void startpausefade(void)
69 {
70 	if (plPause)
71 		starttime=starttime+dos_clock()-pausetime;
72 
73 	if (pausefadedirect)
74 	{
75 		if (pausefadedirect<0)
76 			plPause=1;
77 		pausefadestart=2*dos_clock()-DOS_CLK_TCK-pausefadestart;
78 	} else
79 		pausefadestart=dos_clock();
80 
81 	if (plPause)
82 	{
83 		plChanChanged=1;
84 		mpegPause(plPause=0);
85 		pausefadedirect=1;
86 	} else
87 		pausefadedirect=-1;
88 }
89 
dopausefade(void)90 static void dopausefade(void)
91 {
92 	int16_t i;
93 	if (pausefadedirect>0)
94 	{
95 		i=(dos_clock()-pausefadestart)*64/DOS_CLK_TCK;
96 		if (i<0)
97 			i=0;
98 		if (i>=64)
99 		{
100 			i=64;
101 			pausefadedirect=0;
102 		}
103 	} else {
104 		i=64-(dos_clock()-pausefadestart)*64/DOS_CLK_TCK;
105 		if (i>=64)
106 			i=64;
107 		if (i<=0)
108 		{
109 			i=0;
110 			pausefadedirect=0;
111 			pausetime=dos_clock();
112 			mpegPause(plPause=1);
113 			plChanChanged=1;
114 			mpegSetSpeed(speed);
115 			return;
116 		}
117 	}
118 	pausefaderelspeed=i;
119 	mpegSetSpeed(speed*i/64);
120 }
121 
mpegDrawGStrings(uint16_t (* buf)[CONSOLE_MAX_X])122 static void mpegDrawGStrings(uint16_t (*buf)[CONSOLE_MAX_X])
123 {
124 	struct mpeginfo inf;
125 	uint32_t tim2;
126 	int l;
127 	int p;
128 
129 	mpegGetInfo(&inf);
130 
131 	l=(inf.len>>(10/*-inf.stereo-inf.bit16*/)); /* these now refer to offset in file */
132 	if (!l) l=1;
133 	p=(inf.pos>>(10/*-inf.stereo-inf.bit16*/)); /* these now refer to offset in file */
134 
135 	if (plPause)
136 		tim2=(pausetime-starttime)/DOS_CLK_TCK;
137 	else
138 		tim2=(dos_clock()-starttime)/DOS_CLK_TCK;
139 
140 	if (plScrWidth<128)
141 	{
142 		memset(buf[0]+80, 0, (plScrWidth-80)*sizeof(uint16_t));
143 		memset(buf[1]+80, 0, (plScrWidth-80)*sizeof(uint16_t));
144 		memset(buf[2]+80, 0, (plScrWidth-80)*sizeof(uint16_t));
145 
146 		writestring(buf[0], 0, 0x09, " vol: \xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa ", 15);
147 		writestring(buf[0], 15, 0x09, " srnd: \xfa  pan: l\xfa\xfa\xfam\xfa\xfa\xfar  bal: l\xfa\xfa\xfam\xfa\xfa\xfar ", 41);
148 		writestring(buf[0], 56, 0x09, " spd: ---% \x1D ptch: ---% ", 24);
149 		writestring(buf[0], 6, 0x0F, "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", (vol+4)>>3);
150 		writestring(buf[0], 22, 0x0F, srnd?"x":"o", 1);
151 		if (((pan+70)>>4)==4)
152 			writestring(buf[0], 34, 0x0F, "m", 1);
153 		else {
154 			writestring(buf[0], 30+((pan+70)>>4), 0x0F, "r", 1);
155 			writestring(buf[0], 38-((pan+70)>>4), 0x0F, "l", 1);
156 		}
157 		writestring(buf[0], 46+((bal+70)>>4), 0x0F, "I", 1);
158 		_writenum(buf[0], 62, 0x0F, speed*100/256, 10, 3);
159 		_writenum(buf[0], 75, 0x0F, speed*100/256, 10, 3);
160 
161 		writestring(buf[1], 57, 0x09, "amp: ...% filter: ...  ", 23);
162 		_writenum(buf[1], 62, 0x0F, amp*100/64, 10, 3);
163 		writestring(buf[1], 75, 0x0F, "off", 3);
164 
165 		writestring(buf[1], 0, 0x09, "  pos: ...% / ......k  size: ......k            ", 57);
166 		_writenum(buf[1], 7, 0x0F, p*100/l, 10, 3);
167 		writenum(buf[1], 29, 0x0F, l, 10, 6, 1);
168 		writenum(buf[1], 14, 0x0F, p, 10, 6, 1);
169 
170 		writestring(buf[2],  0, 0x09, "   mpeg \xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa.\xfa\xfa\xfa: ...............................               time: ..:.. ", 80);
171 		writestring(buf[2],  8, 0x0F, currentmodname, _MAX_FNAME);
172 		writestring(buf[2], 16, 0x0F, currentmodext, _MAX_EXT);
173 		writestring(buf[2], 22, 0x0F, modname, 31);
174 		if (plPause)
175 			writestring(buf[2], 57, 0x0C, " paused ", 8);
176 		else {
177 			writestring(buf[2], 57, 0x09, "kbps: ", 6);
178 			writenum(buf[2], 63, 0x0F, mpeg_Bitrate, 10, 3, 1);
179 		}
180 		writenum(buf[2], 74, 0x0F, (tim2/60)%60, 10, 2, 1);
181 		writestring(buf[2], 76, 0x0F, ":", 1);
182 		writenum(buf[2], 77, 0x0F, tim2%60, 10, 2, 0);
183 	} else {
184 		memset(buf[0]+128, 0, (plScrWidth-128)*sizeof(uint16_t));
185 		memset(buf[1]+128, 0, (plScrWidth-128)*sizeof(uint16_t));
186 		memset(buf[2]+128, 0, (plScrWidth-128)*sizeof(uint16_t));
187 
188 		writestring(buf[0], 0, 0x09, "    volume: \xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa  ", 30);
189 		writestring(buf[0], 30, 0x09, " surround: \xfa   panning: l\xfa\xfa\xfa\xfa\xfa\xfa\xfam\xfa\xfa\xfa\xfa\xfa\xfa\xfar   balance: l\xfa\xfa\xfa\xfa\xfa\xfa\xfam\xfa\xfa\xfa\xfa\xfa\xfa\xfar  ", 72);
190 		writestring(buf[0], 102, 0x09,  " speed: ---% \x1D pitch: ---%    ", 30);
191 		writestring(buf[0], 12, 0x0F, "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", (vol+2)>>2);
192 		writestring(buf[0], 41, 0x0F, srnd?"x":"o", 1);
193 		if (((pan+68)>>3)==8)
194 			writestring(buf[0], 62, 0x0F, "m", 1);
195 		else {
196 			writestring(buf[0], 54+((pan+68)>>3), 0x0F, "r", 1);
197 			writestring(buf[0], 70-((pan+68)>>3), 0x0F, "l", 1);
198 		}
199 		writestring(buf[0], 83+((bal+68)>>3), 0x0F, "I", 1);
200 		_writenum(buf[0], 110, 0x0F, speed*100/256, 10, 3);
201 		_writenum(buf[0], 124, 0x0F, speed*100/256, 10, 3);
202 
203 		writestring(buf[1], 0, 0x09, "    position: ...% / ......k  size: ......k                 opt: .....Hz, .. bit, ......", 92);
204 		_writenum(buf[1], 14, 0x0F, p*100/l, 10, 3);
205 		writenum(buf[1], 36, 0x0F, l, 10, 6, 1);
206 		writenum(buf[1], 21, 0x0F, p, 10, 6, 1);
207 		writenum(buf[1], 65, 0x0F, inf.rate, 10, 5, 1);
208 		writenum(buf[1], 74, 0x0F, 8<<inf.bit16, 10, 2, 1);
209 		writestring(buf[1], 82, 0x0F, inf.stereo?"stereo":"mono", 6);
210 
211 		writestring(buf[1], 92, 0x09, "   amplification: ...%  filter: ...     ", 40);
212 		_writenum(buf[1], 110, 0x0F, amp*100/64, 10, 3);
213 		writestring(buf[1], 124, 0x0F, "off", 3);
214 
215 
216 		if (plPause)
217 			tim2=(pausetime-starttime)/DOS_CLK_TCK;
218 		else
219 			tim2=(dos_clock()-starttime)/DOS_CLK_TCK;
220 
221 		writestring(buf[2],  0, 0x09, "      mpeg \xfa\xfa\xfa\xfa\xfa\xfa\xfa\xfa.\xfa\xfa\xfa: ...............................  composer: ...............................                  time: ..:..    ", 132);
222 		writestring(buf[2], 11, 0x0F, currentmodname, _MAX_FNAME);
223 		writestring(buf[2], 19, 0x0F, currentmodext, _MAX_EXT);
224 		writestring(buf[2], 25, 0x0F, modname, 31);
225 		writestring(buf[2], 68, 0x0F, composer, 31);
226 		if (plPause)
227 			writestring(buf[2], 100, 0x0C, "playback paused", 15);
228 		else {
229 			writestring(buf[2], 100, 0x09, "kbps: ", 6);
230 			writenum(buf[2], 106, 0x0F, mpeg_Bitrate, 10, 3, 1);
231 		}
232 		writenum(buf[2], 123, 0x0F, (tim2/60)%60, 10, 2, 1);
233 		writestring(buf[2], 125, 0x0F, ":", 1);
234 		writenum(buf[2], 126, 0x0F, tim2%60, 10, 2, 0);
235 	}
236 }
237 
normalize(void)238 static void normalize(void)
239 {
240 	mcpNormalize(0);
241 	speed=set.speed;
242 	pan=set.pan;
243 	bal=set.bal;
244 	vol=set.vol;
245 	amp=set.amp;
246 	srnd=set.srnd;
247 	reverb=set.reverb;
248 	chorus=set.chorus;
249 	mpegSetAmplify(1024*amp);
250 	mpegSetVolume(vol, bal, pan, srnd);
251 	mpegSetSpeed(speed);
252 	/*  mpegSetMasterReverbChorus(reverb, chorus); */
253 }
254 
mpegProcessKey(uint16_t key)255 static int mpegProcessKey(uint16_t key)
256 {
257 	switch (key)
258 	{
259 		case KEY_ALT_K:
260 			cpiKeyHelp('p', "Start/stop pause with fade");
261 			cpiKeyHelp('P', "Start/stop pause with fade");
262 			cpiKeyHelp(KEY_CTRL_P, "Start/stop pause");
263 			cpiKeyHelp('<', "Jump back (big)");
264 			cpiKeyHelp(KEY_CTRL_LEFT, "Jump back (big)");
265 			cpiKeyHelp('>', "Jump forward (big)");
266 			cpiKeyHelp(KEY_CTRL_RIGHT, "Jump forward (big)");
267 			cpiKeyHelp(KEY_CTRL_UP, "Jump back (small)");
268 			cpiKeyHelp(KEY_CTRL_DOWN, "Jump forward (small(");
269 			cpiKeyHelp('-', "Decrease volume (small)");
270 			cpiKeyHelp('+', "Increase volume (small)");
271 			cpiKeyHelp('/', "Move balance left (small)");
272 			cpiKeyHelp('*', "Move balance right (small)");
273 			cpiKeyHelp(',', "Move panning against normal (small)");
274 			cpiKeyHelp('.', "Move panning against reverse (small)");
275 			cpiKeyHelp(KEY_F(2), "Decrease volume");
276 			cpiKeyHelp(KEY_F(3), "Increase volume");
277 			cpiKeyHelp(KEY_F(4), "Toggle surround on/off");
278 			cpiKeyHelp(KEY_F(5), "Move panning against normal");
279 			cpiKeyHelp(KEY_F(6), "Move panning against reverse");
280 			cpiKeyHelp(KEY_F(7), "Move balance left");
281 			cpiKeyHelp(KEY_F(8), "Move balance right");
282 			cpiKeyHelp(KEY_F(9), "Decrease pitch speed");
283 			cpiKeyHelp(KEY_F(11), "Decrease pitch speed");
284 			cpiKeyHelp(KEY_F(10), "Increase pitch speed");
285 			cpiKeyHelp(KEY_F(12), "Increase pitch speed");
286 			if (plrProcessKey)
287 				plrProcessKey(key);
288 			return 0;
289 		case 'p': case 'P':
290 			startpausefade();
291 			break;
292 		case KEY_CTRL_P:
293 			pausefadedirect=0;
294 			if (plPause)
295 				starttime=starttime+dos_clock()-pausetime;
296 			else
297 				pausetime=dos_clock();
298 			plPause=!plPause;
299 			mpegPause(plPause);
300 			break;
301 		case KEY_CTRL_UP:
302 		/* case 0x8D00: //ctrl-up */
303 			mpegSetPos(mpegGetPos()-mpegrate);
304 			break;
305 		case KEY_CTRL_DOWN:
306 		/* case 0x9100: //ctrl-down */
307 			mpegSetPos(mpegGetPos()+mpegrate);
308 			break;
309 		case '<':
310 		case KEY_CTRL_LEFT:
311 		/* case 0x7300: //ctrl-left  */
312 			{
313 				uint32_t pos = mpegGetPos();
314 				uint32_t newpos = pos - (mpeglen>>5);
315 				if (newpos > pos)
316 				{
317 					newpos = 0;
318 				}
319 				mpegSetPos(newpos);
320 			}
321 			break;
322 		case '>':
323 		case KEY_CTRL_RIGHT:
324 		/* case 0x7400: //ctrl-right */
325 			{
326 				uint32_t pos = mpegGetPos();
327 				uint32_t newpos = pos + (mpeglen>>5);
328 				if ((newpos < pos) || (newpos > mpeglen))
329 				{
330 					newpos = mpeglen - 4;
331 				}
332 				mpegSetPos(newpos);
333 			}
334 			break;
335 /*
336 		case 0x7700: //ctrl-home TODO keys
337 			mpegSetPos(0);
338 			break;
339 */
340 		case '-':
341 			if (vol>=2)
342 				vol-=2;
343 			mpegSetVolume(vol, bal, pan, srnd);
344 			break;
345 		case '+':
346 			if (vol<=62)
347 				vol+=2;
348 			mpegSetVolume(vol, bal, pan, srnd);
349 			break;
350 		case '/':
351 			if ((bal-=4)<-64)
352 				bal=-64;
353 			mpegSetVolume(vol, bal, pan, srnd);
354 			break;
355 		case '*':
356 			if ((bal+=4)>64)
357 				bal=64;
358 			mpegSetVolume(vol, bal, pan, srnd);
359 			break;
360 		case ',':
361 			if ((pan-=4)<-64)
362 				pan=-64;
363 			mpegSetVolume(vol, bal, pan, srnd);
364 			break;
365 		case '.':
366 			if ((pan+=4)>64)
367 				pan=64;
368 			mpegSetVolume(vol, bal, pan, srnd);
369 			break;
370 		case KEY_F(2):
371 			if ((vol-=8)<0)
372 				vol=0;
373 			mpegSetVolume(vol, bal, pan, srnd);
374 			break;
375 		case KEY_F(3):
376 			if ((vol+=8)>64)
377 				vol=64;
378 			mpegSetVolume(vol, bal, pan, srnd);
379 			break;
380 		case KEY_F(4):
381 			mpegSetVolume(vol, bal, pan, srnd=srnd?0:2);
382 			break;
383 		case KEY_F(5):
384 			if ((pan-=16)<-64)
385 				pan=-64;
386 			mpegSetVolume(vol, bal, pan, srnd);
387 			break;
388 		case KEY_F(6):
389 			if ((pan+=16)>64)
390 				pan=64;
391 			mpegSetVolume(vol, bal, pan, srnd);
392 			break;
393 		case KEY_F(7):
394 			if ((bal-=16)<-64)
395 				bal=-64;
396 			mpegSetVolume(vol, bal, pan, srnd);
397 			break;
398 		case KEY_F(8):
399 			if ((bal+=16)>64)
400 				bal=64;
401 			mpegSetVolume(vol, bal, pan, srnd);
402 			break;
403 		case KEY_F(9):
404 		case KEY_F(11):
405 			if ((speed-=finespeed)<16)
406 				speed=16;
407 			mpegSetSpeed(speed);
408 			break;
409 		case KEY_F(10):
410 		case KEY_F(12):
411 			if ((speed+=finespeed)>2048)
412 				speed=2048;
413 			mpegSetSpeed(speed);
414 			break;
415 			/* TODO keys
416 	case 0x5f00: // ctrl f2
417     if ((amp-=4)<4)
418       amp=4;
419     mpegSetAmplify(1024*amp);
420     break;
421   case 0x6000: // ctrl f3
422     if ((amp+=4)>508)
423       amp=508;
424     mpegSetAmplify(1024*amp);
425     break;
426   case 0x8900: // ctrl f11
427     finespeed=(finespeed==8)?1:8;
428     break;
429   case 0x6a00:
430     normalize();
431     break;
432   case 0x6900:
433     set.pan=pan;
434     set.bal=bal;
435     set.vol=vol;
436     set.speed=speed;
437     set.amp=amp;
438     set.srnd=srnd;
439     break;
440   case 0x6b00:
441     pan=64;
442     bal=0;
443     vol=64;
444     speed=256;
445     amp=64;
446     mpegSetVolume(vol, bal, pan, srnd);
447     mpegSetSpeed(speed);
448     mpegSetAmplify(1024*amp);
449     break;*/
450 		default:
451 			if (plrProcessKey)
452 			{
453 				int ret=plrProcessKey(key);
454 				if (ret==2)
455 					cpiResetScreen();
456 				if (ret)
457 					return 1;
458 			}
459 			return 0;
460 	}
461 	return 1;
462 }
463 
464 
mpegLooped(void)465 static int mpegLooped(void)
466 {
467 	if (pausefadedirect)
468 		dopausefade();
469 	mpegSetLoop(fsLoopMods);
470 	mpegIdle();
471 	if (plrIdle)
472 		plrIdle();
473 	return !fsLoopMods&&mpegIsLooped();
474 }
475 
476 
mpegCloseFile(void)477 static void mpegCloseFile(void)
478 {
479 	ID3InfoDone();
480 	mpegClosePlayer();
481 }
482 
mpegOpenFile(struct moduleinfostruct * info,struct ocpfilehandle_t * mpegfile)483 static int mpegOpenFile(struct moduleinfostruct *info, struct ocpfilehandle_t *mpegfile)
484 {
485 	struct mpeginfo inf;
486 
487 	if (!mpegfile)
488 		return -1;
489 #ifdef __GNUC__
490 # pragma GCC diagnostic push
491 # pragma GCC diagnostic ignored "-Wstringop-truncation"
492 #endif
493 	/* currentmodname and currentmodext as not expected to be zero-terminated */
494 	strncpy(currentmodname, info->name, _MAX_FNAME);
495 	strncpy(currentmodext, info->name + _MAX_FNAME, _MAX_EXT);
496 #ifdef __GNUC__
497 # pragma GCC diagnostic pop
498 #endif
499 	modname=info->modname;
500 	composer=info->composer;
501 
502 	fprintf(stderr, "loading %s%s...\n", currentmodname, currentmodext);
503 
504 	plIsEnd=mpegLooped;
505 	plProcessKey=mpegProcessKey;
506 	plDrawGStrings=mpegDrawGStrings;
507 	plGetMasterSample=plrGetMasterSample;
508 	plGetRealMasterVolume=plrGetRealMasterVolume;
509 
510 	if (mpegOpenPlayer(mpegfile))
511 		return errFileRead;
512 
513 	starttime=dos_clock();
514 	plPause=0;
515 	normalize();
516 	pausefadedirect=0;
517 
518 	mpegGetInfo(&inf);
519 	mpeglen=inf.len;
520 	mpegrate=inf.rate;
521 
522 	ID3InfoInit();
523 
524 	return errOk;
525 }
526 
527 struct cpifaceplayerstruct mpegPlayer = {mpegOpenFile, mpegCloseFile};
528 struct linkinfostruct dllextinfo = {.name = "playmp2", .desc = "OpenCP Audio MPEG Player (c) 1994-2020 Stian Skjelstad, Niklas Beisert & Tammo Hinrichs", .ver = DLLVERSION, .size = 0};
529