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