Annotation of hatari/src/sound.c, revision 1.1.1.4

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] = {
1.1.1.4 ! root       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  */
1.1       root       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) )
1.1.1.4 ! root      508:     bRet = YMFormat_BeginRecording(pszCaptureFileName);
1.1       root      509:   else if (File_DoesFileExtensionMatch(pszCaptureFileName,".wav"))
1.1.1.4 ! root      510:     bRet = WAVFormat_OpenFile(pszCaptureFileName);
1.1       root      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: */
1.1.1.4 ! root      523: void Sound_EndRecording()
1.1       root      524: {
1.1.1.2   root      525:   /* Stop sound recording and close files */
1.1       root      526:   if (bRecordingYM)
1.1.1.4 ! root      527:     YMFormat_EndRecording();
1.1       root      528:   if (bRecordingWav)
1.1.1.4 ! root      529:     WAVFormat_CloseFile();
1.1       root      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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.