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