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