|
|
1.1 root 1: /*
1.1.1.5 root 2: Hatari - sound.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
1.1 root 6:
7: This is where we emulate the YM2149. To obtain cycle-accurate timing we store the current cycle
8: time and this is incremented during each instruction. When a write occurs in the PSG registers
9: we take the difference in time and generate this many samples using the previous register data.
10: Now we begin again from this point. To make sure we always have 1/50th of samples we update
11: the buffer generation every 1/50th second, just in case no write took place on the PSG.
12: As with most 'sample' emulation it appears very quiet. We detect for any sample playback on a channel
13: by a decay timer on the channel amplitude - this will remain high if the PSG register is constantly
14: written to. We use this decay timer to boost the output of a sampled channel so the final sound is more
15: even through-out.
1.1.1.2 root 16: NOTE: If the emulator runs slower than 50fps it cannot update the buffers, but the sound thread still
1.1 root 17: needs some data to play to prevent a 'pop'. The ONLY feasible solution is to play the same buffer again.
18: I have tried all kinds of methods to play the sound 'slower', but this produces un-even timing in the
19: sound and it simply doesn't work. If the emulator cannot keep the speed, users will have to turn off
20: the sound - that's it.
21: */
1.1.1.7 ! root 22: char Sound_rcsid[] = "Hatari $Id: sound.c,v 1.15 2004/04/23 15:33:59 thothy Exp $";
1.1.1.5 root 23:
24: #include <SDL_types.h>
1.1 root 25:
26: #include "main.h"
27: #include "audio.h"
28: #include "debug.h"
29: #include "dialog.h"
30: #include "file.h"
31: #include "int.h"
32: #include "memAlloc.h"
33: #include "memorySnapShot.h"
34: #include "misc.h"
35: #include "psg.h"
36: #include "sound.h"
37: #include "video.h"
38: #include "wavFormat.h"
39: #include "ymFormat.h"
40:
1.1.1.5 root 41: #define LONGLONG Uint64
1.1 root 42:
43: #define ENVELOPE_PERIOD(Fine,Coarse) (((unsigned long)Coarse)<<8) + (unsigned long)Fine
1.1.1.5 root 44: #define NOISE_PERIOD(Freq) ((((unsigned long)Freq)&0x1f)<<11)
45: #define TONE_PERIOD(Fine,Coarse) ((((unsigned long)Coarse)&0x0f)<<8) + (unsigned long)Fine
46: #define MIXTABLE_SIZE (256*8) /* Large table, so don't overflow */
47: #define TONEFREQ_SHIFT 28 /* 4.28 fixed point */
48: #define NOISEFREQ_SHIFT 28 /* 4.28 fixed point */
49: #define ENVFREQ_SHIFT 16 /* 16.16 fixed */
1.1 root 50:
1.1.1.6 root 51: #define SAMPLES_BUFFER_SIZE 1024
52: /* Number of generated samples per frame (eg. 44Khz=882) : */
53: #define SAMPLES_PER_FRAME ((SoundPlayBackFrequencies[OutputAudioFreqIndex]+35)/nScreenRefreshRate)
54: /* Frequency of generated samples: */
55: #define SAMPLES_FREQ (SoundPlayBackFrequencies[OutputAudioFreqIndex])
56: #define YM_FREQ (2000000/SAMPLES_FREQ) /* YM Frequency 2Mhz */
57:
58:
1.1 root 59: /* Original wave samples */
1.1.1.7 ! root 60: static int EnvelopeShapeValues[16*1024]; /* Shape x Length(repeat 3rd/4th entries) */
1.1 root 61: /* Frequency and time period samples */
1.1.1.7 ! root 62: static unsigned long ChannelFreq[3], EnvelopeFreq, NoiseFreq; /* Current frequency of each channel A,B,C,Envelope and Noise */
! 63: static int ChannelAmpDecayTime[3]; /* Store counter to show if amplitude is changed to generate 'samples' */
! 64: static int Envelope[SAMPLES_BUFFER_SIZE],Noise[SAMPLES_BUFFER_SIZE]; /* Current sample for this time period */
1.1 root 65: /* Output channel data */
1.1.1.7 ! root 66: static int Channel_A_Buffer[SAMPLES_BUFFER_SIZE],Channel_B_Buffer[SAMPLES_BUFFER_SIZE],Channel_C_Buffer[SAMPLES_BUFFER_SIZE];
1.1.1.5 root 67: /* Use table to convert from (A+B+C) to clipped 'unsigned char' for sound buffer */
1.1.1.7 ! root 68: static char MixTable[MIXTABLE_SIZE]; /* -ve and +ve range */
! 69: static char *pMixTable = &MixTable[MIXTABLE_SIZE/2]; /* Signed index into above */
! 70: static int ActiveSndBufIdx; /* Current working index into above mix buffer */
! 71: static int nSamplesToGenerate; /* How many samples are needed for this time-frame */
! 72:
! 73: /* global values */
1.1 root 74: BOOL bWriteEnvelopeFreq; /* Did write to register '13' - causes frequency reset */
1.1.1.5 root 75: BOOL bWriteChannelAAmp, bWriteChannelBAmp, bWriteChannelCAmp; /* Did write to amplitude registers? */
1.1 root 76: BOOL bEnvelopeFreqFlag; /* As above, but cleared each frame for YM saving */
1.1.1.2 root 77: /* Buffer to store circular samples */
1.1 root 78: char MixBuffer[MIXBUFFER_SIZE];
1.1.1.5 root 79: int nGeneratedSamples; /* Generated samples since audio buffer update */
1.1.1.7 ! root 80: int SoundCycles;
1.1.1.3 root 81:
1.1 root 82:
1.1.1.2 root 83: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root 84: /* Envelope shape table */
! 85: typedef struct
! 86: {
! 87: int WaveStart[4], WaveDelta[4];
! 88: } ENVSHAPE;
! 89:
1.1.1.2 root 90: /* Envelope shapes */
1.1.1.7 ! root 91: static ENVSHAPE EnvShapes[16] =
1.1.1.5 root 92: {
1.1.1.4 root 93: { {127,-128,-128,-128}, {-1, 0, 0, 0} }, /* \_____ 00xx */
94: { {127,-128,-128,-128}, {-1, 0, 0, 0} }, /* \_____ 00xx */
95: { {127,-128,-128,-128}, {-1, 0, 0, 0} }, /* \_____ 00xx */
96: { {127,-128,-128,-128}, {-1, 0, 0, 0} }, /* \_____ 00xx */
97: { {-128,-128,-128,-128}, {1, 0, 0, 0} }, /* /_____ 01xx */
98: { {-128,-128,-128,-128}, {1, 0, 0, 0} }, /* /_____ 01xx */
99: { {-128,-128,-128,-128}, {1, 0, 0, 0} }, /* /_____ 01xx */
100: { {-128,-128,-128,-128}, {1, 0, 0, 0} }, /* /_____ 01xx */
101: { {127,127,127,127}, {-1,-1,-1,-1} }, /* \\\\\\ 1000 */
102: { {127,-128,-128,-128}, {-1, 0, 0, 0} }, /* \_____ 1001 */
103: { {127,-128,127,-128}, {-1, 1,-1, 1} }, /* \/\/\/ 1010 */
104: { {127,127,127,127}, {-1, 0, 0, 0} }, /* \~~~~~ 1011 */
105: { {-128,-128,-128,-128}, {1, 1, 1, 1} }, /* ////// 1100 */
106: { {-128,127,127,127}, {1, 0, 0, 0} }, /* /~~~~~ 1101 */
107: { {-128,127,-128,127}, {1,-1, 1,-1} }, /* /\/\/\ 1110 */
108: { {-128,-128,-128,-128}, {1, 0, 0, 0} } /* /_____ 1111 */
1.1 root 109: };
110:
111: /* Square wave look up table */
1.1.1.7 ! root 112: static int SquareWave[16] = { 127,127,127,127,127,127,127,127, -128,-128,-128,-128,-128,-128,-128,-128 };
1.1 root 113: /* LogTable */
1.1.1.7 ! root 114: static int LogTable[256];
! 115: static int LogTable16[16];
! 116: static int *pEnvelopeLogTable = &LogTable[128];
1.1 root 117:
1.1.1.5 root 118:
1.1.1.2 root 119: /*-----------------------------------------------------------------------*/
1.1 root 120: /*
121: Create Log tables
122: */
1.1.1.7 ! root 123: static void Sound_CreateLogTables(void)
1.1 root 124: {
125: float a;
126: int i;
127:
128: /* Generate 'log' table for envelope output. It isn't quite a 'log' but it mimicks the ST */
1.1.1.2 root 129: /* output very well */
1.1 root 130: a = 1.0f;
1.1.1.5 root 131: for(i=0; i<256; i++)
132: {
1.1 root 133: LogTable[255-i] = (int)(255*a);
134: a /= 1.02f;
135: }
136: LogTable[0] = 0;
137:
138: /* And a 16 entry version(thanks to Nick for the '/= 1.5' bit) */
139: /* This is VERY important for clear sample playback */
140: a = 1.0f;
1.1.1.5 root 141: for(i=0; i<15; i++)
142: {
1.1 root 143: LogTable16[15-i] = (int)(255*a);
144: a /= 1.5f;
145: }
146: LogTable16[0] = 0;
147: }
148:
1.1.1.5 root 149:
1.1.1.2 root 150: /*-----------------------------------------------------------------------*/
1.1 root 151: /*
152: Create envelope shape, store to table
153: ( Wave is stored as 4 cycles, where cycles 1,2 are start and 3,4 are looped )
154: */
1.1.1.7 ! root 155: static void Sound_CreateEnvelopeShape(ENVSHAPE *pEnvShape,int *pEnvelopeValues)
1.1 root 156: {
157: int i,j,Value;
158:
1.1.1.2 root 159: /* Create shape */
1.1.1.5 root 160: for(i=0; i<4; i++)
161: {
1.1.1.2 root 162: Value = pEnvShape->WaveStart[i]; /* Set starting value for gradient */
1.1 root 163: for(j=0; j<256; j++,Value+=pEnvShape->WaveDelta[i])
164: *pEnvelopeValues++ = Misc_LimitInt(Value,-128,127);
165: }
166: }
167:
1.1.1.5 root 168:
1.1.1.2 root 169: /*-----------------------------------------------------------------------*/
1.1 root 170: /*
171: Create YM2149 envelope shapes(x16)
172: */
1.1.1.7 ! root 173: static void Sound_CreateEnvelopeShapes(void)
1.1 root 174: {
175: int i;
176:
1.1.1.2 root 177: /* Create 'envelopes' for YM table */
1.1 root 178: for(i=0; i<16; i++)
179: Sound_CreateEnvelopeShape(&EnvShapes[i],&EnvelopeShapeValues[i*1024]);
180: }
181:
1.1.1.5 root 182:
1.1.1.2 root 183: /*-----------------------------------------------------------------------*/
1.1 root 184: /*
185: Create table to clip samples top 8-bit range
1.1.1.7 ! root 186: This keeps then 'signed', although many sound cards want 'unsigned' values,
1.1.1.5 root 187: but we keep them signed so we can vary the volume easily.
1.1 root 188: */
1.1.1.7 ! root 189: static void Sound_CreateSoundMixClipTable(void)
1.1 root 190: {
191: int i,v;
192:
1.1.1.2 root 193: /* Create table to 'clip' values to -128...127 */
1.1.1.5 root 194: for(i=0; i<MIXTABLE_SIZE; i++)
195: {
1.1.1.2 root 196: v = (float)(i-(MIXTABLE_SIZE/2)) * 0.3f; /* Scale, to prevent clipping */
197: if (v<-128) v = -128; /* Limit -128..128 */
1.1 root 198: if (v>127) v = 127;
199: MixTable[i] = v;
200: }
201: }
202:
203:
1.1.1.2 root 204: /*-----------------------------------------------------------------------*/
1.1 root 205: /*
1.1.1.7 ! root 206: Init sound tables and envelopes
! 207: */
! 208: void Sound_Init(void)
! 209: {
! 210: Sound_CreateLogTables();
! 211: Sound_CreateEnvelopeShapes();
! 212: Sound_CreateSoundMixClipTable();
! 213:
! 214: Sound_Reset();
! 215: }
! 216:
! 217:
! 218: /*-----------------------------------------------------------------------*/
! 219: /*
! 220: Reset the sound emulation
! 221: */
! 222: void Sound_Reset(void)
! 223: {
! 224: int i;
! 225:
! 226: Sound_ClearMixBuffer(); /* Clear buffer */
! 227:
! 228: /* Clear cycle counts, buffer index and register '13' flags */
! 229: SoundCycles = 0;
! 230: bEnvelopeFreqFlag = FALSE;
! 231: bWriteEnvelopeFreq = FALSE;
! 232: bWriteChannelAAmp = bWriteChannelBAmp = bWriteChannelCAmp = FALSE;
! 233:
! 234: /* Lock audio system before accessing variables that are also use by the callback function! */
! 235: Audio_Lock();
! 236: CompleteSndBufIdx = 0;
! 237: ActiveSndBufIdx = (SoundBufferSize + SAMPLES_PER_FRAME) % MIXBUFFER_SIZE;
! 238: nGeneratedSamples = 0;
! 239: Audio_Unlock();
! 240:
! 241: /* Clear frequency counter */
! 242: for(i=0; i<3; i++)
! 243: {
! 244: ChannelFreq[i] =
! 245: ChannelAmpDecayTime[i] = 0;
! 246: }
! 247: EnvelopeFreq = NoiseFreq = 0;
! 248: }
! 249:
! 250:
! 251: /*-----------------------------------------------------------------------*/
! 252: /*
! 253: Clear mixer buffer, where samples are stored ready to pass to sound player
! 254: */
! 255: void Sound_ClearMixBuffer(void)
! 256: {
! 257: Audio_Lock();
! 258:
! 259: Memory_Clear(MixBuffer, MIXBUFFER_SIZE); /* Clear buffer */
! 260:
! 261: Audio_Unlock();
! 262: }
! 263:
! 264:
! 265: /*-----------------------------------------------------------------------*/
! 266: /*
! 267: Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
! 268: */
! 269: void Sound_MemorySnapShot_Capture(BOOL bSave)
! 270: {
! 271: /* Save/Restore details */
! 272: MemorySnapShot_Store(ChannelFreq,sizeof(ChannelFreq));
! 273: MemorySnapShot_Store(&EnvelopeFreq,sizeof(EnvelopeFreq));
! 274: MemorySnapShot_Store(&NoiseFreq,sizeof(NoiseFreq));
! 275: }
! 276:
! 277:
! 278: /*-----------------------------------------------------------------------*/
! 279: /*
1.1 root 280: Find how many samples to generate and store in 'nSamplesToGenerate'
281: Also update 'SoundCycles' to store how many we actually did so generates set amount each frame
282: */
1.1.1.7 ! root 283: static void Sound_SetSamplesPassed(void)
1.1 root 284: {
285: int nSampleCycles;
1.1.1.7 ! root 286: int nSamplesPerFrame;
1.1 root 287: int Dec=1;
288:
1.1.1.2 root 289: /* Check how many cycles have passed, as we use this to help find out if we are playing sample data */
1.1 root 290:
1.1.1.2 root 291: /* First, add decay to channel amplitude variables */
1.1 root 292: if (SoundCycles>(CYCLES_PER_FRAME/4))
1.1.1.2 root 293: Dec = 16; /* Been long time between sound writes, must be normal tone sound */
1.1 root 294:
1.1.1.5 root 295: if (!bWriteChannelAAmp) /* Not written to amplitude, decay value */
296: {
1.1 root 297: ChannelAmpDecayTime[0]-=Dec;
298: if (ChannelAmpDecayTime[0]<0) ChannelAmpDecayTime[0] = 0;
299: }
1.1.1.5 root 300: if (!bWriteChannelBAmp)
301: {
1.1 root 302: ChannelAmpDecayTime[1]-=Dec;
303: if (ChannelAmpDecayTime[1]<0) ChannelAmpDecayTime[1] = 0;
304: }
1.1.1.5 root 305: if (!bWriteChannelCAmp)
306: {
1.1 root 307: ChannelAmpDecayTime[2]-=Dec;
308: if (ChannelAmpDecayTime[2]<0) ChannelAmpDecayTime[2] = 0;
309: }
310:
1.1.1.2 root 311: /* 160256 cycles per VBL, 44Khz = 882 samples per VBL */
312: /* 882/160256 samples per clock cycle */
1.1.1.7 ! root 313: nSamplesPerFrame = SAMPLES_PER_FRAME;
! 314: #if 0 /* Use floats for calculation */
! 315: nSamplesToGenerate = (int)( (float)SoundCycles * ((float)nSamplesPerFrame/(float)CYCLES_PER_FRAME) );
! 316: if (nSamplesToGenerate > nSamplesPerFrame)
! 317: nSamplesToGenerate = nSamplesPerFrame;
1.1 root 318:
1.1.1.7 ! root 319: nSampleCycles = (int)( (float)nSamplesToGenerate / ((float)nSamplesPerFrame/(float)CYCLES_PER_FRAME) );
1.1 root 320: SoundCycles -= nSampleCycles;
1.1.1.7 ! root 321: #else /* Use integers for calculation - both of these calculations should fit into 32-bit int */
! 322: nSamplesToGenerate = SoundCycles * nSamplesPerFrame / CYCLES_PER_FRAME;
! 323: if (nSamplesToGenerate > nSamplesPerFrame)
! 324: nSamplesToGenerate = nSamplesPerFrame;
! 325:
! 326: nSampleCycles = nSamplesToGenerate * CYCLES_PER_FRAME / nSamplesPerFrame;
! 327: SoundCycles -= nSampleCycles;
! 328: #endif
1.1 root 329: }
330:
1.1.1.5 root 331:
1.1.1.2 root 332: /*-----------------------------------------------------------------------*/
1.1 root 333: /*
334: Generate envelope wave for this time-frame
335: */
1.1.1.7 ! root 336: static void Sound_GenerateEnvelope(unsigned char EnvShape, unsigned char Fine, unsigned char Coarse)
1.1 root 337: {
338: int *pEnvelopeValues;
339: unsigned long EnvelopePeriod,EnvelopeFreqDelta;
340: int i;
341:
1.1.1.2 root 342: /* Find envelope details */
1.1 root 343: if (bWriteEnvelopeFreq)
344: EnvelopeFreq = 0;
1.1.1.2 root 345: pEnvelopeValues = &EnvelopeShapeValues[ (EnvShape&0x0f)*1024 ]; /* Envelope shape values */
1.1 root 346: EnvelopePeriod = ENVELOPE_PERIOD((unsigned long)Fine,(unsigned long)Coarse);
347:
1.1.1.2 root 348: if (EnvelopePeriod==0) /* Handle div by zero */
1.1 root 349: EnvelopeFreqDelta = 0;
350: else
1.1.1.2 root 351: EnvelopeFreqDelta = ((LONGLONG)YM_FREQ<<ENVFREQ_SHIFT) / (EnvelopePeriod); /* 16.16 fixed point */
1.1 root 352:
1.1.1.2 root 353: /* Create envelope from current shape and frequency */
1.1.1.5 root 354: for(i=0; i<nSamplesToGenerate; i++)
355: {
1.1.1.2 root 356: Envelope[i] = pEnvelopeValues[EnvelopeFreq>>ENVFREQ_SHIFT]; /* Store envelope wave, already applied 'log' function */
1.1 root 357: EnvelopeFreq += EnvelopeFreqDelta;
358: if (EnvelopeFreq&0xfe000000)
1.1.1.2 root 359: EnvelopeFreq = 0x02000000 | (EnvelopeFreq&0x01ffffff); /* Keep in range 512-1024 once past 511! */
1.1 root 360: }
361: }
362:
1.1.1.5 root 363:
1.1.1.2 root 364: /*-----------------------------------------------------------------------*/
1.1 root 365: /*
366: Generate nosie for this time-frame
367: */
1.1.1.7 ! root 368: static void Sound_GenerateNoise(unsigned char MixerControl, unsigned char NoiseGen)
1.1 root 369: {
370: int NoiseValue;
371: unsigned long NoisePeriod,NoiseFreqDelta;
372: int i;
373:
374: NoisePeriod = NOISE_PERIOD((unsigned long)NoiseGen);
375:
1.1.1.2 root 376: if (NoisePeriod==0) /* Handle div by zero */
1.1 root 377: NoiseFreqDelta = 0;
378: else
1.1.1.2 root 379: NoiseFreqDelta = (((LONGLONG)YM_FREQ)<<NOISEFREQ_SHIFT) / NoisePeriod; /* 4.28 fixed point */
1.1 root 380:
1.1.1.2 root 381: /* Generate noise samples */
1.1.1.5 root 382: for(i=0; i<nSamplesToGenerate; i++)
383: {
1.1.1.2 root 384: NoiseValue = (unsigned int)Misc_GetRandom()%96; /* Get random value */
385: if (SquareWave[NoiseFreq>>NOISEFREQ_SHIFT]<=0) /* Add to square wave at given frequency */
1.1 root 386: NoiseValue = -NoiseValue;
387:
388: Noise[i] = NoiseValue;
389: NoiseFreq += NoiseFreqDelta;
390: }
391: }
392:
1.1.1.5 root 393:
1.1.1.2 root 394: /*-----------------------------------------------------------------------*/
1.1 root 395: /*
396: Generate channel of samples for this time-frame
397: */
1.1.1.7 ! root 398: static void Sound_GenerateChannel(int *pBuffer, unsigned char ToneFine, unsigned char ToneCoarse,unsigned char Amplitude,unsigned char MixerControl,unsigned long *pChannelFreq,int MixMask)
1.1 root 399: {
400: int *pNoise = Noise, *pEnvelope = Envelope;
401: unsigned long ToneFreq=*pChannelFreq;
402: unsigned long TonePeriod;
403: unsigned long ToneFreqDelta;
404: int i,Amp,Mix;
405: int ToneOutput,NoiseOutput,MixerOutput,EnvelopeOutput,AmplitudeOutput;
406:
407: TonePeriod = TONE_PERIOD((unsigned long)ToneFine,(unsigned long)ToneCoarse);
1.1.1.2 root 408: /* Find frequency of channel */
1.1 root 409: if (TonePeriod==0)
1.1.1.2 root 410: ToneFreqDelta = 0; /* Handle div by zero */
1.1 root 411: else
1.1.1.2 root 412: ToneFreqDelta = (((LONGLONG)YM_FREQ)<<TONEFREQ_SHIFT) / TonePeriod; /* 4.28 fixed point */
1.1 root 413: Amp = LogTable16[(Amplitude&0x0f)];
1.1.1.2 root 414: Mix = (MixerControl>>MixMask)&9; /* Read I/O Mixer */
1.1 root 415:
1.1.1.2 root 416: /* Check if we are trying to play a 'sample' - we need to up the volume on these as they tend to be rather quiet */
1.1.1.5 root 417: if ((Amplitude&0x10)==0) /* Fixed level amplitude? */
418: {
1.1.1.2 root 419: ChannelAmpDecayTime[MixMask]++; /* Increment counter to find out if we are playing samples... */
1.1 root 420: if (ChannelAmpDecayTime[MixMask]>16)
1.1.1.2 root 421: ChannelAmpDecayTime[MixMask] = 16; /* And limit */
1.1 root 422: }
423:
1.1.1.5 root 424: for(i=0; i<nSamplesToGenerate; i++)
425: {
1.1.1.2 root 426: /* Output from Tone Generator(0-255) */
1.1 root 427: ToneOutput = SquareWave[ToneFreq>>TONEFREQ_SHIFT];
428:
1.1.1.2 root 429: /* Output from Noise Generator(0-255) */
1.1 root 430: NoiseOutput = *pNoise++;
1.1.1.2 root 431: /* Output from Mixer(combines Tone+Noise) */
1.1 root 432: switch (Mix) {
1.1.1.2 root 433: case 0: /* Has Noise and Tone */
1.1 root 434: MixerOutput = NoiseOutput+ToneOutput;
435: break;
1.1.1.2 root 436: case 1: /* Has Noise */
1.1 root 437: MixerOutput = NoiseOutput;
438: break;
1.1.1.2 root 439: case 8: /* Has Tone */
1.1 root 440: MixerOutput = ToneOutput;
441: break;
442:
1.1.1.2 root 443: default: /* This is used to emulate samples - should give no output, but ST gives set tone!!?? */
444: /* MixerControl gets set to give a continuous tone and then then Amplitude */
445: /* of channels A,B and C get changed with all other registers in the PSG */
446: /* staying as zero's. This produces the sounds from Quartet, Speech, NoiseTracker etc...! */
1.1 root 447: MixerOutput = 127;
448: }
449:
450: EnvelopeOutput = pEnvelopeLogTable[*pEnvelope++];
451:
1.1.1.5 root 452: if ((Amplitude&0x10)==0)
453: {
1.1.1.2 root 454: AmplitudeOutput = Amp; /* Fixed level amplitude */
1.1 root 455:
1.1.1.2 root 456: /* As with most emulators, sample playback is always 'quiet'. We check to see if */
457: /* the amplitude of a channel is repeatedly changing and when this is detected we */
458: /* scale the volume accordingly */
1.1 root 459: if (ChannelAmpDecayTime[MixMask]>8)
1.1.1.2 root 460: AmplitudeOutput <<= 1; /* Scale up by a factor of 2 */
1.1 root 461: }
462: else
463: AmplitudeOutput = EnvelopeOutput;
464:
465: *pBuffer++ = (MixerOutput*AmplitudeOutput)>>8;
466:
467: ToneFreq+=ToneFreqDelta;
468: }
469:
1.1.1.2 root 470: /* Store back incremented frequency, for next call */
1.1 root 471: *pChannelFreq = ToneFreq;
472: }
473:
1.1.1.5 root 474:
1.1.1.2 root 475: /*-----------------------------------------------------------------------*/
1.1 root 476: /*
477: Generate samples for all channels during this time-frame
478: */
1.1.1.5 root 479: static void Sound_GenerateSamples(void)
1.1 root 480: {
481: int *pChannelA=Channel_A_Buffer, *pChannelB=Channel_B_Buffer, *pChannelC=Channel_C_Buffer;
482: int i;
483:
1.1.1.2 root 484: /* Anything to do? */
1.1.1.5 root 485: if (nSamplesToGenerate>0)
486: {
1.1.1.2 root 487: /* Generate envelope/noise samples for this time */
1.1 root 488: Sound_GenerateEnvelope(PSGRegisters[PSG_REG_ENV_SHAPE],PSGRegisters[PSG_REG_ENV_FINE],PSGRegisters[PSG_REG_ENV_COARSE]);
489: Sound_GenerateNoise(PSGRegisters[PSG_REG_MIXER_CONTROL],PSGRegisters[PSG_REG_NOISE_GENERATOR]);
490:
1.1.1.2 root 491: /* Generate 3 channels, store to separate buffer so can mix/clip */
1.1 root 492: Sound_GenerateChannel(pChannelA,PSGRegisters[PSG_REG_CHANNEL_A_FINE],PSGRegisters[PSG_REG_CHANNEL_A_COARSE],PSGRegisters[PSG_REG_CHANNEL_A_AMP],PSGRegisters[PSG_REG_MIXER_CONTROL],&ChannelFreq[0],0);
493: Sound_GenerateChannel(pChannelB,PSGRegisters[PSG_REG_CHANNEL_B_FINE],PSGRegisters[PSG_REG_CHANNEL_B_COARSE],PSGRegisters[PSG_REG_CHANNEL_B_AMP],PSGRegisters[PSG_REG_MIXER_CONTROL],&ChannelFreq[1],1);
494: Sound_GenerateChannel(pChannelC,PSGRegisters[PSG_REG_CHANNEL_C_FINE],PSGRegisters[PSG_REG_CHANNEL_C_COARSE],PSGRegisters[PSG_REG_CHANNEL_C_AMP],PSGRegisters[PSG_REG_MIXER_CONTROL],&ChannelFreq[2],2);
495:
1.1.1.2 root 496: /* Mix channels together, using table to clip and also convert to 'unsigned char' */
1.1 root 497: for(i=0; i<nSamplesToGenerate; i++)
1.1.1.5 root 498: MixBuffer[(i+ActiveSndBufIdx)%MIXBUFFER_SIZE] = pMixTable[(*pChannelA++) + (*pChannelB++) + (*pChannelC++)];
499:
500: ActiveSndBufIdx = (ActiveSndBufIdx + nSamplesToGenerate) % MIXBUFFER_SIZE;
501: nGeneratedSamples += nSamplesToGenerate;
502:
1.1.1.2 root 503: /* Reset the write to register '13' flag */
1.1 root 504: bWriteEnvelopeFreq = FALSE;
1.1.1.2 root 505: /* And amplitude write flags */
1.1 root 506: bWriteChannelAAmp = bWriteChannelBAmp = bWriteChannelCAmp = FALSE;
507: }
508: }
509:
1.1.1.5 root 510:
1.1.1.2 root 511: /*-----------------------------------------------------------------------*/
1.1 root 512: /*
1.1.1.5 root 513: This is called to built samples up until this clock cycle
1.1 root 514: */
1.1.1.5 root 515: void Sound_Update(void)
1.1 root 516: {
1.1.1.5 root 517: int OldSndBufIdx = ActiveSndBufIdx;
518:
1.1.1.6 root 519: /* Make sure that we don't interfere with the audio callback function */
520: Audio_Lock();
521:
1.1.1.5 root 522: /* Find how many to generate */
1.1 root 523: Sound_SetSamplesPassed();
1.1.1.2 root 524: /* And generate */
1.1 root 525: Sound_GenerateSamples();
526:
1.1.1.6 root 527: /* Allow audio callback function to occur again */
528: Audio_Unlock();
529:
1.1.1.2 root 530: /* Save to WAV file, if open */
1.1.1.5 root 531: WAVFormat_Update(MixBuffer, OldSndBufIdx, nSamplesToGenerate);
1.1 root 532: }
533:
1.1.1.5 root 534:
1.1.1.2 root 535: /*-----------------------------------------------------------------------*/
1.1 root 536: /*
1.1.1.5 root 537: On each VBL (50fps) complete samples.
1.1 root 538: */
1.1.1.5 root 539: void Sound_Update_VBL(void)
1.1 root 540: {
1.1.1.5 root 541: Sound_Update();
542:
543: /* Clear write to register '13', used for YM file saving */
544: bEnvelopeFreqFlag = FALSE;
1.1 root 545: }
546:
547:
1.1.1.2 root 548: /*-----------------------------------------------------------------------*/
1.1 root 549: /*
1.1.1.6 root 550: This is called from the audio callback function to create enough samples
551: to fill the current sound buffer.
552: */
553: void Sound_UpdateFromAudioCallBack(void)
554: {
555: /* If there are already enough samples or if we are recording, we should
556: * not generate more samples here! */
557: if(nGeneratedSamples >= SoundBufferSize || Sound_AreWeRecording())
558: return;
559:
560: nSamplesToGenerate = SoundBufferSize - nGeneratedSamples;
561:
562: Sound_GenerateSamples();
563: }
564:
565:
566: /*-----------------------------------------------------------------------*/
567: /*
1.1 root 568: Start recording sound, as .YM or .WAV output
569: */
570: BOOL Sound_BeginRecording(char *pszCaptureFileName)
571: {
572: BOOL bRet;
573:
1.1.1.7 ! root 574: if (!pszCaptureFileName || strlen(pszCaptureFileName) <= 3)
! 575: {
! 576: fprintf(stderr, "Illegal sound recording file name!\n");
! 577: return FALSE;
! 578: }
! 579:
1.1.1.2 root 580: /* Did specify .YM or .WAV? If neither report error */
1.1.1.7 ! root 581: if (File_DoesFileExtensionMatch(pszCaptureFileName,".ym"))
1.1.1.4 root 582: bRet = YMFormat_BeginRecording(pszCaptureFileName);
1.1 root 583: else if (File_DoesFileExtensionMatch(pszCaptureFileName,".wav"))
1.1.1.4 root 584: bRet = WAVFormat_OpenFile(pszCaptureFileName);
1.1.1.5 root 585: else
586: {
1.1 root 587: Main_Message("Unknown Sound Recording format\n\n.Please specify a .YM or .WAV output file.",PROG_NAME /*,MB_OK|MB_ICONSTOP*/);
588: bRet = FALSE;
589: }
590:
591: return(bRet);
592: }
593:
1.1.1.5 root 594:
1.1.1.2 root 595: /*-----------------------------------------------------------------------*/
1.1 root 596: /*
597: End sound recording
598: */
1.1.1.7 ! root 599: void Sound_EndRecording(void)
1.1 root 600: {
1.1.1.2 root 601: /* Stop sound recording and close files */
1.1 root 602: if (bRecordingYM)
1.1.1.4 root 603: YMFormat_EndRecording();
1.1 root 604: if (bRecordingWav)
1.1.1.4 root 605: WAVFormat_CloseFile();
1.1 root 606: }
607:
1.1.1.6 root 608:
1.1.1.2 root 609: /*-----------------------------------------------------------------------*/
1.1 root 610: /*
611: Are we recording sound data?
612: */
613: BOOL Sound_AreWeRecording(void)
614: {
615: return(bRecordingYM || bRecordingWav);
616: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.