/****************************************************************************/ /* Beebem - (c) David Alan Gilbert 1994/1995 */ /* ----------------------------------------- */ /* This program may be distributed freely within the following restrictions:*/ /* */ /* 1) You may not charge for this program or for any part of it. */ /* 2) This copyright message must be distributed with all copies. */ /* 3) This program must be distributed complete with source code. Binary */ /* only distribution is not permitted. */ /* 4) The author offers no warrenties, or guarentees etc. - you use it at */ /* your own risk. If it messes something up or destroys your computer */ /* thats YOUR problem. */ /* 5) You may use small sections of code from this program in your own */ /* applications - but you must acknowledge its use. If you plan to use */ /* large sections then please ask the author. */ /* */ /* If you do not agree with any of the above then please do not use this */ /* program. */ /* Please report any problems to the author at gilbertd@cs.man.ac.uk */ /****************************************************************************/ // // Modified for DOS by MHG dec 97 // // 2 feb 97 MHG Square wave made to decay in PlayUpTil() // 27 aug 97 MHG Sound_RegWrite() calls SoundTrigger_Real() first not last // 28 Aug 97 MHG Cleaned up Sound_RegWrite() to not reset sound trigger timer // 28 Aug 97 MHG PlayUpTil() increases buffer storage if almost running slow // #define NOISE 1 #include #include "sound.h" #ifdef SOUNDSUPPORT #include #include #include #include //#include //#include #include "6502core.h" #include "port.h" #include //#include //#include /*#define DEBUGSOUND*/ /*#define DEBUGSOUNDTOFILE*/ extern "C" SetSoundBuffer(char *buff,int len,int rlen); //#define PREFSAMPLERATE 40000 #define PREFSAMPLERATE RATE //#define BUFSIZE 256 #define BUFSIZE 6*256 //MHG static unsigned char Buffer[BUFSIZE]; //#define AUTOSOUNDTIME ((2000000*BUFSIZE)/PREFSAMPLERATE) #define AUTOSOUNDTIME ((2000000*256)/PREFSAMPLERATE) /* Number of places to shift the volume */ #define VOLMAG 3 struct { unsigned int ToneFreq[4]; unsigned int ChangeSamps[4]; /* How often this channel should flip its otuput */ unsigned int ToneVolume[4]; /* In units of /dev/dsp */ struct { unsigned int FB:1; /* =0 for periodic, =1 for white */ unsigned int Freq:2; /* 0=low, 1=medium, 2=high, 3=tone gen 1 freq */ unsigned int Vol:4; } Noise; int LastToneFreqSet; /* the tone generator last set - for writing the 2nd byte */ } BeebState76489; static int ActiveChannels; /* Those channels with non-0 voolume */ static int devfd; /* Audio device id */ static int samplerate=RATE; static double OurTime=0.0; /* Time in sample periods */ int SoundTrigger=0; /* Time to trigger a sound event */ static unsigned int GenIndex[4]; /* Used by the voice generators */ static int GenDy[4]; static int GenErr[4]; static int GenState[4]; static float GenFloat[4]= {0,0,0,0}; /****************************************************************************/ static int bufptr=0; /* DestTime is in samples */ extern "C" int GetSoundGap(void); static void PlayUpTil(double DestTime) { int tmptotal,channel,bufinc; double gap=DestTime-OurTime; int gap2=DestTime-OurTime+0.5; //cout << "PlayUpTil DestTime=" << DestTime << " OurTime=" << OurTime << " Gap=" << gap << " Gap2=" << gap2 << "\n" ; if (GetSoundGap()<512) { OurTime-=512-GetSoundGap(); } OurTime--; while (DestTime>OurTime) { /* if ((BeebState76489.ToneVolume[0]==0) && (BeebState76489.ToneVolume[1]==0) && (BeebState76489.ToneVolume[2]==0) && (BeebState76489.ToneVolume[3]==0) && 0) { OurTime=DestTime; } else */ { for(bufinc=0;(bufptr=BeebState76489.ChangeSamps[channel]) { GenIndex[channel]=0; GenState[channel]^=1; }; #endif }; /* Channel loop */ #if NOISE /* Now put in noise generator stuff */ if (BeebState76489.Noise.FB) { /* White noise */ tmptotal+=GenState[0]?BeebState76489.ToneVolume[0]:0; GenIndex[0]++; switch (BeebState76489.Noise.Freq) { case 0: /* Low */ if (GenIndex[0]>=(samplerate/10000)) { GenIndex[0]=0; GenState[0]=rand() & 1; }; break; case 1: /* Med */ if (GenIndex[0]>=(samplerate/5000)) { GenIndex[0]=0; GenState[0]=rand() & 1; }; break; case 2: /* High */ if (GenIndex[0]>=(samplerate/2500)) { GenIndex[0]=0; GenState[0]=rand() & 1; }; break; case 3: /* as channel 1 */ if (GenIndex[0]>=BeebState76489.ChangeSamps[1]) { GenIndex[0]=0; GenState[0]=rand() & 1; }; break; }; /* Freq type switch */ } else { /* Periodic */ tmptotal+=GenState[0]?BeebState76489.ToneVolume[0]:0; GenIndex[0]++; switch (BeebState76489.Noise.Freq) { case 0: /* Low */ if (GenState[0]) { if (GenIndex[0]>=(samplerate/125)) { GenIndex[0]=0; GenState[0]=0; }; } else { if (GenIndex[0]>=(samplerate/1250)) { GenIndex[0]=0; GenState[0]=1; }; }; break; case 1: /* Med */ if (GenState[0]) { if (GenIndex[0]>=(samplerate/250)) { GenIndex[0]=0; GenState[0]=0; }; } else { if (GenIndex[0]>=(samplerate/2500)) { GenIndex[0]=0; GenState[0]=1; }; }; break; case 2: /* High */ if (GenState[0]) { if (GenIndex[0]>=(samplerate/500)) { GenIndex[0]=0; GenState[0]=0; }; } else { if (GenIndex[0]>=(samplerate/5000)) { GenIndex[0]=0; GenState[0]=1; }; }; break; case 3: /* Tone gen 1 */ if (GenState[0]) { if (GenIndex[0]>=(BeebState76489.ChangeSamps[1]*16)) { GenIndex[0]=0; GenState[0]=0; }; } else { if (GenIndex[0]>=(BeebState76489.ChangeSamps[1])) { GenIndex[0]=0; GenState[0]=1; }; }; break; }; /* Freq type switch */ }; #endif tmptotal/=4; Buffer[bufptr]=tmptotal+128; //centre sample values }; /* buffer loop */ SetSoundBuffer((char *)Buffer,bufptr,bufptr); //send sound to // if (write(devfd,Buffer,bufptr)==-1) { write to sound buffer here // cerr << "Write on audio device failed\n"; // }; // cerr << "bufptr " << bufptr << "\n"; // fprintf(stderr,"PlayUpTil: After write: bufptr=%d OurTime=%f\n",bufptr,OurTime); OurTime+=bufinc; bufptr=0; }; /* If no volume */ }; /* While time */ }; /* PlayUpTil */ /****************************************************************************/ /* Convert time in cycles to time in samples */ static double CyclesToSamples(unsigned int beebtime) { static unsigned int LastBeebCycle=0; /* Last parameter to this function */ static double LastOurTime=0; /* Last result of this function */ double tmp; /* OK - beeb cycles are in 2MHz units, ours are in 1/samplerate */ /* This is all done incrementally - find the number of ticks since the last call in both domains. This does mean this should only be called once */ /* Extract number of cycles since last call */ if (beebtimesamplerate) { ChangeSamps=INT_MAX; /* Way to high frequency - shut off */ } else { if (freq>(samplerate/2)) { /* Hmm - a bit high - make it top out - change on every sample */ /* What we should be doing is moving to sine wave at 1/6 samplerate */ ChangeSamps=2; } else { ChangeSamps=(int)((((double)samplerate/(double)freq)/2.0)+0.5); }; }; /* freq<=samplerate */ }; /* Freqval!=0 */ BeebState76489.ChangeSamps[Channel]=ChangeSamps; GenErr[Channel]=samplerate; GenDy[Channel]=2*freq; }; /****************************************************************************/ void SoundTrigger_Real(void) { PlayTilNow(); SetTrigger(AUTOSOUNDTIME,SoundTrigger); }; /* SoundTrigger_Real */ void SoundTrigger_Real2(void) { PlayTilNow(); // SetTrigger(AUTOSOUNDTIME,SoundTrigger); }; /* SoundTrigger_Real */ /****************************************************************************/ /* Called from in main.cc */ void SoundInit() { /* I don't have any info on what this lot should be */ BeebState76489.LastToneFreqSet=0; BeebState76489.ToneVolume[0]=BeebState76489.ToneVolume[1]=BeebState76489.ToneVolume[2]=BeebState76489.ToneVolume[3]=0; BeebState76489.ToneFreq[0]=BeebState76489.ToneFreq[1]=BeebState76489.ToneFreq[2]=0x2ff; BeebState76489.Noise.FB=0; BeebState76489.Noise.Freq=0; // ClearTrigger(SoundTrigger); ActiveChannels=0; InitAudioDev(PREFSAMPLERATE); }; /* SoundInit */ /****************************************************************************/ /* Called in sysvia.cc when a write is made to the 76489 sound chip */ //double lastt; void Sound_RegWrite(int value) { #ifdef DEBUGSOUND cerr << "Sound_RegWrite - Value=" << value << "\n"; #endif //cout << "Sound reg write val=" << value << " Total=" << TotalCycles << " gap=" << TotalCycles-lastt << "\n"; SoundTrigger_Real2(); // lastt=TotalCycles; if (!(value & 0x80)) { unsigned val=BeebState76489.ToneFreq[BeebState76489.LastToneFreqSet] & 15; /* Its changing the top half of the frequency */ val |= (value & 0x3f)<<4; /* And update */ BeebState76489.ToneFreq[BeebState76489.LastToneFreqSet]=val; SetFreq(BeebState76489.LastToneFreqSet+1,BeebState76489.ToneFreq[BeebState76489.LastToneFreqSet]); #ifdef DEBUGSOUND cerr << "Sound_RegWrite: Freq of tone " << (BeebState76489.LastToneFreqSet+1) << " now " << val << "\n"; #endif } else { /* Another register */ switch ((value>>4) & 0x7) { case 0: /* Tone 3 freq */ BeebState76489.ToneFreq[2]=(BeebState76489.ToneFreq[2] & 0x2f0) | (value & 0xf); #ifdef DEBUGSOUND cerr << "Sound_RegWrite: Freq of tone 3 now " << BeebState76489.ToneFreq[2] << "\n"; #endif SetFreq(3,BeebState76489.ToneFreq[2]); BeebState76489.LastToneFreqSet=2; break; //--------------------------------------------------------------------------- case 1: /* Tone 3 vol */ if ((BeebState76489.ToneVolume[3]==0) && ((value &15)!=15)) ActiveChannels++; if ((BeebState76489.ToneVolume[3]!=0) && ((value &15)==15)) ActiveChannels--; BeebState76489.ToneVolume[3]=(15-(value & 15))<>2)&1; #ifdef DEBUGSOUND cerr << "Sound_RegWrite: Noise now " << (BeebState76489.Noise.FB?"Periodic":"White") << " with Frequency setting " << BeebState76489.Noise.Freq << "\n"; #endif break; case 7: /* Noise volume */ if ((BeebState76489.ToneVolume[0]==0) && ((value &15)!=15)) ActiveChannels++; if ((BeebState76489.ToneVolume[0]!=0) && ((value &15)==15)) ActiveChannels--; BeebState76489.ToneVolume[0]=(15-(value & 15))<