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