1 /*-----------------------------------------------------------------------------
2 
3 	ST-Sound ( YM files player library )
4 
5 	Copyright (C) 1995-1999 Arnaud Carre ( http://leonard.oxg.free.fr )
6 
7 	YM Music Driver
8 
9 -----------------------------------------------------------------------------*/
10 
11 /*-----------------------------------------------------------------------------
12 
13 	This file is part of ST-Sound
14 
15 	ST-Sound is free software; you can redistribute it and/or modify
16 	it under the terms of the GNU General Public License as published by
17 	the Free Software Foundation; either version 2 of the License, or
18 	(at your option) any later version.
19 
20 	ST-Sound is distributed in the hope that it will be useful,
21 	but WITHOUT ANY WARRANTY; without even the implied warranty of
22 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 	GNU General Public License for more details.
24 
25 	You should have received a copy of the GNU General Public License
26 	along with ST-Sound; if not, write to the Free Software
27 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 
29 -----------------------------------------------------------------------------*/
30 
31 #include <string.h>
32 #include <stdlib.h>
33 #include "YmMusic.h"
34 
35 #define	_LINEAR_OVRS				// Activate linear oversampling (best quality) Only used for DigiMix and UniversalTracker YM file type
36 
37 
38 // ATARI-ST MFP chip predivisor
39 static	const ymint	mfpPrediv[8] = {0,4,10,16,50,64,100,200};
40 
41 
42 
43 CYmMusic::CYmMusic(ymint _replayRate)
44 {
45 
46 	pBigMalloc = NULL;
47 	pSongName = NULL;
48 	pSongAuthor = NULL;
49 	pSongComment = NULL;
50 	pSongType = NULL;
51 	pSongPlayer = NULL;
52 
53 	pBigSampleBuffer = NULL;
54 	pMixBlock = NULL;
55 
56 	replayRate = _replayRate;
57 	innerSamplePos = 0;
58 	nbDrum = 0;
59 	pDrumTab = NULL;
60 	setLoopMode(YMFALSE);
61 }
62 
63 void	CYmMusic::setTimeControl(ymbool bTime)
64 {
65 		if (bTime)
66 			attrib |= A_TIMECONTROL;
67 		else
68 			attrib &= (~A_TIMECONTROL);
69 }
70 
71 CYmMusic::~CYmMusic()
72 {
73 		stop();
74 		unLoad();
75 }
76 
77 void	CYmMusic::setLoopMode(ymbool bLoopMode)
78 {
79 		bLoop = bLoopMode;
80 }
81 
82 void	CYmMusic::setPlayerRate(ymint rate)
83 {
84 		playerRate = rate;
85 }
86 
87 ymu32 CYmMusic::getPos()
88 {
89 	if (!isSeekable()) return 0;
90 	if ((nbFrame>0) && (playerRate>0))
91 	{
92 		return ((ymu32)currentFrame*1000)/(ymu32)playerRate;
93 	}
94 	else
95 		return 0;
96 
97 }
98 
99 ymu32	CYmMusic::getMusicTime(void)
100 {
101 		if ((nbFrame>0) && (playerRate>0))
102 		{
103 			return ((ymu32)nbFrame*1000)/(ymu32)playerRate;
104 		}
105 		else
106 			return 0;
107 
108 }
109 
110 ymu32	CYmMusic::setMusicTime(ymu32 time)
111 {
112 		if (!isSeekable()) return 0;
113 		ymu32 newTime = 0;
114 
115 		if ((songType>=YM_V2) && (songType<YM_VMAX))
116 		{
117 			newTime = time;
118 			if (newTime>=getMusicTime()) newTime = 0;
119 			currentFrame = (newTime*(ymu32)playerRate)/1000;
120 		}
121 		else if ((songType>=YM_TRACKER1) && (songType<YM_TRACKERMAX))
122 		{
123 			newTime = time;
124 			if (newTime>=getMusicTime()) newTime = 0;
125 			currentFrame = (newTime*(ymu32)playerRate)/1000;
126 		}
127 
128 		return newTime;
129 }
130 
131 void	CYmMusic::getMusicInfo(ymMusicInfo_t *pInfo)
132 {
133 		if (pInfo)
134 		{
135 			pInfo->pSongName = pSongName;
136 			pInfo->pSongAuthor = pSongAuthor;
137 			pInfo->pSongComment = pSongComment;
138 			pInfo->pSongType = pSongType;
139 			pInfo->pSongPlayer = pSongPlayer;
140 
141 			if (playerRate>0)
142 			{
143 				pInfo->musicTimeInMs = (ymu32(nbFrame) * 1000) / (ymu32)playerRate;
144 				pInfo->musicTimeInSec = pInfo->musicTimeInMs / 1000;
145 			}
146 			else
147 			{
148 				pInfo->musicTimeInSec = 0;
149 				pInfo->musicTimeInMs = 0;
150 			}
151 		}
152 }
153 
154 
155 void	CYmMusic::setAttrib(ymint _attrib)
156 {
157 		attrib = _attrib;
158 }
159 
160 ymint		CYmMusic::getAttrib(void)
161 {
162 		return attrib;
163 }
164 
165 ymbool	CYmMusic::isSeekable(void)
166 {
167 		return getAttrib()&A_TIMECONTROL;
168 }
169 
170 void	CYmMusic::setLastError(const char *pError)
171 {
172 		pLastError = pError;
173 }
174 
175 const char *CYmMusic::getLastError(void)
176 {
177 		return pLastError;
178 }
179 
180 void	bufferClear(ymsample *pBuffer,ymint nbSample)
181 {
182 		memset((void*)pBuffer,0,nbSample*sizeof(ymsample));
183 }
184 
185 ymbool	CYmMusic::update(ymsample *sampleBuffer,ymint nbSample)
186 {
187 ymint sampleToCompute;
188 ymint	vblNbSample;
189 
190 
191 		if ((!bMusicOk) ||
192 			(bPause) ||
193 			(bMusicOver))
194 		{
195 			bufferClear(sampleBuffer,nbSample);
196 			if (bMusicOver)
197 				return YMFALSE;
198 			else
199 				return YMTRUE;
200 		}
201 
202 		if ((songType >= YM_MIX1) && (songType < YM_MIXMAX))
203 		{
204 			stDigitMix(sampleBuffer,nbSample);
205 		}
206 		else if ((songType >= YM_TRACKER1) && (songType<YM_TRACKERMAX))
207 		{
208 			ymTrackerUpdate(sampleBuffer,nbSample);
209 		}
210 		else
211 		{
212 			ymsample *pOut = sampleBuffer;
213 			ymint nbs = nbSample;
214 			vblNbSample = replayRate/playerRate;
215 			do
216 			{
217 				// Nb de sample � calculer avant l'appel de Player
218 				sampleToCompute = vblNbSample-innerSamplePos;
219 				// Test si la fin du buffer arrive avant la fin de sampleToCompute
220 				if (sampleToCompute>nbs) sampleToCompute = nbs;
221 				innerSamplePos += sampleToCompute;
222 				if (innerSamplePos>=vblNbSample)
223 				{
224 					player();			// Lecture de la partition (playerRate Hz)
225 					innerSamplePos -= vblNbSample;
226 				}
227 				if (sampleToCompute>0)
228 				{
229 					ymChip.update(pOut,sampleToCompute);	// YM Emulation.
230 					pOut += sampleToCompute;
231 				}
232 				nbs -= sampleToCompute;
233 			}
234 			while (nbs>0);
235 		}
236 
237 
238 		return YMTRUE;
239 }
240 
241 
242 
243 void	CYmMusic::readYm6Effect(unsigned char *pReg,ymint code,ymint prediv,ymint count)
244 {
245 ymint voice;
246 ymint ndrum;
247 
248 		code = pReg[code]&0xf0;
249 		prediv = (pReg[prediv]>>5)&7;
250 		count = pReg[count];
251 
252 		if (code&0x30)
253 		{
254 			ymu32 tmpFreq;
255 			// Ici il y a un effet sur la voie:
256 
257 			voice = ((code&0x30)>>4)-1;
258 			switch (code&0xc0)
259 			{
260 				case 0x00:		// SID
261 				case 0x80:		// Sinus-SID
262 
263 					prediv = mfpPrediv[prediv];
264 					prediv *= count;
265 					tmpFreq = 0;
266 					if (prediv)
267 					{
268 						tmpFreq = 2457600L / prediv;
269 						if ((code&0xc0)==0x00)
270 							ymChip.sidStart(voice,tmpFreq,pReg[voice+8]&15);
271 						else
272 							ymChip.sidSinStart(voice,tmpFreq,pReg[voice+8]&15);
273 					}
274 					break;
275 
276 				case 0x40:		// DigiDrum
277 					ndrum = pReg[voice+8]&31;
278 					if ((ndrum>=0) && (ndrum<nbDrum))
279 					{
280 						prediv = mfpPrediv[prediv];
281 						prediv *= count;
282 						if (prediv>0)
283 						{
284 							tmpFreq = 2457600L / prediv;
285 							ymChip.drumStart(voice,pDrumTab[ndrum].pData,pDrumTab[ndrum].size,tmpFreq);
286 						}
287 					}
288 					break;
289 
290 				case 0xc0:		// Sync-Buzzer.
291 
292 					prediv = mfpPrediv[prediv];
293 					prediv *= count;
294 					tmpFreq = 0;
295 					if (prediv)
296 					{
297 						tmpFreq = 2457600L / prediv;
298 						ymChip.syncBuzzerStart(tmpFreq,pReg[voice+8]&15);
299 					}
300 					break;
301 
302 
303 			}
304 
305 		}
306 }
307 
308 void	CYmMusic::setVolume(ymint volume)
309 {
310 //		ymChip.setGlobalVolume(volume);
311 }
312 
313 void	CYmMusic::player(void)
314  {
315  ymu8	*ptr;
316  ymu32 prediv;
317  ymint voice;
318  ymint ndrum;
319 
320 
321 	if (currentFrame<0) currentFrame = 0;
322 
323 	if (currentFrame>=nbFrame)
324 	{
325 		if (bLoop)
326 		{
327 			currentFrame = loopFrame;
328 		}
329 		else
330 		{
331 			bMusicOver = YMTRUE;
332 			ymChip.reset();
333 			return;
334 		}
335 	}
336 
337 	ptr = pDataStream+currentFrame*streamInc;
338 
339 	for (ymint i=0;i<=10;i++)
340 		ymChip.writeRegister(i,ptr[i]);
341 
342 	ymChip.sidStop(0);
343 	ymChip.sidStop(1);
344 	ymChip.sidStop(2);
345 	ymChip.syncBuzzerStop();
346 
347 	//---------------------------------------------
348 	// Check digi-drum
349 	//---------------------------------------------
350 	if (songType == YM_V2)		// MADMAX specific !
351 	{
352 		if (ptr[13]!=0xff)
353 		{
354 			ymChip.writeRegister(11,ptr[11]);
355 			ymChip.writeRegister(12,0);
356 			ymChip.writeRegister(13,10);				// MADMAX specific !!
357 		}
358 		if (ptr[10]&0x80)					// bit 7 volume canal C pour annoncer une digi-drum madmax.
359 		{
360 			ymint	sampleNum;
361 			ymu32 sampleFrq;
362 			ymChip.writeRegister(7,ymChip.readRegister(7)|0x24)	;	// Coupe TONE + NOISE canal C.
363 			sampleNum = ptr[10]&0x7f;		// Numero du sample
364 
365 			if (ptr[12])
366 			{
367 				if (sampleNum < MAX_DIGIDRUM)
368 				{
369 					sampleFrq = (MFP_CLOCK / ptr[12]);
370 					ymChip.drumStart(	2,							// Voice C
371 										sampleAdress[sampleNum],
372 										sampleLen[sampleNum],
373 										sampleFrq);
374 				}
375 			}
376 		}
377 	}
378 	else if (songType >= YM_V3)
379 	{
380 		ymChip.writeRegister(11,ptr[11]);
381 		ymChip.writeRegister(12,ptr[12]);
382 		if (ptr[13]!=0xff)
383 		{
384 			ymChip.writeRegister(13,ptr[13]);
385 		}
386 
387 		if (songType >= YM_V5)
388 		{
389 			ymint code;
390 
391 			if (songType == YM_V6)
392 			{
393 				readYm6Effect(ptr,1,6,14);
394 				readYm6Effect(ptr,3,8,15);
395 			}
396 			else
397 			{	// YM5 effect decoding
398 
399 			//------------------------------------------------------
400 			// Sid Voice !!
401 			//------------------------------------------------------
402 				code = (ptr[1]>>4)&3;
403 				if (code!=0)
404 				{
405 					ymu32 tmpFreq;
406 					voice = code-1;
407 					prediv = mfpPrediv[(ptr[6]>>5)&7];
408 					prediv *= ptr[14];
409 					tmpFreq = 0;
410 					if (prediv)
411 					{
412 						tmpFreq = 2457600L / prediv;
413 						ymChip.sidStart(voice,tmpFreq,ptr[voice+8]&15);
414 					}
415 				}
416 
417 			//------------------------------------------------------
418 			// YM5 Digi Drum.
419 			//------------------------------------------------------
420 				code = (ptr[3]>>4)&3;
421 				if (code!=0)
422 				{	// Ici un digidrum demarre sur la voie voice.
423 					voice = code-1;
424 					ndrum = ptr[8+voice]&31;
425 					if ((ndrum>=0) && (ndrum<nbDrum))
426 					{
427 						ymu32 sampleFrq;
428 						prediv = mfpPrediv[(ptr[8]>>5)&7];
429 						prediv *= ptr[15];
430 						if (prediv)
431 						{
432 							sampleFrq = MFP_CLOCK / prediv;
433 							ymChip.drumStart(voice,pDrumTab[ndrum].pData,pDrumTab[ndrum].size,sampleFrq);
434 						}
435 					}
436 				}
437 			}
438 		}
439 	}
440 	currentFrame++;
441  }
442 
443 /*
444 
445 	x x x x x x x x	r0
446 	0 0 0 0 x x x x	r1		// Special FX 1a
447 	x x x x x x x x	r2
448 	0 0 0 0 x x x x r3		// Special FX 2a
449 	x x x x x x x x r4
450 	0 0 0 0 x x x x r5
451 	0 0 0 x x x x x r6		// Special FX 1b
452 	0 0 x x x x x x r7
453 	0 0 0 x x x x x r8		// Special FX 2b
454 	0 0 0 x x x x x r9
455 	0 0 0 x x x x x r10
456 	x x x x x x x x r11
457 	x x x x x x x x r12
458 	0 0 0 0 x x x x r13
459 	0 0 0 0 0 0 0 0 r14		// Special FX 1c
460 	0 0 0 0 0 0 0 0 r15		// Special FX 2c
461 
462 
463   Special Fx ?a
464 	0 0 0 0	: No special FX running
465 	0 0 0 1 : Sid Voice A
466 	0 0 1 0 : Sid Voice B
467 	0 0 1 1 : Sid Voice C
468 	0 1 0 0 : Extended Fx voice A
469 	0 1 0 1 : Digidrum voice A
470 	0 1 1 0 : Digidrum voice B
471 	0 1 1 1 : Digidrum voice C
472 	1 0 0 0 : Extended Fx voice B
473 	1 0 0 1 : Sinus SID voice A
474 	1 0 1 0 : Sinus SID voice B
475 	1 0 1 1 : Sinus SID voice C
476 	1 1 0 0 : Extended Fx voice C
477 	1 1 0 1 : Sync Buzzer voice A
478 	1 1 1 0 : Sync Buzzer voice B
479 	1 1 1 1 : Sync Buzzer voice C
480 
481 
482 
483 */
484 
485 void	CYmMusic::readNextBlockInfo(void)
486 {
487 	nbRepeat--;
488 	if (nbRepeat<=0)
489 	{
490 		mixPos++;
491 		if (mixPos >= nbMixBlock)
492 		{
493 			mixPos = 0;
494 			if (!bLoop) bMusicOver = YMTRUE;
495 		}
496 		nbRepeat = pMixBlock[mixPos].nbRepeat;
497 	}
498 	pCurrentMixSample = pBigSampleBuffer + pMixBlock[mixPos].sampleStart;
499 	currentSampleLength = (pMixBlock[mixPos].sampleLength)<<12;
500 	currentPente = (((ymu32)pMixBlock[mixPos].replayFreq)<<12) / PC_DAC_FREQ;
501 	currentPos &= ((1<<12)-1);
502 }
503 
504 void	CYmMusic::stDigitMix(ymsample *pWrite16,ymint nbs)
505 {
506 
507 
508 		if (bMusicOver) return;
509 
510 		if (mixPos == -1)
511 		{
512 			nbRepeat = -1;
513 			readNextBlockInfo();
514 		}
515 
516 		if (nbs) do
517 		{
518 
519 			ymint sa = (ymint)(ymsample)(pCurrentMixSample[currentPos>>12]<<8);
520 #ifdef _LINEAR_OVRS
521 			ymint sb = sa;
522 			if ((currentPos>>12)<((currentSampleLength>>12)-1))
523 				sb = (ymint)(ymsample)(pCurrentMixSample[(currentPos>>12)+1]<<8);
524 			ymint frac = currentPos&((1<<12)-1);
525 			sa += (((sb-sa)*frac)>>12);
526 #endif
527 			*pWrite16++ = sa;
528 
529 			currentPos += currentPente;
530 			if (currentPos>=currentSampleLength)
531 			{
532 				readNextBlockInfo();
533 				if (bMusicOver) return;
534 			}
535 		}
536 		while (--nbs);
537 }
538 
539 void	CYmMusic::ymTrackerDesInterleave(void)
540 {
541 unsigned char *a0,*a1,*a2;
542 unsigned char *pNewBuffer;
543 ymint	step;
544 ymu32 n1,n2;
545 
546 
547 		if (attrib&A_STREAMINTERLEAVED)
548 		{
549 			a0 = pDataStream;
550 			ymint size = sizeof(ymTrackerLine_t)*nbVoice*nbFrame;
551 			pNewBuffer = (unsigned char*)malloc(size);
552 			step = sizeof(ymTrackerLine_t)*nbVoice;
553 			n1 = step;
554 			a2 = pNewBuffer;
555 			do
556 			{
557 				n2 = nbFrame;
558 				a1 = a2;
559 				do
560 				{
561 					*a1 = *a0++;
562 					a1 += step;
563 				}
564 				while (--n2);
565 				a2++;
566 			}
567 			while (--n1);
568 			memcpy(pDataStream,pNewBuffer,size);
569 			free(pNewBuffer);
570 			attrib &= (~A_STREAMINTERLEAVED);
571 		}
572 }
573 
574 
575 void	CYmMusic::ymTrackerInit(ymint volMaxPercent)
576 {
577 ymint i,s;
578 ymint vol;
579 ymint scale;
580 ymsample *pTab;
581 
582 
583 
584 		for (i=0;i<MAX_VOICE;i++)
585 			ymTrackerVoice[i].bRunning = 0;
586 
587 		ymTrackerNbSampleBefore = 0;
588 
589 		scale = (256*volMaxPercent) / (nbVoice*100);
590 		pTab = ymTrackerVolumeTable;
591 
592 		// Construit la table de volume.
593 		for (vol=0;vol<64;vol++)
594 		{
595 			for (s=-128;s<128;s++)
596 			{
597 				*pTab++ = (s*(ymint)scale*vol)/64;
598 			}
599 		}
600 
601 		// Des-interleave si necessaire.
602 		ymTrackerDesInterleave();
603 
604 }
605 
606 
607 void	CYmMusic::ymTrackerPlayer(ymTrackerVoice_t *pVoice)
608 {
609 ymint i;
610 ymTrackerLine_t *pLine;
611 
612 
613 		pLine = (ymTrackerLine_t*)pDataStream;
614 		pLine += (currentFrame*nbVoice);
615 	  	for (i=0;i<nbVoice;i++)
616 		{
617 			ymint n;
618 			pVoice[i].sampleFreq = ((ymint)pLine->freqHigh<<8) | pLine->freqLow;
619 			if (pVoice[i].sampleFreq)
620 			{
621 				pVoice[i].sampleVolume = pLine->volume&63;
622 				pVoice[i].bLoop = (pLine->volume&0x40);
623 				n = pLine->noteOn;
624 				if (n != 0xff)		// Note ON.
625 				{
626 					pVoice[i].bRunning = 1;
627 					pVoice[i].pSample = pDrumTab[n].pData;
628 					pVoice[i].sampleSize = pDrumTab[n].size;
629 					pVoice[i].repLen = pDrumTab[n].repLen;
630 					pVoice[i].samplePos = 0;
631 				}
632 			}
633 			else
634 			{
635 				pVoice[i].bRunning = 0;
636 			}
637 			pLine++;
638 		}
639 
640 		currentFrame++;
641 		if (currentFrame >= nbFrame)
642 		{
643 			if (!bLoop)
644 			{
645 				bMusicOver = YMTRUE;
646 			}
647 			currentFrame = 0;
648 		}
649 }
650 
651 
652 void	CYmMusic::ymTrackerVoiceAdd(ymTrackerVoice_t *pVoice,ymsample *pBuffer,ymint nbs)
653 {
654 ymsample *pVolumeTab;
655 ymu8 *pSample;
656 ymu32 samplePos;
657 ymu32 sampleEnd;
658 ymu32 sampleInc;
659 ymu32 repLen;
660 double	step;
661 
662 
663 		if (!(pVoice->bRunning)) return;
664 
665 		pVolumeTab = &ymTrackerVolumeTable[256*(pVoice->sampleVolume&63)];
666 		pSample = pVoice->pSample;
667 		samplePos = pVoice->samplePos;
668 
669 		step = (double)(pVoice->sampleFreq<<YMTPREC);
670 		step *= (double)(1<<ymTrackerFreqShift);
671 		step /= (double)replayRate;
672 		sampleInc = (ymu32)step;
673 
674 		sampleEnd = (pVoice->sampleSize<<YMTPREC);
675 		repLen = (pVoice->repLen<<YMTPREC);
676 		if (nbs>0) do
677 		{
678 			ymint va = pVolumeTab[pSample[samplePos>>YMTPREC]];
679 #ifdef _LINEAR_OVRS
680 			ymint vb = va;
681 			if (samplePos < (sampleEnd-(1<<YMTPREC)))
682 				vb = pVolumeTab[pSample[(samplePos>>YMTPREC)+1]];
683 			ymint frac = samplePos & ((1<<YMTPREC)-1);
684 			va += (((vb-va)*frac)>>YMTPREC);
685 #endif
686 			(*pBuffer++) += va;
687 
688 			samplePos += sampleInc;
689 			if (samplePos>=sampleEnd)
690 			{
691 				if (pVoice->bLoop)
692 				{
693 					samplePos -= repLen;
694 				}
695 				else
696 				{
697 					pVoice->bRunning = 0;
698 					return;
699 				}
700 			}
701 		}
702 		while (--nbs);
703 		pVoice->samplePos = samplePos;
704 }
705 
706 void	CYmMusic::ymTrackerUpdate(ymsample *pBuffer,ymint nbSample)
707 {
708 ymint i;
709 ymint _nbs;
710 
711 		// Clear les buffers.
712 		memset(pBuffer,0,sizeof(ymsample)*nbSample);
713 		if (bMusicOver) return;
714 
715 		do
716 		{
717 			if (ymTrackerNbSampleBefore == 0)
718 			{
719 				// Lit la partition ymTracker
720 				ymTrackerPlayer(ymTrackerVoice);
721 				if (bMusicOver) return;
722 				ymTrackerNbSampleBefore = YMTNBSRATE;
723 			}
724 			_nbs = ymTrackerNbSampleBefore;		// nb avant playerUpdate.
725 			if (_nbs>nbSample) _nbs = nbSample;
726 			ymTrackerNbSampleBefore -= _nbs;
727 			if (_nbs>0)
728 			{
729 				// Genere les samples.
730 				for (i=0;i<nbVoice;i++)
731 				{
732 					ymTrackerVoiceAdd(&ymTrackerVoice[i],pBuffer,_nbs);
733 				}
734 				pBuffer += _nbs;
735 				nbSample -= _nbs;
736 			}
737 		}
738 		while (nbSample>0);
739 }
740