/*************************************************************************/ /* cp_player_data_events.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "cp_player_data.h" #include "cp_sample_manager.h" #include "stdio.h" /* setup_voices(): This will go throught all the REAL channels, if it finds a channel that needs to be restarted or assigned a new VIRTUAL channel, then it will just find one and do it. */ #define C5FREQ 261.6255653006 static const int32_t C5FREQ_MIXER = ((int32_t)(C5FREQ * (float)(1 << CPMixer::FREQUENCY_BITS))); void CPPlayer::setup_voices() { int i, voice_index; for (i = 0; i < CPPattern::WIDTH; i++) { voice_index = -1; if (control.channel[i].note_delay) continue; // check if we need a new empty voice if (control.channel[i].kick == KICK_NOTE) { /* if no channel was cut above, find an empty or quiet channel here */ if (song->has_instruments() && !control.force_no_nna) { if (control.channel[i].slave_voice == NULL) { // no slave?? int newchn; if ((newchn = find_empty_voice()) != -1) { control.channel[i].slave_voice_index = newchn; control.channel[i].slave_voice = &voice[newchn]; } } } else { if (i < control.max_voices) { control.channel[i].slave_voice_index = i; control.channel[i].slave_voice = &voice[i]; } else { //This is a _DIRTY_ hack, but i cant think a better way. control.channel[i].slave_voice_index = control.max_voices - 1; control.channel[i].slave_voice = &voice[control.max_voices - 1]; } } /* assign parts of MP_VOICE only done for a KICK_NOTE */ if ((control.channel[i].slave_voice != NULL)) { voice_index = control.channel[i].slave_voice_index; Voice_Control &v = voice[voice_index]; if (v.has_master_channel && (v.master_channel != NULL)) { // If this voice already has a master channel, make sure to remove the reference to it. v.master_channel->slave_voice = NULL; } //notify the voice that the current channel is the master v.master_channel = &control.channel[i]; //set the voice as slave of the current channel control.channel[i].slave_voice = &v; //master channel index of the voice v.master_channel_index = i; v.has_master_channel = true; } } else { // nope.. // so if we DO have a slave voice then use it. if (control.channel[i].slave_voice != NULL) { voice_index = control.channel[i].slave_voice_index; } } //assuming this channel has a slave voice.. if (voice_index >= 0) { // IMPROVE: Code a method for this: voice[voice_index].update_info_from_master_channel(); } control.channel[i].kick = KICK_NOTHING; } } void CPPlayer::Voice_Control::reset() { cp_memzero(this, sizeof(*this)); instrument_ptr = NULL; sample_ptr = NULL; has_master_channel = false; instrument_index = -1; reverb_send = 0; chorus_send = 0; filter.it_cutoff = 255; filter.it_reso = 0; display_volume = 0; } void CPPlayer::Channel_Control::reset() { int prev_gv = channel_global_volume; cp_memzero(this, sizeof(*this)); slave_voice = NULL; slave_voice_index = 255; mute = false; old_note = 255; real_note = 255; instrument_index = 255; filter.it_cutoff = 255; filter.it_reso = 0; reverb_send = 0; chorus_send = 0; reserved = false; carry.maybe = false; last_event_usecs = -1; channel_global_volume = prev_gv; } void CPPlayer::Voice_Control::update_info_from_master_channel() { instrument_ptr = master_channel->instrument_ptr; sample_ptr = master_channel->sample_ptr; instrument_index = master_channel->instrument_index; sample_index = master_channel->sample_index; note = master_channel->note; output_volume = master_channel->output_volume; channel_volume = master_channel->channel_volume; panning = master_channel->panning; kick = master_channel->kick; note_end_flags = master_channel->note_end_flags; period = master_channel->period; volume_envelope_ctrl.active = master_channel->volume_envelope_on; panning_envelope_ctrl.active = master_channel->panning_envelope_on; pitch_envelope_ctrl.active = master_channel->pitch_envelope_on; NNA_type = master_channel->NNA_type; reverb_send = master_channel->reverb_send; chorus_send = master_channel->chorus_send; // last_note_type=master_channel->last_note_type; sample_start_index = master_channel->sample_start_index; filter = master_channel->filter; } void CPPlayer::update_mixer() { int tmp_mixer_period; int32_t tmp_vibrato_value, tmp_vibrato_depth, tmp_volenv_value; uint64_t tmpvol; // 64bits should be the only way to avoid getting notes raped out int i; control.voices_used = 0; for (i = 0; i < control.max_voices; i++) { int filter_env = -1; Voice_Control &v = voice[i]; if (!((v.kick == KICK_NOTE) || (v.kick == KICK_NOTEOFF)) && !is_voice_active(i)) continue; //if voice doesnt have a sample set or size is 0.. forget it if (v.sample_ptr == NULL) continue; //TODO set limits somewhere else if (v.period < 40) { v.period = 40; } else if (v.period > 50000) { v.period = 50000; } if ((v.kick == KICK_NOTE) || (v.kick == KICK_NOTEOFF)) { int real_start_index; if (v.sample_start_index == -1) { real_start_index = 0; } else { real_start_index = v.sample_start_index; } mixer->setup_voice(i, v.sample_ptr->get_sample_data(), real_start_index); v.fadeout_volume = 1024; //IT Docs it is 1024 internally v.auto_vibrato_sweep_pos = 0; } /* Start Envelopes */ if (song->has_instruments() && ((v.kick == KICK_NOTE) || (v.kick == KICK_ENVELOPE))) { // Voice_Control *carry=0; if (v.has_master_channel && v.master_channel->carry.maybe) { v.start_envelope(v.instrument_ptr->get_volume_envelope(), &v.volume_envelope_ctrl, &v.master_channel->carry.vol); v.start_envelope(v.instrument_ptr->get_pan_envelope(), &v.panning_envelope_ctrl, &v.master_channel->carry.pan); v.start_envelope(v.instrument_ptr->get_pitch_filter_envelope(), &v.pitch_envelope_ctrl, &v.master_channel->carry.pitch); } else { v.start_envelope(v.instrument_ptr->get_volume_envelope(), &v.volume_envelope_ctrl, NULL); v.start_envelope(v.instrument_ptr->get_pan_envelope(), &v.panning_envelope_ctrl, NULL); v.start_envelope(v.instrument_ptr->get_pitch_filter_envelope(), &v.pitch_envelope_ctrl, NULL); } } v.kick = KICK_NOTHING; if (song->has_instruments()) { if (!v.process_envelope(v.instrument_ptr->get_volume_envelope(), &v.volume_envelope_ctrl)) v.volume_envelope_ctrl.value = 64; if (!v.process_envelope(v.instrument_ptr->get_pan_envelope(), &v.panning_envelope_ctrl)) v.panning_envelope_ctrl.value = 0; if (!v.process_envelope(v.instrument_ptr->get_pitch_filter_envelope(), &v.pitch_envelope_ctrl)) v.pitch_envelope_ctrl.value = 0; if (v.volume_envelope_ctrl.terminated) { if (v.volume_envelope_ctrl.kill) { v.fadeout_volume = 0; } else { v.note_end_flags |= END_NOTE_FADE; } } } if (song->has_instruments()) { tmp_volenv_value = v.volume_envelope_ctrl.value; } else { tmp_volenv_value = 64; } /*printf("fadeout %i\n",(int)v.fadeout_volume); printf("channel %i\n",(int)v.channel_volume); printf("output %i\n",(int)v.output_volume); printf("env %i\n",(int)tmp_volenv_value); printf("cgb %i\n",(int)v.master_channel->channel_global_volume); */ tmpvol = (uint64_t)v.fadeout_volume; /* max 1024 - 10 bits */ tmpvol *= (uint64_t)v.channel_volume; /* * max 64 - 6 bits */ tmpvol *= (uint64_t)v.output_volume; /* * max 256 - 8 bits */ tmpvol *= (uint64_t)tmp_volenv_value; /* max 64 - 6 bits*/ tmpvol *= (uint64_t)v.master_channel->channel_global_volume; v.display_volume = tmpvol >> 22; //volume used for display purposes , 0 -- 256 tmpvol *= (uint64_t)song->get_mixing_volume(); /* max 128 - 7 bits */ tmpvol *= (uint64_t)control.global_volume; /* max 128 - 7 bits*/ /* total 10+6+8+6+7+7=44 bits */ tmpvol >>= 43; /* Move back to 8 bits range , 44-19+8=43*/ if (tmpvol > CP_VOL_MAX) tmpvol = CP_VOL_MAX; //printf("volume check - fade %i, channel %i, output %i, env %i, mix %i, global %i -- final %i\n",v.fadeout_volume, v.channel_volume,v.output_volume,tmp_volenv_value, song->get_mixing_volume(),control.global_volume,tmpvol); v.total_volume = tmpvol; if ((v.master_channel != NULL) && song->is_channel_mute(v.master_channel_index) && !v.master_channel->reserved) { mixer->set_voice_volume(i, 0); } else { mixer->set_voice_volume(i, tmpvol); if (v.fadeout_volume > 0) control.voices_used++; } if (!song->is_stereo()) { mixer->set_voice_panning(i, PAN_CENTER); } else if (v.panning == PAN_SURROUND) { mixer->set_voice_panning(i, PAN_SURROUND); } else if (song->has_instruments()) { int newpan, real_modifier; real_modifier = (v.panning_envelope_ctrl.value * (PAN_CENTER - cp_intabs(v.panning - PAN_CENTER))) / 32; newpan = v.panning + real_modifier; newpan = (newpan < PAN_LEFT) ? PAN_LEFT : (newpan > PAN_RIGHT) ? PAN_RIGHT : newpan; //printf("panenv val: %i, finalpan val %i\n",v.panning_envelope_ctrl.value,newpan); mixer->set_voice_panning(i, newpan); } else { mixer->set_voice_panning(i, v.panning); } /* VIBRATO */ if ((v.period > 0) && (v.sample_ptr->get_vibrato_depth() > 0)) { switch (v.sample_ptr->get_vibrato_type()) { case CPSample::VIBRATO_SINE: tmp_vibrato_value = auto_vibrato_table[v.auto_vibrato_pos & 127]; if (v.auto_vibrato_pos & 0x80) tmp_vibrato_value = -tmp_vibrato_value; break; case CPSample::VIBRATO_SQUARE: tmp_vibrato_value = 64; if (v.auto_vibrato_pos & 0x80) tmp_vibrato_value = -tmp_vibrato_value; break; case CPSample::VIBRATO_SAW: tmp_vibrato_value = 63 - (((v.auto_vibrato_pos + 128) & 255) >> 1); break; default: tmp_vibrato_value = (((v.auto_vibrato_pos + 128) & 255) >> 1) - 64; break; } } else { tmp_vibrato_value = 0; } if ((v.auto_vibrato_sweep_pos >> 8) < v.sample_ptr->get_vibrato_depth()) { v.auto_vibrato_sweep_pos += v.sample_ptr->get_vibrato_speed(); //FIXME - speed? i think so tmp_vibrato_depth = v.auto_vibrato_sweep_pos; } else { tmp_vibrato_depth = v.sample_ptr->get_vibrato_depth() << 8; } tmp_vibrato_value = (tmp_vibrato_value * tmp_vibrato_depth) >> 16; if (song->has_linear_slides()) tmp_vibrato_value >>= 1; v.period -= tmp_vibrato_value; /* update vibrato position */ v.auto_vibrato_pos = (v.auto_vibrato_pos + v.sample_ptr->get_vibrato_rate()) & 0xff; /* process pitch envelope */ tmp_mixer_period = v.period; if (v.pitch_envelope_ctrl.active) { long aux_pitch_diff; int pe_value = v.pitch_envelope_ctrl.value; if (!v.instrument_ptr->is_pitch_use_as_filter()) { if (((uint16_t)v.note << 1) + pe_value <= 0) pe_value = -(v.note << 1); int smp_c5 = CPSampleManager::get_singleton()->get_c5_freq(v.sample_ptr->get_sample_data()); int base = get_period(((uint16_t)v.note << 1), smp_c5); int env = get_period(((uint16_t)v.note << 1) + pe_value, smp_c5); /* int env_next=(pe_value<0)?get_period(((uint16_t)(v.note-1)<<1)+pe_value,smp_c5):get_period(((uint16_t)(v.note+1)<<1)+pe_value,smp_c5); env=env+(abs(v.pitch_envelope_ctrl.value)&((1<stop_voice(i); } else { int32_t freq = get_frequency(tmp_mixer_period); int32_t tracker_c5 = get_frequency(get_period(60 << 1, CPSampleManager::get_singleton()->get_c5_freq(v.sample_ptr->get_sample_data()))); freq = (int32_t)((uint64_t)freq * (uint64_t)C5FREQ_MIXER / (uint64_t)tracker_c5); //numbers may become very high mixer->set_voice_frequency(i, freq); /* if keyfade, start substracting fadeoutspeed from fadevol: */ if ((song->has_instruments()) && (v.note_end_flags & END_NOTE_FADE)) { if (v.fadeout_volume >= (v.instrument_ptr->get_volume_fadeout())) { v.fadeout_volume -= (v.instrument_ptr->get_volume_fadeout()); } else { v.fadeout_volume = 0; } } /*FILTARSSSSSSSS*/ v.filter.envelope_cutoff = filter_env; v.filter.process(); if ((v.filter.final_cutoff < 0xFF) && (control.filters)) { //int final_cutoff; //uint8_t final_reso; //v.filter.set_filter_parameters( &final_cutoff, &final_reso ); mixer->set_voice_filter(i, true, v.filter.final_cutoff, v.filter.it_reso); } else { mixer->set_voice_filter(i, false, 0, 0); } /* RAIVERV */ mixer->set_voice_reverb_send(i, v.reverb_send); /* CHAURUZ */ mixer->set_voice_chorus_send(i, v.chorus_send); } } switch (song->get_reverb_mode()) { case CPSong::REVERB_MODE_ROOM: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_ROOM); } break; case CPSong::REVERB_MODE_STUDIO_SMALL: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_STUDIO_SMALL); } break; case CPSong::REVERB_MODE_STUDIO_MEDIUM: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_STUDIO_MEDIUM); } break; case CPSong::REVERB_MODE_STUDIO_LARGE: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_STUDIO_LARGE); } break; case CPSong::REVERB_MODE_HALL: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_HALL); } break; case CPSong::REVERB_MODE_SPACE_ECHO: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_SPACE_ECHO); } break; case CPSong::REVERB_MODE_ECHO: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_ECHO); } break; case CPSong::REVERB_MODE_DELAY: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_DELAY); } break; case CPSong::REVERB_MODE_HALF_ECHO: { mixer->set_reverb_mode(CPMixer::REVERB_MODE_HALF_ECHO); } break; } mixer->set_chorus_params(song->get_chorus_delay_ms(), song->get_chorus_separation_ms(), song->get_chorus_depth_ms10(), song->get_chorus_speed_hz10()); } void CPPlayer::handle_tick() { int i; if (mixer == NULL) return; if (song == NULL) return; /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */ if (control.ticks_counter >= control.speed) { // time to process... ***THE ROW***! /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is the command memory. */ // if (control.pattern_delay_1) { // control.pattern_delay_2=control.pattern_delay_1; // control.pattern_delay_1=0; // } // if (control.pattern_delay_2) { // patterndelay active // if (--control.pattern_delay_2) // so turn back pf->patpos by 1 // if (pf->patpos) pf->patpos--; // } if (control.play_mode != PLAY_NOTHING) { control.ticks_counter = 0; if (control.position.force_next_order >= 0) { control.position.current_order = control.position.force_next_order; } control.position.force_next_order = -1; control.previous_position = control.position; // for those special cases... control.position.forbid_jump = false; for (i = 0; i < CPPattern::WIDTH; i++) { process_note(i, song->get_pattern(control.position.current_pattern)->get_note(i, control.position.current_row)); } control.position.current_row++; if (control.position.current_row >= song->get_pattern(control.position.current_pattern)->get_length()) { if (control.play_mode == PLAY_SONG) { int next_order; next_order = get_song_next_order_idx(song, control.position.current_order); if (next_order != -1) { // Do we have a "next order?" control.position.current_pattern = song->get_order(next_order); if (next_order <= control.position.current_order) control.reached_end = true; control.position.current_order = next_order; } else { // no, probably the user deleted the orderlist. control.play_mode = PLAY_NOTHING; reset(); control.reached_end = true; } } control.position.current_row = 0; } } } pre_process_effects(); process_NNAs(); setup_voices(); /* now set up the actual hardware channel playback information */ update_mixer(); control.ticks_counter++; }