// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ #include #include #include #include #include "Blip_Buffer.h" /* Copyright (C) 2003-2007 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // TODO: use scoped for variables in treble_eq() Blip_Buffer::Blip_Buffer() { this->factor_ = static_cast(std::numeric_limits::max()); this->buffer_.clear(); this->buffer_size_ = 0; this->sample_rate_ = 0; this->bass_shift_ = 0; this->clock_rate_ = 0; this->bass_freq_ = 16; this->length_ = 0; // assumptions code makes about implementation-defined features #ifndef NDEBUG // right shift of negative value preserves sign buf_t_ i = -0x7FFFFFFE; assert((i >> 1) == -0x3FFFFFFF); // casting to short truncates to 16 bits and sign-extends i = 0x18000; assert(static_cast(i) == -0x8000); #endif this->clear(); } Blip_Buffer::~Blip_Buffer() { } void Blip_Buffer::clear(int entire_buffer) { this->offset_ = 0; this->reader_accum_ = 0; this->modified_ = nullptr; if (!this->buffer_.empty()) { long count = entire_buffer ? this->buffer_size_ : this->samples_avail(); memset(&this->buffer_[0], 0, (count + blip_buffer_extra_) * sizeof(buf_t_)); } } void Blip_Buffer::set_sample_rate(long new_rate, int msec) { // start with maximum length that resampled time can represent long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; if (msec != blip_max_length) { long s = (new_rate * (msec + 1) + 999) / 1000; if (s < new_size) new_size = s; else assert(false); // fails if requested buffer length exceeds limit } if (this->buffer_size_ != new_size) this->buffer_.resize(new_size + blip_buffer_extra_); this->buffer_size_ = new_size; // update things based on the sample rate this->sample_rate_ = new_rate; this->length_ = new_size * 1000 / new_rate - 1; if (msec) assert(this->length_ == msec); // ensure length is same as that passed in // update these since they depend on sample rate if (this->clock_rate_) this->clock_rate(this->clock_rate_); this->bass_freq(this->bass_freq_); this->clear(); } blip_resampled_time_t Blip_Buffer::clock_rate_factor(long rate) const { double ratio = static_cast(this->sample_rate_) / rate; int32_t factor = static_cast(std::floor(ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5)); assert(factor > 0 || !this->sample_rate_); // fails if clock/output ratio is too large return static_cast(factor); } void Blip_Buffer::bass_freq(int freq) { this->bass_freq_ = freq; int shift = 31; if (freq > 0) { shift = 13; long f = (freq << 16) / this->sample_rate_; while ((f >>= 1) && --shift); } this->bass_shift_ = shift; } void Blip_Buffer::end_frame(blip_time_t t) { this->offset_ += t * this->factor_; assert(this->samples_avail() <= static_cast(this->buffer_size_)); // fails if time is past end of buffer } long Blip_Buffer::count_samples(blip_time_t t) const { auto last_sample = this->resampled_time(t) >> BLIP_BUFFER_ACCURACY; auto first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY; return static_cast(last_sample - first_sample); } blip_time_t Blip_Buffer::count_clocks(long count) const { if (!this->factor_) { assert(false); // sample rate and clock rates must be set first return 0; } if (count > this->buffer_size_) count = this->buffer_size_; auto time = static_cast(count) << BLIP_BUFFER_ACCURACY; return static_cast((time - this->offset_ + this->factor_ - 1) / this->factor_); } void Blip_Buffer::remove_samples(long count) { if (count) { this->remove_silence(count); // copy remaining samples to beginning and clear old samples long remain = this->samples_avail() + blip_buffer_extra_; memmove(&this->buffer_[0], &this->buffer_[count], remain * sizeof(buf_t_)); memset(&this->buffer_[remain], 0, count * sizeof(buf_t_)); } } // Blip_Synth_ Blip_Synth_Fast_::Blip_Synth_Fast_() { this->buf = nullptr; this->last_amp = this->delta_factor = 0; } void Blip_Synth_Fast_::volume_unit(double new_unit) { this->delta_factor = static_cast(new_unit * (1L << blip_sample_bits) + 0.5); } #if !BLIP_BUFFER_FAST Blip_Synth_::Blip_Synth_(short *p, int w) : impulses(p), width(w) { this->volume_unit_ = 0.0; this->kernel_unit = 0; this->buf = nullptr; this->last_amp = this->delta_factor = 0; } #ifndef M_PI static const double M_PI = 3.1415926535897932384626433832795029; #endif static void gen_sinc(float *out, int count, double oversample, double treble, double cutoff) { if (cutoff >= 0.999) cutoff = 0.999; if (treble < -300.0) treble = -300.0; if (treble > 5.0) treble = 5.0; static const double maxh = 4096.0; double rolloff = std::pow(10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff)); double pow_a_n = std::pow(rolloff, maxh - maxh * cutoff); double to_angle = M_PI / 2 / maxh / oversample; for (int i = 0; i < count; ++i) { double angle = ((i - count) * 2 + 1) * to_angle; double c = rolloff * std::cos((maxh - 1.0) * angle) - std::cos(maxh * angle); double cos_nc_angle = std::cos(maxh * cutoff * angle); double cos_nc1_angle = std::cos((maxh * cutoff - 1.0) * angle); double cos_angle = std::cos(angle); c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); double b = 2.0 - cos_angle - cos_angle; double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; out[i] = static_cast((a * d + c * b) / (b * d)); // a / b + c / d } } void blip_eq_t::generate(float *out, int count) const { // lower cutoff freq for narrow kernels with their wider transition band // (8 points->1.49, 16 points->1.15) double oversample = blip_res * 2.25 / count + 0.85; double half_rate = this->sample_rate * 0.5; if (this->cutoff_freq) oversample = half_rate / this->cutoff_freq; double cutoff = this->rolloff_freq * oversample / half_rate; gen_sinc(out, count, blip_res * oversample, this->treble, cutoff); // apply (half of) hamming window double to_fraction = M_PI / (count - 1); for (int i = count; i--; ) out[i] *= 0.54f - 0.46f * static_cast(std::cos(i * to_fraction)); } void Blip_Synth_::adjust_impulse() { // sum pairs for each phase and add error correction to end of first half int size = this->impulses_size(); for (int p = blip_res; p-- >= blip_res / 2; ) { int p2 = blip_res - 2 - p; long error = this->kernel_unit; for (int i = 1; i < size; i += blip_res) error -= this->impulses[i + p] + this->impulses[i + p2]; if (p == p2) error /= 2; // phase = 0.5 impulse uses same half for both sides this->impulses[size - blip_res + p] += static_cast(error); //printf( "error: %ld\n", error ); } //for (int i = blip_res; i--; printf("\n")) //for (int j = 0; j < this->width / 2; ++j) //printf("%5ld,", this->impulses[j * blip_res + i + 1]); } void Blip_Synth_::treble_eq(const blip_eq_t &eq) { float fimpulse[blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; int half_size = blip_res / 2 * (this->width - 1); eq.generate(&fimpulse[blip_res], half_size); int i; // need mirror slightly past center for calculation for (i = blip_res; i--; ) fimpulse[blip_res + half_size + i] = fimpulse[blip_res + half_size - 1 - i]; // starts at 0 std::fill(&fimpulse[0], &fimpulse[blip_res], 0.0f); // find rescale factor double total = std::accumulate(&fimpulse[blip_res], &fimpulse[blip_res + half_size], 0.0); //static const double base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB //static const double base_unit = 37888.0; // allows treble to +5 dB static const double base_unit = 32768.0; // necessary for blip_unscaled to work double rescale = base_unit / 2 / total; this->kernel_unit = static_cast(base_unit); // integrate, first difference, rescale, convert to int double sum = 0.0; double next = 0.0; int size = this->impulses_size(); for (i = 0; i < size; ++i) { this->impulses[i] = static_cast(std::floor((next - sum) * rescale + 0.5)); sum += fimpulse[i]; next += fimpulse[i + blip_res]; } this->adjust_impulse(); // volume might require rescaling double vol = this->volume_unit_; if (vol) { this->volume_unit_ = 0.0; this->volume_unit(vol); } } void Blip_Synth_::volume_unit(double new_unit) { if (new_unit != this->volume_unit_) { // use default eq if it hasn't been set yet if (!this->kernel_unit) this->treble_eq(-8.0); this->volume_unit_ = new_unit; double factor = new_unit * (1L << blip_sample_bits) / this->kernel_unit; if (factor > 0.0) { int shift = 0; // if unit is really small, might need to attenuate kernel while (factor < 2.0) { ++shift; factor *= 2.0; } if (shift) { this->kernel_unit >>= shift; assert(this->kernel_unit > 0); // fails if volume unit is too low // keep values positive to avoid round-towards-zero of sign-preserving // right shift for negative values long offset = 0x8000 + (1 << (shift - 1)); long offset2 = 0x8000 >> shift; for (int i = this->impulses_size(); i--; ) this->impulses[i] = static_cast(((this->impulses[i] + offset) >> shift) - offset2); this->adjust_impulse(); } } this->delta_factor = static_cast(std::floor(factor + 0.5)); //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); } } #endif long Blip_Buffer::read_samples(blip_sample_t *out_, long max_samples, bool stereo) { long count = this->samples_avail(); if (count > max_samples) count = max_samples; if (count) { int bass = BLIP_READER_BASS(this); BLIP_READER_BEGIN(reader, *this); BLIP_READER_ADJ_(reader, count); auto out = &out_[count]; int32_t offset = static_cast(-count); do { int32_t s = BLIP_READER_READ(reader); BLIP_READER_NEXT_IDX_(reader, bass, offset); BLIP_CLAMP(s, s); out[offset * (stereo ? 2 : 1)] = static_cast(s); } while (++offset); BLIP_READER_END(reader, *this); this->remove_samples(count); } return count; } void Blip_Buffer::mix_samples(const blip_sample_t *in, long count) { auto out = &this->buffer_[(this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2]; static const int sample_shift = blip_sample_bits - 16; int prev = 0; while (count--) { int32_t s = static_cast(*in++) << sample_shift; *out += s - prev; prev = s; ++out; } *out -= prev; }