/* YAPE - Yet Another Plus/4 Emulator The program emulates the Commodore 264 family of 8 bit microcomputers This program is free software, you are welcome to distribute it, and/or modify it under certain conditions. For more information, read 'Copying'. (c) 2000, 2001, 2004 Attila Grósz */ #include "sound.h" #include "tedmem.h" #include #include #include #include static MEM *ted; static Sint32 Sound; static Sint32 MixingFreq; static Sint32 BufferLength; static Sint32 FragmentSize; static Sint32 Volume; static Sint32 Snd1Status; static Sint32 Snd2Status; static Sint32 SndNoiseStatus; static Sint32 DAStatus; static Uint16 Freq1; static Uint16 Freq2; static Sint32 NoiseCounter; static Sint32 Side1, Side2; static Sint32 WaveCounter1_1; static Sint32 WaveCounter1_2; static Sint32 WaveCounter2_1; static Sint32 WaveCounter2_2; static Sint32 WaveCounter3; static Sint32 WaveLen1_1; static Sint32 WaveLen1_2; static Sint32 WaveLen2_1; static Sint32 WaveLen2_2; static Sint8 noise[9][255]; // 0-8 static Sint32 write_position; static Sint32 play_position; static SDL_AudioSpec *audiohwspec; struct snd_frag { snd_frag *next; int valid; Uint8 *buf_data; }; static snd_frag *first_frag; static snd_frag *last_frag; // undef this to get unbuffered sound #define BUFFERED_SOUND // adds a new frag to the end of the list void add_new_frag(snd_frag *last_fragment, int fragsize) { snd_frag *frag; frag = new snd_frag; if (last_fragment) last_fragment->next = frag; else first_frag = last_frag = frag; frag->buf_data = new Uint8[BufferLength]; frag->next = NULL; write_position++; last_frag = frag; } // deletes first frag in the list along with its buffer, // returns the next valid frag void delete_frag(snd_frag *frag) { snd_frag *new_first_frag = NULL; if (frag) { if (frag->next == NULL) last_frag = NULL; delete [] (frag->buf_data); new_first_frag = frag->next; delete frag; play_position++; } first_frag = new_first_frag; } void fragment_done() { #ifdef BUFFERED_SOUND int lead_in_frags = (write_position - play_position); //fprintf( stderr, "Lead in frags: %i\n", lead_in_frags); //SDL_LockAudio(); while (lead_in_frags<4) { //fprintf( stderr, "Adding a frag.\n"); add_new_frag( last_frag, BufferLength); render_audio( BufferLength, last_frag->buf_data); /*for(int i=0; i<16; i++) fprintf(stderr,"%c",last_frag->buf_data[i]);*/ lead_in_frags++; } //SDL_UnlockAudio(); #endif } void audio_callback(void *userdata, Uint8 *stream, int len) { #ifndef BUFFERED_SOUND render_audio( len, stream); #else snd_frag *playthis = first_frag; if (playthis) { if (len>BufferLength) len=BufferLength; memcpy( stream, playthis->buf_data, len); //fprintf( stderr, "Playing/removing a frag in length %i.\n",len); delete_frag(playthis); } #endif } void init_audio(class MEM *p) { SDL_AudioSpec *desired, *obtained = NULL; int i; float j,k; MixingFreq = 44100;//44100 22050 11025 FragmentSize = MixingFreq / 50; BufferLength = 2*FragmentSize;//2*FragmentSize 2048 4096 in Windows version... desired =(SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec)); obtained=(SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec)); desired->freq = MixingFreq; desired->format = AUDIO_U8; desired->channels = 1; desired->samples = BufferLength;//FragmentSize; desired->callback = audio_callback; desired->userdata = NULL; desired->size = desired->channels * desired->samples * sizeof(Uint8); desired->silence = 0x80; Sound = 0; if (SDL_OpenAudio( desired, obtained)) { fprintf(stderr,"SDL_OpenAudio failed!\n"); return; } else { fprintf(stderr,"SDL_OpenAudio success!\n"); if ( obtained == NULL ) { fprintf(stderr, "Great! We have our desired audio format!\n"); audiohwspec = desired; free(obtained); } else { fprintf(stderr, "Oops! Failed to get desired audio format!\n"); audiohwspec = obtained; free(desired); } } MixingFreq = audiohwspec->freq; BufferLength = audiohwspec->samples; fprintf(stderr, "Obtained mixing frequency: %u\n",audiohwspec->freq); fprintf(stderr, "Obtained audio format: %04X\n",audiohwspec->format); fprintf(stderr, "Obtained channel number: %u\n",audiohwspec->channels); fprintf(stderr, "Obtained audio buffer size: %u\n",audiohwspec->size); fprintf(stderr, "Obtained sample buffer size: %u\n",audiohwspec->samples); fprintf(stderr, "Obtained silence value: %u\n",audiohwspec->silence); SDL_PauseAudio(0); Side1 = 0; Side2 = 0; WaveCounter1_1 = 0; WaveCounter1_2 = 0; WaveCounter2_1 = 0; WaveCounter2_2 = 0; WaveCounter3 = 0; NoiseCounter = 0; play_position = 0; write_position = 0; /* initialise im with 0xa8 */ int im = 1; for ( i=0; i<255; i++) { for (int j=0; j<9; j++) noise[j][i] = (im&1) * j * 64 / 9 ; im=(im<<1)+(1^((im>>7)&1)^((im>>5)&1)^((im>>4)&1)^((im>>1)&1)); } Sound = 1; //SDL_PauseAudio(1); ted = (MEM *) p; } inline void render_audio(unsigned int nrsamples, Uint8 *buffer) { Sint32 Val1_1, Val1_2, Val2_1, Val2_2; Uint32 result1,result2; Volume = ted->TEDVolume; Freq1 = ted->TEDfreq1; Freq2 = ted->TEDfreq2; Snd1Status = ted->TEDChannel1; Snd2Status = ted->TEDChannel2; SndNoiseStatus = ted->TEDNoise; DAStatus = ted->TEDDA; // Channel 1 -------------------------------------- if (Snd1Status) { Val1_1 = 0 + ( Volume *(64/9) ); Val1_2 = 0 - ( Volume *(64/9) ); } else Val1_1 = Val1_2 = 0; float freq1 = (float) (110860.45/(1024.0 - ((Freq1+1)&0x3FF ))*2); WaveLen1_1 = WaveLen1_2 = (Sint32) (16.0*MixingFreq / freq1); // Correct big frequency jumps... if ((ceil((float)MixingFreq / freq1)-((float)MixingFreq / freq1))<0.5f) WaveLen1_2++; // Channel 2 -------------------------------------- if (Snd2Status) { Val2_1 = 0 + ( Volume *(64/9) ); Val2_2 = 0 - ( Volume *(64/9) ); } else Val2_1 = Val2_2 = 0; float freq2 = (110860.45/(1024.0-((Freq2+1)&0x3FF ))*2); WaveLen2_1 = WaveLen2_2 = (Sint32) (16.0*MixingFreq / freq2); // Correct big frequency jumps... if ((ceil((float)MixingFreq / freq2)-((float)MixingFreq / freq2))<0.5f) WaveLen2_2++; // Rendering... register int i; // Calculate the buffer... if (DAStatus) {// digi? for (i=0;i>=1; // smooth square WaveCounter1_1-=16; if (WaveCounter1_1<=0) { result1>>=1; // smooth square WaveCounter1_2 += WaveLen1_2; if (WaveCounter1_2) Side1 ^= 1; } } else { result1 = Val1_2; if (WaveCounter1_2 == WaveLen1_2) result1>>=1; // smooth square WaveCounter1_2-=16; if (WaveCounter1_2<=0) { result1>>=1; // smooth square WaveCounter1_1 += WaveLen1_1; if (WaveCounter1_1) Side1 ^= 1; } } // Channel 2 if (Snd2Status) { if (!Side2) { result2 = Val2_1; if (WaveCounter2_1 == WaveLen2_1) result2>>=1; // smooth square WaveCounter2_1-=16; if (WaveCounter2_1<=0) { result2>>=1; // smooth square WaveCounter2_2 += WaveLen2_2; if (WaveCounter2_2) Side2 ^= 1; } } else { result2 = Val2_2; if (WaveCounter2_2 == WaveLen2_2) result2>>=1; // smooth square WaveCounter2_2-=16; if (WaveCounter2_2<=0) { result2>>=1; // smooth square WaveCounter2_1 += WaveLen2_1; if (WaveCounter2_1) Side2 ^= 1; } } } else if (SndNoiseStatus) { WaveCounter2_2-=16; if (WaveCounter2_2<=0) { WaveCounter2_2 += WaveLen2_2; // Same freq as channel 2... if (NoiseCounter++==255) NoiseCounter=0; } result2 = noise[Volume][NoiseCounter]; } else result2 = 0; buffer[i] = (128 + result1 + result2); } } // for } void close_audio() { SDL_PauseAudio(1); SDL_Delay(60); SDL_CloseAudio(); if ( audiohwspec ) free( audiohwspec ); }