static void sound_ay_overlay(CAY8910 *_this) { int tone_level[3]; int mixer, envshape; int f, g, level, count; // libspectrum_signed_word *ptr; struct ay_change_tag *change_ptr = _this->ay_change; int changes_left = _this->ay_change_count; int reg, r; int is_low; int chan1, chan2, chan3; unsigned int tone_count, noise_count; libspectrum_dword sfreq, cpufreq; ///* If no AY chip, don't produce any AY sound (!) */ // if( !machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_AY ) // return; /* convert change times to sample offsets, use common denominator of 50 to avoid overflowing a dword */ sfreq = sound_generator_freq / HZ_COMMON_DENOMINATOR; // cpufreq = machine_current->timings.processor_speed / HZ_COMMON_DENOMINATOR; cpufreq = (libspectrum_dword) (m_fCurrentCLK_AY8910 / HZ_COMMON_DENOMINATOR); // [TC] for( f = 0; f < _this->ay_change_count; f++ ) _this->ay_change[f].ofs = (uint16_t) (( _this->ay_change[f].tstates * sfreq ) / cpufreq); // [TC] Added cast libspectrum_signed_word* pBuf1 = g_ppSoundBuffers[0]; libspectrum_signed_word* pBuf2 = g_ppSoundBuffers[1]; libspectrum_signed_word* pBuf3 = g_ppSoundBuffers[2]; // for( f = 0, ptr = sound_buf; f < sound_generator_framesiz; f++ ) { for( f = 0; f < sound_generator_framesiz; f++ ) { /* update ay registers. All this sub-frame change stuff * is pretty hairy, but how else would you handle the * samples in Robocop? :-) It also clears up some other * glitches. */ while( changes_left && f >= change_ptr->ofs ) { _this->sound_ay_registers[ reg = change_ptr->reg ] = change_ptr->val; change_ptr++; changes_left--; /* fix things as needed for some register changes */ switch ( reg ) { case 0: case 1: case 2: case 3: case 4: case 5: r = reg >> 1; /* a zero-len period is the same as 1 */ _this->ay_tone_period[r] = ( _this->sound_ay_registers[ reg & ~1 ] | ( _this->sound_ay_registers[ reg | 1 ] & 15 ) << 8 ); if( !_this->ay_tone_period[r] ) _this->ay_tone_period[r]++; /* important to get this right, otherwise e.g. Ghouls 'n' Ghosts * has really scratchy, horrible-sounding vibrato. */ if( _this->ay_tone_tick[r] >= _this->ay_tone_period[r] * 2 ) _this->ay_tone_tick[r] %= _this->ay_tone_period[r] * 2; break; case 6: _this->ay_noise_tick = 0; _this->ay_noise_period = ( _this->sound_ay_registers[ reg ] & 31 ); break; case 11: case 12: /* this one *isn't* fixed-point */ _this->ay_env_period = _this->sound_ay_registers[11] | ( _this->sound_ay_registers[12] << 8 ); break; case 13: _this->ay_env_internal_tick = _this->ay_env_tick = _this->ay_env_subcycles = 0; _this->env_first = 1; _this->env_rev = 0; _this->env_counter = ( _this->sound_ay_registers[13] & AY_ENV_ATTACK ) ? 0 : 15; break; } } /* the tone level if no enveloping is being used */ for( g = 0; g < 3; g++ ) tone_level[g] = ay_tone_levels[ _this->sound_ay_registers[ 8 + g ] & 15 ]; /* envelope */ envshape = _this->sound_ay_registers[13]; level = ay_tone_levels[ _this->env_counter ]; for( g = 0; g < 3; g++ ) if( _this->sound_ay_registers[ 8 + g ] & 16 ) tone_level[g] = level; /* envelope output counter gets incr'd every 16 AY cycles. * Has to be a while, as this is sub-output-sample res. */ _this->ay_env_subcycles += _this->ay_tick_incr; noise_count = 0; while( _this->ay_env_subcycles >= ( 16 << 16 ) ) { _this->ay_env_subcycles -= ( 16 << 16 ); noise_count++; _this->ay_env_tick++; while( _this->ay_env_tick >= _this->ay_env_period ) { _this->ay_env_tick -= _this->ay_env_period; /* do a 1/16th-of-period incr/decr if needed */ if( _this->env_first || ( ( envshape & AY_ENV_CONT ) && !( envshape & AY_ENV_HOLD ) ) ) { if( _this->env_rev ) _this->env_counter -= ( envshape & AY_ENV_ATTACK ) ? 1 : -1; else _this->env_counter += ( envshape & AY_ENV_ATTACK ) ? 1 : -1; if( _this->env_counter < 0 ) _this->env_counter = 0; if( _this->env_counter > 15 ) _this->env_counter = 15; } _this->ay_env_internal_tick++; while( _this->ay_env_internal_tick >= 16 ) { _this->ay_env_internal_tick -= 16; /* end of cycle */ if( !( envshape & AY_ENV_CONT ) ) _this->env_counter = 0; else { if( envshape & AY_ENV_HOLD ) { if( _this->env_first && ( envshape & AY_ENV_ALT ) ) _this->env_counter = ( _this->env_counter ? 0 : 15 ); } else { /* non-hold */ if( envshape & AY_ENV_ALT ) _this->env_rev = !_this->env_rev; else _this->env_counter = ( envshape & AY_ENV_ATTACK ) ? 0 : 15; } } _this->env_first = 0; } /* don't keep trying if period is zero */ if( !_this->ay_env_period ) break; } } /* generate tone+noise... or neither. * (if no tone/noise is selected, the chip just shoves the * level out unmodified. This is used by some sample-playing * stuff.) */ chan1 = tone_level[0]; chan2 = tone_level[1]; chan3 = tone_level[2]; mixer = _this->sound_ay_registers[7]; _this->ay_tone_subcycles += _this->ay_tick_incr; tone_count = _this->ay_tone_subcycles >> ( 3 + 16 ); _this->ay_tone_subcycles &= ( 8 << 16 ) - 1; if( ( mixer & 1 ) == 0 ) { level = chan1; AY_DO_TONE( chan1, 0 ); } if( ( mixer & 0x08 ) == 0 && _this->noise_toggle ) chan1 = 0; if( ( mixer & 2 ) == 0 ) { level = chan2; AY_DO_TONE( chan2, 1 ); } if( ( mixer & 0x10 ) == 0 && _this->noise_toggle ) chan2 = 0; if( ( mixer & 4 ) == 0 ) { level = chan3; AY_DO_TONE( chan3, 2 ); } if( ( mixer & 0x20 ) == 0 && _this->noise_toggle ) chan3 = 0; /* write the sample(s) */ *pBuf1++ = chan1; // [TC] *pBuf2++ = chan2; // [TC] *pBuf3++ = chan3; // [TC] #if 0 if( !sound_stereo ) { /* mono */ ( *ptr++ ) += chan1 + chan2 + chan3; } else { if( !sound_stereo_ay ) { /* stereo output, but mono AY sound; still, * incr separately in case of beeper pseudostereo. */ ( *ptr++ ) += chan1 + chan2 + chan3; ( *ptr++ ) += chan1 + chan2 + chan3; } else { /* stereo with ACB/ABC AY positioning. * Here we use real stereo positions for the channels. * Just because, y'know, it's cool and stuff. No, really. :-) * This is a little tricky, as it works by delaying sounds * on the left or right channels to model the delay you get * in the real world when sounds originate at different places. */ GEN_STEREO( rchan1pos, chan1 ); GEN_STEREO( rchan2pos, chan2 ); GEN_STEREO( rchan3pos, chan3 ); ( *ptr++ ) += rstereobuf_l[ rstereopos ]; ( *ptr++ ) += rstereobuf_r[ rstereopos ]; rstereobuf_l[ rstereopos ] = rstereobuf_r[ rstereopos ] = 0; rstereopos++; if( rstereopos >= STEREO_BUF_SIZE ) rstereopos = 0; } } #endif /* update noise RNG/filter */ _this->ay_noise_tick += noise_count; while( _this->ay_noise_tick >= _this->ay_noise_period ) { _this->ay_noise_tick -= _this->ay_noise_period; if( ( _this->rng & 1 ) ^ ( ( _this->rng & 2 ) ? 1 : 0 ) ) _this->noise_toggle = !_this->noise_toggle; /* rng is 17-bit shift reg, bit 0 is output. * input is bit 0 xor bit 2. */ _this->rng |= ( ( _this->rng & 1 ) ^ ( ( _this->rng & 4 ) ? 1 : 0 ) ) ? 0x20000 : 0; _this->rng >>= 1; /* don't keep trying if period is zero */ if( !_this->ay_noise_period ) break; } } }
void sound_ay_overlay(void) { signed short *ptr; int tone_level[3]; int mixer,envshape; int f,g,level,count; struct ay_change_tag *change_ptr=ay_change; int changes_left=ay_change_count; int reg,r; int is_low; int chan1,chan2,chan3; unsigned int tone_count,noise_count; /* If no AY chip, don't produce any AY sound (!) */ //if(!machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_AY) return; /* convert change times to sample offsets */ for(f=0;f<ay_change_count;f++) ay_change[f].ofs=(ay_change[f].tstates*sound_freq)/ (tsmax*50); for(f=0,ptr=sound_buf;f<sound_framesiz;f++) { /* update ay registers. All this sub-frame change stuff * is pretty hairy, but how else would you handle the * samples in Robocop? :-) It also clears up some other * glitches. */ while(changes_left && f>=change_ptr->ofs) { sound_ay_registers[reg=change_ptr->reg]=change_ptr->val; change_ptr++; changes_left--; /* fix things as needed for some register changes */ switch(reg) { case 0: case 1: case 2: case 3: case 4: case 5: r=reg>>1; /* a zero-len period is the same as 1 */ ay_tone_period[r]=(sound_ay_registers[reg&~1]| (sound_ay_registers[reg|1]&15)<<8); if(!ay_tone_period[r]) ay_tone_period[r]++; /* important to get this right, otherwise e.g. Ghouls 'n' Ghosts * has really scratchy, horrible-sounding vibrato. */ if(ay_tone_tick[r]>=ay_tone_period[r]<<1) ay_tone_tick[r]%=ay_tone_period[r]<<1; break; case 6: ay_noise_tick=0; ay_noise_period=(sound_ay_registers[reg]&31); break; case 11: case 12: /* this one *isn't* fixed-point */ ay_env_period=sound_ay_registers[11]|(sound_ay_registers[12]<<8); break; case 13: ay_env_internal_tick=ay_env_tick=ay_env_subcycles=0; env_first=1; env_rev=0; env_counter=(sound_ay_registers[13]&AY_ENV_ATTACK)?0:15; break; } } /* the tone level if no enveloping is being used */ for(g=0;g<3;g++) tone_level[g]=ay_tone_levels[sound_ay_registers[8+g]&15]; /* envelope */ envshape=sound_ay_registers[13]; level=ay_tone_levels[env_counter]; for(g=0;g<3;g++) if(sound_ay_registers[8+g]&16) tone_level[g]=level; /* envelope output counter gets incr'd every 16 AY cycles. * Has to be a while, as this is sub-output-sample res. */ ay_env_subcycles+=ay_tick_incr; noise_count=0; while(ay_env_subcycles>=(16<<16)) { ay_env_subcycles-=(16<<16); noise_count++; ay_env_tick++; while(ay_env_tick>=ay_env_period) { ay_env_tick-=ay_env_period; /* do a 1/16th-of-period incr/decr if needed */ if(env_first || ((envshape&AY_ENV_CONT) && !(envshape&AY_ENV_HOLD))) { if(env_rev) env_counter-=(envshape&AY_ENV_ATTACK)?1:-1; else env_counter+=(envshape&AY_ENV_ATTACK)?1:-1; if(env_counter<0) env_counter=0; if(env_counter>15) env_counter=15; } ay_env_internal_tick++; while(ay_env_internal_tick>=16) { ay_env_internal_tick-=16; /* end of cycle */ if(!(envshape&AY_ENV_CONT)) env_counter=0; else { if(envshape&AY_ENV_HOLD) { if(env_first && (envshape&AY_ENV_ALT)) env_counter=(env_counter?0:15); } else { /* non-hold */ if(envshape&AY_ENV_ALT) env_rev=!env_rev; else env_counter=(envshape&AY_ENV_ATTACK)?0:15; } } env_first=0; } /* don't keep trying if period is zero */ if(!ay_env_period) break; } } /* generate tone+noise... or neither. * (if no tone/noise is selected, the chip just shoves the * level out unmodified. This is used by some sample-playing * stuff.) */ chan1=tone_level[0]; chan2=tone_level[1]; chan3=tone_level[2]; mixer=sound_ay_registers[7]; ay_tone_subcycles+=ay_tick_incr; tone_count=ay_tone_subcycles>>(3+16); ay_tone_subcycles&=(8<<16)-1; if((mixer&1)==0) { level=chan1; AY_DO_TONE(chan1,0); } if((mixer&0x08)==0 && noise_toggle) chan1=0; if((mixer&2)==0) { level=chan2; AY_DO_TONE(chan2,1); } if((mixer&0x10)==0 && noise_toggle) chan2=0; if((mixer&4)==0) { level=chan3; AY_DO_TONE(chan3,2); } if((mixer&0x20)==0 && noise_toggle) chan3=0; //Seleuco: cuidado si ponemos pseudostereo es el codigo de abajo. /* write the sample(s) */ //(*ptr++)+=chan1+chan2+chan3; if(!sound_stereo) { /* mono */ (*ptr++)+=chan1+chan2+chan3; } else { if(!sound_stereo_ay) { /* stereo output, but mono AY sound; still, * incr separately in case of beeper pseudostereo. */ (*ptr++)+=chan1+chan2+chan3; (*ptr++)+=chan1+chan2+chan3; } else { /* stereo with ACB/ABC AY positioning. * Here we use real stereo positions for the channels. * Just because, y'know, it's cool and stuff. No, really. :-) * This is a little tricky, as it works by delaying sounds * on the left or right channels to model the delay you get * in the real world when sounds originate at different places. */ GEN_STEREO(rchan1pos,chan1); GEN_STEREO(rchan2pos,chan2); GEN_STEREO(rchan3pos,chan3); (*ptr++)+=rstereobuf_l[rstereopos]; (*ptr++)+=rstereobuf_r[rstereopos]; rstereobuf_l[rstereopos]=rstereobuf_r[rstereopos]=0; rstereopos++; if(rstereopos>=STEREO_BUF_SIZE) rstereopos=0; } } /* update noise RNG/filter */ ay_noise_tick+=noise_count; while(ay_noise_tick>=ay_noise_period) { ay_noise_tick-=ay_noise_period; if((rng&1)^((rng&2)?1:0)) noise_toggle=!noise_toggle; /* rng is 17-bit shift reg, bit 0 is output. * input is bit 0 xor bit 2. */ rng|=((rng&1)^((rng&4)?1:0))?0x20000:0; rng>>=1; /* don't keep trying if period is zero */ if(!ay_noise_period) break; } } }