From b3054fd6398b1ec51fbee8087f6378c8c9f32d49 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 15 Jul 2017 22:27:30 +0100 Subject: [PATCH 01/14] Equalizer plugin, refinement to analysis display (#3530) The spectural analysis was using a rectangle window, leading to high spectural leakage. This pull request uses the Blackman-Harris window to give a display more representative of the audio. --- plugins/Eq/EqSpectrumView.cpp | 23 +++++++++++++++++++++-- plugins/Eq/EqSpectrumView.h | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/plugins/Eq/EqSpectrumView.cpp b/plugins/Eq/EqSpectrumView.cpp index 0f29ffec3..d44e01ece 100644 --- a/plugins/Eq/EqSpectrumView.cpp +++ b/plugins/Eq/EqSpectrumView.cpp @@ -1,9 +1,8 @@ /* eqspectrumview.cpp - implementation of EqSpectrumView class. * -* Copyright (c) 2014 David French +* Copyright (c) 2014-2017, David French * * This file is part of LMMS - https://lmms.io -* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either @@ -38,6 +37,20 @@ EqAnalyser::EqAnalyser() : m_inProgress=false; m_specBuf = ( fftwf_complex * ) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); + + //initialize Blackman-Harris window, constants taken from + //https://en.wikipedia.org/wiki/Window_function#A_list_of_window_functions + const float a0 = 0.35875; + const float a1 = 0.48829; + const float a2 = 0.14128; + const float a3 = 0.01168; + + for(int i = 0; i < FFT_BUFFER_SIZE; i++) + { + m_fftWindow[i] = ( a0 - a1 * cosf( 2 * F_PI * i / (float)FFT_BUFFER_SIZE - 1 ) + + a2 * cosf( 4 * F_PI * i / (float)FFT_BUFFER_SIZE-1) + - a3 * cos( 6 * F_PI * i / (float)FFT_BUFFER_SIZE - 1.0 )); + } clear(); } @@ -84,6 +97,12 @@ void EqAnalyser::analyze( sampleFrame *buf, const fpp_t frames ) const int LOWEST_FREQ = 0; const int HIGHEST_FREQ = m_sampleRate / 2; + //apply FFT window + for( int i = 0; i < FFT_BUFFER_SIZE; i++ ) + { + m_buffer[i] = m_buffer[i] * m_fftWindow[i]; + } + fftwf_execute( m_fftPlan ); absspec( m_specBuf, m_absSpecBuf, FFT_BUFFER_SIZE+1 ); diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index 2c615d888..cd3f17758 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -32,7 +32,6 @@ const int MAX_BANDS = 2048; - class EqAnalyser { public: @@ -61,6 +60,7 @@ private: int m_sampleRate; bool m_active; bool m_inProgress; + float m_fftWindow[FFT_BUFFER_SIZE]; }; From 587989b1c2b233de29ceb2bea103b14b5c4b6894 Mon Sep 17 00:00:00 2001 From: Karmo Rosental Date: Fri, 14 Jul 2017 23:51:58 +0300 Subject: [PATCH 02/14] Updated Freeboy files from Game Music Emu 0.6.1. (#3618) Uses upstream files to fix #326 [oskar.wallgren13@gmail.com: backported from master] --- plugins/papu/Basic_Gb_Apu.cpp | 14 +- plugins/papu/Basic_Gb_Apu.h | 20 +- plugins/papu/CMakeLists.txt | 2 +- plugins/papu/gb_apu/Blip_Buffer.cpp | 759 ++++++++++++++------------- plugins/papu/gb_apu/Blip_Buffer.h | 649 +++++++++++++++-------- plugins/papu/gb_apu/Blip_Synth.h | 208 -------- plugins/papu/gb_apu/Gb_Apu.cpp | 257 +++++---- plugins/papu/gb_apu/Gb_Apu.h | 66 +-- plugins/papu/gb_apu/Gb_Oscs.cpp | 585 +++++++++------------ plugins/papu/gb_apu/Gb_Oscs.h | 101 ++-- plugins/papu/gb_apu/Multi_Buffer.cpp | 213 ++++---- plugins/papu/gb_apu/Multi_Buffer.h | 94 ++-- plugins/papu/gb_apu/blargg_common.h | 300 ++++++----- plugins/papu/gb_apu/blargg_config.h | 43 ++ plugins/papu/gb_apu/blargg_source.h | 68 ++- plugins/papu/papu_instrument.cpp | 4 +- 16 files changed, 1728 insertions(+), 1655 deletions(-) delete mode 100644 plugins/papu/gb_apu/Blip_Synth.h create mode 100644 plugins/papu/gb_apu/blargg_config.h diff --git a/plugins/papu/Basic_Gb_Apu.cpp b/plugins/papu/Basic_Gb_Apu.cpp index 7307eb8a2..53c2fab17 100644 --- a/plugins/papu/Basic_Gb_Apu.cpp +++ b/plugins/papu/Basic_Gb_Apu.cpp @@ -14,15 +14,11 @@ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -gb_time_t const frame_length = 70224; +blip_time_t const frame_length = 70224; Basic_Gb_Apu::Basic_Gb_Apu() { time = 0; - - // Adjust frequency equalization to make it sound like a tiny speaker - apu.treble_eq( -20.0 ); // lower values muffle it more - buf.bass_freq( 461 ); // higher values simulate smaller speaker } Basic_Gb_Apu::~Basic_Gb_Apu() @@ -36,12 +32,12 @@ blargg_err_t Basic_Gb_Apu::set_sample_rate( long rate ) return buf.set_sample_rate( rate ); } -void Basic_Gb_Apu::write_register( gb_addr_t addr, int data ) +void Basic_Gb_Apu::write_register( blip_time_t addr, int data ) { apu.write_register( clock(), addr, data ); } -int Basic_Gb_Apu::read_register( gb_addr_t addr ) +int Basic_Gb_Apu::read_register( blip_time_t addr ) { return apu.read_register( clock(), addr ); } @@ -49,8 +45,8 @@ int Basic_Gb_Apu::read_register( gb_addr_t addr ) void Basic_Gb_Apu::end_frame() { time = 0; - bool stereo = apu.end_frame( frame_length ); - buf.end_frame( frame_length, stereo ); + apu.end_frame( frame_length ); + buf.end_frame( frame_length ); } long Basic_Gb_Apu::samples_avail() const diff --git a/plugins/papu/Basic_Gb_Apu.h b/plugins/papu/Basic_Gb_Apu.h index 24b9dc774..b39efe9cf 100644 --- a/plugins/papu/Basic_Gb_Apu.h +++ b/plugins/papu/Basic_Gb_Apu.h @@ -15,23 +15,23 @@ class Basic_Gb_Apu { public: Basic_Gb_Apu(); ~Basic_Gb_Apu(); - + // Set output sample rate blargg_err_t set_sample_rate( long rate ); - + // Pass reads and writes in the range 0xff10-0xff3f - void write_register( gb_addr_t, int data ); - int read_register( gb_addr_t ); - + void write_register( blip_time_t, int data ); + int read_register( blip_time_t ); + // End a 1/60 sound frame and add samples to buffer void end_frame(); - + // Samples are generated in stereo, left first. Sample counts are always // a multiple of 2. - + // Number of samples in buffer long samples_avail() const; - + // Read at most 'count' samples out of buffer and return number actually read typedef blip_sample_t sample_t; long read_samples( sample_t* out, long count ); @@ -41,12 +41,12 @@ public: void treble_eq( const blip_eq_t& eq ); void bass_freq( int bf ); //<--- - + private: Gb_Apu apu; Stereo_Buffer buf; blip_time_t time; - + // faked CPU timing blip_time_t clock() { return time += 4; } }; diff --git a/plugins/papu/CMakeLists.txt b/plugins/papu/CMakeLists.txt index 8662a1cb3..b45fcf614 100644 --- a/plugins/papu/CMakeLists.txt +++ b/plugins/papu/CMakeLists.txt @@ -3,4 +3,4 @@ INCLUDE(BuildPlugin) # Disable C++11 REMOVE_DEFINITIONS(-std=c++0x) -BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Blip_Synth.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/papu/gb_apu/Blip_Buffer.cpp b/plugins/papu/gb_apu/Blip_Buffer.cpp index e291d73ab..a46b0e350 100644 --- a/plugins/papu/gb_apu/Blip_Buffer.cpp +++ b/plugins/papu/gb_apu/Blip_Buffer.cpp @@ -1,429 +1,460 @@ - -// Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ #include "Blip_Buffer.h" +#include +#include #include -#include +#include #include -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* Copyright (C) 2003-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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 */ -#include BLARGG_SOURCE_BEGIN +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer Blip_Buffer::Blip_Buffer() { - samples_per_sec = 44100; - buffer_ = NULL; - - // try to cause assertion failure if buffer is used before these are set - clocks_per_sec = 0; - factor_ = ~0ul; - offset_ = 0; - buffer_size_ = 0; - length_ = 0; - - bass_freq_ = 16; -} + factor_ = (blip_ulong)-1 / 2; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; -void Blip_Buffer::clear( bool entire_buffer ) -{ - long count = (entire_buffer ? buffer_size_ : samples_avail()); - offset_ = 0; - reader_accum = 0; - if ( buffer_ ) - memset( buffer_, sample_offset_ & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) ); -} + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); -blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) -{ - unsigned new_size = (0xFFFFFFFF >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64; - if ( msec != blip_default_length ) - { - size_t s = (new_rate * (msec + 1) + 999) / 1000; - if ( s < new_size ) - new_size = s; - else - require( false ); // requested buffer length exceeds limit - } - - if ( buffer_size_ != new_size ) - { - delete [] buffer_; - buffer_ = NULL; // allow for exception in allocation below - buffer_size_ = 0; - offset_ = 0; - - int const count_clocks_extra = 2; - buffer_ = BLARGG_NEW buf_t_ [new_size + widest_impulse_ + count_clocks_extra]; - BLARGG_CHECK_ALLOC( buffer_ ); - } - - buffer_size_ = new_size; - length_ = new_size * 1000 / new_rate - 1; - if ( msec ) - assert( length_ == msec ); // ensure length is same as that passed in - - samples_per_sec = new_rate; - if ( clocks_per_sec ) - clock_rate( clocks_per_sec ); // recalculate factor - - bass_freq( bass_freq_ ); // recalculate shift - - clear(); - - return blargg_success; -} - -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const -{ - blip_resampled_time_t factor = (unsigned long) floor( - (double) samples_per_sec / clock_rate * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - require( factor > 0 ); // clock_rate/sample_rate ratio is too large - return factor; + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif } Blip_Buffer::~Blip_Buffer() { - delete [] buffer_; + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + long new_size = (UINT_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( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; } void Blip_Buffer::bass_freq( int freq ) { bass_freq_ = freq; - if ( freq == 0 ) + int shift = 31; + if ( freq > 0 ) { - bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere - return; + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } } - bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) ); - if ( bass_shift < 0 ) - bass_shift = 0; - if ( bass_shift > 24 ) - bass_shift = 24; + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; } long Blip_Buffer::count_samples( blip_time_t t ) const { - return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY); + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); } blip_time_t Blip_Buffer::count_clocks( long count ) const { - if ( count > (long) buffer_size_ ) + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) count = buffer_size_; - - return ((count << BLIP_BUFFER_ACCURACY) - offset_ + (factor_ - 1)) / factor_; -} - -void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb ) -{ - fine_bits = fb; - width = w; - impulses = (imp_t*) imps; - generate = true; - volume_unit_ = -1.0; - res = r; - buf = NULL; - - impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)]; - offset = 0; -} - -const int impulse_bits = 15; -const long impulse_amp = 1L << impulse_bits; -const long impulse_offset = impulse_amp / 2; - -void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const -{ - long offset = ((long) unit << impulse_bits) - impulse_offset * unit + - (1 << (impulse_bits - 1)); - imp_t* imp = imp_in; - imp_t* fimp = impulse; - for ( int n = res / 2 + 1; n--; ) - { - int error = unit; - for ( int nn = width; nn--; ) - { - long a = ((long) *fimp++ * unit + offset) >> impulse_bits; - error -= a - unit; - *imp++ = (imp_t) a; - } - - // add error to middle - imp [-width / 2 - 1] += (imp_t) error; - } - - if ( res > 2 ) - { - // second half is mirror-image - const imp_t* rev = imp - width - 1; - for ( int nn = (res / 2 - 1) * width - 1; nn--; ) - *imp++ = *--rev; - *imp++ = (imp_t) unit; - } - - // copy to odd offset - *imp++ = (imp_t) unit; - memcpy( imp, imp_in, (res * width - 1) * sizeof *imp ); - - /* - for ( int i = 0; i < res; i++ ) - { - for ( int j = 0; j < width; j++ ) - printf( "%6d,", imp_in [i * width + j] - 0x8000 ); - printf( "\n" ); - }*/ -} - -const int max_res = 1 << blip_res_bits_; - -void Blip_Impulse_::fine_volume_unit() -{ - // to do: find way of merging in-place without temporary buffer - - imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_]; - scale_impulse( (offset & 0xffff) << fine_bits, temp ); - imp_t* imp2 = impulses + res * 2 * width; - scale_impulse( offset & 0xffff, imp2 ); - - // merge impulses - imp_t* imp = impulses; - imp_t* src2 = temp; - for ( int n = res / 2 * 2 * width; n--; ) - { - *imp++ = *imp2++; - *imp++ = *imp2++; - *imp++ = *src2++; - *imp++ = *src2++; - } -} - -void Blip_Impulse_::volume_unit( double new_unit ) -{ - if ( new_unit == volume_unit_ ) - return; - - if ( generate ) - treble_eq( blip_eq_t( -8.87, 8800, 44100 ) ); - - volume_unit_ = new_unit; - - offset = 0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 ); - - if ( fine_bits ) - fine_volume_unit(); - else - scale_impulse( offset & 0xffff, impulses ); -} - -static const double pi = 3.1415926535897932384626433832795029L; - -void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq ) -{ - if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff && - new_eq.sample_rate == eq.sample_rate ) - return; // already calculated with same parameters - - generate = false; - eq = new_eq; - - double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50) - if ( treble < 0.000005 ) - treble = 0.000005; - - const double treble_freq = 22050.0; // treble level at 22 kHz harmonic - const double sample_rate = eq.sample_rate; - const double pt = treble_freq * 2 / sample_rate; - double cutoff = eq.cutoff * 2 / sample_rate; - if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) - { - cutoff = 0.5; - treble = 1.0; - } - - // DSF Synthesis (See T. Stilson & J. Smith (1996), - // Alias-free digital synthesis of classic analog waveforms) - - // reduce adjacent impulse interference by using small part of wide impulse - const double n_harm = 4096; - const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) ); - const double rescale = 1.0 / pow( rolloff, n_harm * cutoff ); - - const double pow_a_n = rescale * pow( rolloff, n_harm ); - const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff ); - - double total = 0.0; - const double to_angle = pi / 2 / n_harm / max_res; - - float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2]; - const int size = max_res * (width - 2) / 2; - for ( int i = size; i--; ) - { - double angle = (i * 2 + 1) * to_angle; - - // equivalent - //double y = dsf( angle, n_harm * cutoff, 1.0 ); - //y -= rescale * dsf( angle, n_harm * cutoff, rolloff ); - //y += rescale * dsf( angle, n_harm, rolloff ); - - const double cos_angle = cos( angle ); - const double cos_nc_angle = cos( n_harm * cutoff * angle ); - const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle ); - - double b = 2.0 - 2.0 * cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle); - double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) - - pow_a_n * cos( n_harm * angle ) - - pow_a_nc * rolloff * cos_nc1_angle + - pow_a_nc * cos_nc_angle; - - // optimization of a / b + c / d - double y = (a * d + c * b) / (b * d); - - // fixed window which affects wider impulses more - if ( width > 12 ) - { - double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle ); - y *= window * window; - } - - total += (float) y; - buf [i] = (float) y; - } - - // integrate runs of length 'max_res' - double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half - imp_t* imp = impulse; - const int step = max_res / res; - int offset = res > 1 ? max_res : max_res / 2; - for ( int n = res / 2 + 1; n--; offset -= step ) - { - for ( int w = -width / 2; w < width / 2; w++ ) - { - double sum = 0; - for ( int i = max_res; i--; ) - { - int index = w * max_res + offset + i; - if ( index < 0 ) - index = -index - 1; - if ( index < size ) - sum += buf [index]; - } - *imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) ); - } - } - - // rescale - double unit = volume_unit_; - if ( unit >= 0 ) - { - volume_unit_ = -1; - volume_unit( unit ); - } + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); } void Blip_Buffer::remove_samples( long count ) { - require( buffer_ ); // sample rate must have been set - - if ( !count ) // optimization - return; - - remove_silence( count ); - - // Allows synthesis slightly past time passed to end_frame(), as long as it's - // not more than an output sample. - // to do: kind of hacky, could add run_until() which keeps track of extra synthesis - int const copy_extra = 1; - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + widest_impulse_ + copy_extra; - if ( count >= remain ) - memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); - else - memcpy( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); - memset( buffer_ + remain, sample_offset_ & 0xFF, count * sizeof (buf_t_) ); + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } } -#include BLARGG_ENABLE_OPTIMIZER +// Blip_Synth_ -long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo ) +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +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; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double angle_maxh = angle * maxh; + double angle_maxh_mid = angle_maxh * cutoff; + + double y = maxh; + + // 0 to Fs/2*cutoff, flat + if ( angle_maxh_mid ) // unstable at t=0 + y *= sin( angle_maxh_mid ) / angle_maxh_mid; + + // Fs/2*cutoff to Fs/2, logarithmic rolloff + double cosa = cos( angle ); + double den = 1 + rolloff * (rolloff - cosa - cosa); + + // Becomes unstable when rolloff is near 1.0 and t is near 0, + // which is the only time den becomes small + if ( den > 1e-13 ) + { + double num = + (cos( angle_maxh - angle ) * rolloff - cos( angle_maxh )) * pow_a_n - + cos( angle_maxh_mid - angle ) * rolloff + cos( angle_maxh_mid ); + + y = y * cutoff + num / den; + } + + out [i] = (float) y; + } +} + +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 = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (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 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size = this->impulses_size(); + for ( i = 0; i < impulses_size; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / 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 ) + { + kernel_unit >>= shift; + assert( 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 = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) 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* BLIP_RESTRICT out, long max_samples, int stereo ) { - require( buffer_ ); // sample rate must have been set - long count = samples_avail(); if ( count > max_samples ) count = max_samples; - - if ( !count ) - return 0; // optimization - - int sample_offset_ = this->sample_offset_; - int bass_shift = this->bass_shift; - buf_t_* buf = buffer_; - long accum = reader_accum; - - if ( !stereo ) + + if ( count ) { - for ( long n = count; n--; ) + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) { - long s = accum >> accum_fract; - accum -= accum >> bass_shift; - accum += (long (*buf++) - sample_offset_) << accum_fract; - *out++ = (blip_sample_t) s; - - // clamp sample - if ( (BOOST::int16_t) s != s ) - out [-1] = blip_sample_t (0x7FFF - (s >> 24)); + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } } - } - else - { - for ( long n = count; n--; ) + else { - long s = accum >> accum_fract; - accum -= accum >> bass_shift; - accum += (long (*buf++) - sample_offset_) << accum_fract; - *out = (blip_sample_t) s; - out += 2; - - // clamp sample - if ( (BOOST::int16_t) s != s ) - out [-2] = blip_sample_t (0x7FFF - (s >> 24)); + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); } - - reader_accum = accum; - - remove_samples( count ); - return count; } -void Blip_Buffer::mix_samples( const blip_sample_t* in, long count ) +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) { - buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)]; - + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; int prev = 0; while ( count-- ) { - int s = *in++; - *buf += s - prev; + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; prev = s; - ++buf; + ++out; } - *buf -= *--in; + *out -= prev; } diff --git a/plugins/papu/gb_apu/Blip_Buffer.h b/plugins/papu/gb_apu/Blip_Buffer.h index ef5209b4b..4cc526d2f 100644 --- a/plugins/papu/gb_apu/Blip_Buffer.h +++ b/plugins/papu/gb_apu/Blip_Buffer.h @@ -1,259 +1,488 @@ +// Band-limited sound synthesis buffer -// Buffer of sound samples into which band-limited waveforms can be synthesized -// using Blip_Wave or Blip_Synth. - -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Blip_Buffer 0.4.1 #ifndef BLIP_BUFFER_H #define BLIP_BUFFER_H -#include "blargg_common.h" + // internal + #include + #if INT_MAX < 0x7FFFFFFF + #error "int must be at least 32 bits" + #endif + + typedef int blip_long; + typedef unsigned blip_ulong; -class Blip_Reader; +// Time unit at source clock rate +typedef blip_long blip_time_t; -// Source time unit. -typedef long blip_time_t; - -// Type of sample produced. Signed 16-bit format. -typedef BOOST::int16_t blip_sample_t; - -// Make buffer as large as possible (currently about 65000 samples) -const int blip_default_length = 0; - -typedef unsigned long blip_resampled_time_t; // not documented +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; class Blip_Buffer { public: - // Construct an empty buffer. - Blip_Buffer(); - ~Blip_Buffer(); + typedef const char* blargg_err_t; - // Set output sample rate and buffer length in milliseconds (1/1000 sec), - // then clear buffer. If length is not specified, make as large as possible. - // If there is insufficient memory for the buffer, sets the buffer length - // to 0 and returns error string (or propagates exception if compiler supports it). - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = blip_default_length ); + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; // Length of buffer, in milliseconds int length() const; - // Current output sample rate - long sample_rate() const; - // Number of source time units per second - void clock_rate( long ); long clock_rate() const; - // Set frequency at which high-pass filter attenuation passes -3dB + // Set frequency high-pass filter frequency, where higher values reduce bass more void bass_freq( int frequency ); - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clear out any samples waiting rather than the entire buffer. - void clear( bool entire_buffer = true ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begin - // a new time frame at the end of the current frame. All transitions must have - // been added before 'time'. - void end_frame( blip_time_t time ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Return number of samples actually read and removed. If stereo is - // true, increment 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false ); - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - // Number of samples delay from synthesis to samples read out int output_latency() const; -// Beta features + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); - // Number of raw samples that can be mixed within frame of specified duration - long count_samples( blip_time_t duration ) const; + // Number of samples available for reading with read_samples() + long samples_avail() const; - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( const blip_sample_t* buf, long count ); + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features // Count number of clocks needed until 'count' samples will be available. // If buffer can't even hold 'count' samples, returns number of clocks until - // buffer is full. + // buffer becomes full. blip_time_t count_clocks( long count ) const; + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); // not documented yet - + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_ulong blip_resampled_time_t; void remove_silence( long count ); - - blip_resampled_time_t resampled_time( blip_time_t t ) const - { - return t * blip_resampled_time_t (factor_) + offset_; - } - + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); - blip_resampled_time_t resampled_duration( int t ) const - { - return t * blip_resampled_time_t (factor_); - } - + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } private: // noncopyable Blip_Buffer( const Blip_Buffer& ); Blip_Buffer& operator = ( const Blip_Buffer& ); - - // Don't use the following members. They are public only for technical reasons. - public: - enum { sample_offset_ = 0x7F7F }; // repeated byte allows memset to clear buffer - enum { widest_impulse_ = 24 }; - typedef BOOST::uint16_t buf_t_; - - unsigned long factor_; - blip_resampled_time_t offset_; - buf_t_* buffer_; - unsigned buffer_size_; - private: - long reader_accum; - int bass_shift; - long samples_per_sec; - long clocks_per_sec; - int bass_freq_; - int length_; - - enum { accum_fract = 15 }; // less than 16 to give extra sample range - - friend class Blip_Reader; -}; - -// Low-pass equalization parameters (see notes.txt) -class blip_eq_t { public: - blip_eq_t( double treble = 0 ); - blip_eq_t( double treble, long cutoff, long sample_rate ); + typedef blip_time_t buf_t_; + blip_ulong factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; private: - double treble; - long cutoff; - long sample_rate; - friend class Blip_Impulse_; + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; }; -// not documented yet (see Multi_Buffer.cpp for an example of use) -class Blip_Reader { - const Blip_Buffer::buf_t_* buf; - long accum; - #ifdef __MWERKS__ - void operator = ( struct foobar ); // helps optimizer - #endif -public: - // avoid anything which might cause optimizer to put object in memory - - int begin( Blip_Buffer& blip_buf ) { - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum; - return blip_buf.bass_shift; - } - - int read() const { - return accum >> Blip_Buffer::accum_fract; - } - - void next( int bass_shift = 9 ) { - accum -= accum >> bass_shift; - accum += ((long) *buf++ - Blip_Buffer::sample_offset_) << Blip_Buffer::accum_fract; - } - - void end( Blip_Buffer& blip_buf ) { - blip_buf.reader_accum = accum; - } -}; +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif - - -// End of public interface - +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. #ifndef BLIP_BUFFER_ACCURACY #define BLIP_BUFFER_ACCURACY 16 #endif -const int blip_res_bits_ = 5; - -typedef BOOST::uint32_t blip_pair_t_; - -class Blip_Impulse_ { - typedef BOOST::uint16_t imp_t; - - blip_eq_t eq; - double volume_unit_; - imp_t* impulses; - imp_t* impulse; - int width; - int fine_bits; - int res; - bool generate; - - void fine_volume_unit(); - void scale_impulse( int unit, imp_t* ) const; -public: - Blip_Buffer* buf; - BOOST::uint32_t offset; - - void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 ); - void volume_unit( double ); - void treble_eq( const blip_eq_t& ); -}; - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), cutoff( 0 ), sample_rate( 44100 ) { -} - -inline blip_eq_t::blip_eq_t( double t, long c, long sr ) : - treble( t ), cutoff( c ), sample_rate( sr ) { -} - -inline int Blip_Buffer::length() const { - return length_; -} - -inline long Blip_Buffer::samples_avail() const { - return long (offset_ >> BLIP_BUFFER_ACCURACY); -} - -inline long Blip_Buffer::sample_rate() const { - return samples_per_sec; -} - -inline void Blip_Buffer::end_frame( blip_time_t t ) { - offset_ += t * factor_; -/* assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer", - samples_avail() <= (long) buffer_size_ ));*/ -} - -inline void Blip_Buffer::remove_silence( long count ) { -/* assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available", - count <= samples_avail() ));*/ - offset_ -= blip_resampled_time_t (count) << BLIP_BUFFER_ACCURACY; -} - -inline int Blip_Buffer::output_latency() const { - return widest_impulse_ / 2; -} - -inline long Blip_Buffer::clock_rate() const { - return clocks_per_sec; -} - -inline void Blip_Buffer::clock_rate( long cps ) -{ - clocks_per_sec = cps; - factor_ = clock_rate_factor( cps ); -} - -#include "Blip_Synth.h" - +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #if BLIP_BUFFER_FAST + #define BLIP_PHASE_BITS 8 + #else + #define BLIP_PHASE_BITS 6 + #endif #endif + // Internal + typedef blip_ulong blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +// End of public interface + +#include + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +inline int Blip_Buffer::length() const { return length_; } +inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif diff --git a/plugins/papu/gb_apu/Blip_Synth.h b/plugins/papu/gb_apu/Blip_Synth.h deleted file mode 100644 index 170e22459..000000000 --- a/plugins/papu/gb_apu/Blip_Synth.h +++ /dev/null @@ -1,208 +0,0 @@ - -// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding -// waveforms to a Blip_Buffer. - -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_SYNTH_H -#define BLIP_SYNTH_H - -#ifndef BLIP_BUFFER_H - #include "Blip_Buffer.h" -#endif - -// Quality level. Higher levels are slower, and worse in a few cases. -// Use blip_good_quality as a starting point. -const int blip_low_quality = 1; -const int blip_med_quality = 2; -const int blip_good_quality = 3; -const int blip_high_quality = 4; - -// Blip_Synth is a transition waveform synthesizer which adds band-limited -// offsets (transitions) into a Blip_Buffer. For a simpler interface, use -// Blip_Wave (below). -// -// Range specifies the greatest expected offset that will occur. For a -// waveform that goes between +amp and -amp, range should be amp * 2 (half -// that if it only goes between +amp and 0). When range is large, a higher -// accuracy scheme is used; to force this even when range is small, pass -// the negative of range (i.e. -range). -template -class Blip_Synth { - BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); - BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 ); - enum { - abs_range = (range < 0) ? -range : range, - fine_mode = (range > 512 || range < 0), - width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_), - res = 1 << blip_res_bits_, - impulse_size = width / 2 * (fine_mode + 1), - base_impulses_size = width / 2 * (res / 2 + 1), - fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : - abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : - abs_range <= 2048 ? 7 : 8) : 0) - }; - blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; - Blip_Impulse_ impulse; - void init() { impulse.init( impulses, width, res, fine_bits ); } -public: - Blip_Synth() { init(); } - Blip_Synth( double volume ) { init(); this->volume( volume ); } - - // Configure low-pass filter (see notes.txt). Not optimized for real-time control - void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } - - // Set volume of a transition at amplitude 'range' by setting volume_unit - // to v / range - void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } - - // Set base volume unit of transitions, where 1.0 is a full swing between the - // positive and negative extremes. Not optimized for real-time control. - void volume_unit( double unit ) { impulse.volume_unit( unit ); } - - // Default Blip_Buffer used for output when none is specified for a given call - Blip_Buffer* output() const { return impulse.buf; } - void output( Blip_Buffer* b ) { impulse.buf = b; } - - // Add an amplitude offset (transition) with a magnitude of delta * volume_unit - // into the specified buffer (default buffer if none specified) at the - // specified source time. Delta can be positive or negative. To increase - // performance by inlining code at the call site, use offset_inline(). - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - void offset_resampled( blip_resampled_time_t t, int o ) const { - offset_resampled( t, o, impulse.buf ); - } - void offset( blip_time_t t, int delta ) const { - offset( t, delta, impulse.buf ); - } - void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t time, int delta ) const { - offset_inline( time, delta, impulse.buf ); - } -}; - -// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. -// A wave is built from a series of delays and new amplitudes. This provides a -// simpler interface than Blip_Synth, nothing more. -template -class Blip_Wave { - Blip_Synth synth; - blip_time_t time_; - int last_amp; - void init() { time_ = 0; last_amp = 0; } -public: - // Start wave at time 0 and amplitude 0 - Blip_Wave() { init(); } - Blip_Wave( double volume ) { init(); this->volume( volume ); } - - // See Blip_Synth for description - void volume( double v ) { synth.volume( v ); } - void volume_unit( double v ) { synth.volume_unit( v ); } - void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); } - Blip_Buffer* output() const { return synth.output(); } - void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; } - - // Current time in frame - blip_time_t time() const { return time_; } - void time( blip_time_t t ) { time_ = t; } - - // Current amplitude of wave - int amplitude() const { return last_amp; } - void amplitude( int ); - - // Move forward by 't' time units - void delay( blip_time_t t ) { time_ += t; } - - // End time frame of specified duration. Localize time to new frame. - // If wave hadn't been run to end of frame, start it at beginning of new frame. - void end_frame( blip_time_t duration ) - { - time_ -= duration; - if ( time_ < 0 ) - time_ = 0; - } -}; - -// End of public interface - -template -void Blip_Wave::amplitude( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - synth.offset_inline( time_, delta ); -} - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - typedef blip_pair_t_ pair_t; - - unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1; -/* assert(( "Blip_Synth/Blip_wave: Went past end of buffer", - sample_index < blip_buf->buffer_size_ ));*/ - enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; - pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; - - enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; - enum { mask = res * 2 - 1 }; - const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; - - pair_t offset = impulse.offset * delta; - - if ( !fine_bits ) - { - // normal mode - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta; - t1 += imp [1] * delta; - imp += 2; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } - else - { - // fine mode - enum { sub_range = 1 << fine_bits }; - delta += sub_range / 2; - int delta2 = (delta & (sub_range - 1)) - sub_range / 2; - delta >>= fine_bits; - - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta2; - t0 += imp [1] * delta; - - t1 += imp [2] * delta2; - t1 += imp [3] * delta; - - imp += 4; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } -} - -template -void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); -} - -#endif - diff --git a/plugins/papu/gb_apu/Gb_Apu.cpp b/plugins/papu/gb_apu/Gb_Apu.cpp index 6039b043b..866594ddf 100644 --- a/plugins/papu/gb_apu/Gb_Apu.cpp +++ b/plugins/papu/gb_apu/Gb_Apu.cpp @@ -1,28 +1,29 @@ - -// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ #include "Gb_Apu.h" #include -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* Copyright (C) 2003-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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 */ -#include BLARGG_SOURCE_BEGIN +#include "blargg_source.h" + +unsigned const vol_reg = 0xFF24; +unsigned const status_reg = 0xFF26; Gb_Apu::Gb_Apu() { square1.synth = &square_synth; square2.synth = &square_synth; - square1.has_sweep = true; wave.synth = &other_synth; noise.synth = &other_synth; @@ -31,25 +32,37 @@ Gb_Apu::Gb_Apu() oscs [2] = &wave; oscs [3] = &noise; + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + osc.regs = ®s [i * 5]; + osc.output = 0; + osc.outputs [0] = 0; + osc.outputs [1] = 0; + osc.outputs [2] = 0; + osc.outputs [3] = 0; + } + + set_tempo( 1.0 ); volume( 1.0 ); reset(); } -Gb_Apu::~Gb_Apu() -{ -} - void Gb_Apu::treble_eq( const blip_eq_t& eq ) { square_synth.treble_eq( eq ); other_synth.treble_eq( eq ); } -void Gb_Apu::volume( double vol ) +void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - vol *= 0.60 / osc_count; - square_synth.volume( vol ); - other_synth.volume( vol ); + require( (unsigned) index < osc_count ); + require( (center && left && right) || (!center && !left && !right) ); + Gb_Osc& osc = *oscs [index]; + osc.outputs [1] = right; + osc.outputs [2] = left; + osc.outputs [3] = center; + osc.output = osc.outputs [osc.output_select]; } void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) @@ -58,44 +71,62 @@ void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right osc_output( i, center, left, right ); } +void Gb_Apu::update_volume() +{ + // TODO: doesn't handle differing left/right global volume (support would + // require modification to all oscillator code) + int data = regs [vol_reg - start_addr]; + double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; + square_synth.volume( vol ); + other_synth.volume( vol ); +} + +static unsigned char const powerup_regs [0x20] = { + 0x80,0x3F,0x00,0xFF,0xBF, // square 1 + 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 + 0x7F,0xFF,0x9F,0xFF,0xBF, // wave + 0xFF,0xFF,0x00,0x00,0xBF, // noise + 0x00, // left/right enables + 0x77, // master volume + 0x80, // power + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 256; // 256 Hz + if ( t != 1.0 ) + frame_period = blip_time_t (frame_period / t); +} + void Gb_Apu::reset() { next_frame_time = 0; - last_time = 0; - frame_count = 0; - stereo_found = false; + last_time = 0; + frame_count = 0; square1.reset(); square2.reset(); wave.reset(); noise.reset(); + noise.bits = 1; + wave.wave_pos = 0; - memset( regs, 0, sizeof regs ); + // avoid click at beginning + regs [vol_reg - start_addr] = 0x77; + update_volume(); + + regs [status_reg - start_addr] = 0x01; // force power + write_register( 0, status_reg, 0x00 ); + + static unsigned char const initial_wave [] = { + 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table + 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA + }; + memcpy( wave.wave, initial_wave, sizeof wave.wave ); } -void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - - Gb_Osc& osc = *oscs [index]; - if ( center && !left && !right ) - { - // mono - left = center; - right = center; - } - else - { - // must be silenced or stereo - require( (!left && !right) || (left && right) ); - } - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; -} - -void Gb_Apu::run_until( gb_time_t end_time ) +void Gb_Apu::run_until( blip_time_t end_time ) { require( end_time >= last_time ); // end_time must not be before previous time if ( end_time == last_time ) @@ -103,17 +134,28 @@ void Gb_Apu::run_until( gb_time_t end_time ) while ( true ) { - gb_time_t time = next_frame_time; + blip_time_t time = next_frame_time; if ( time > end_time ) time = end_time; // run oscillators - for ( int i = 0; i < osc_count; ++i ) { + for ( int i = 0; i < osc_count; ++i ) + { Gb_Osc& osc = *oscs [i]; - if ( osc.output ) { - if ( osc.output != osc.outputs [3] ) - stereo_found = true; - osc.run( last_time, time ); + if ( osc.output ) + { + osc.output->set_modified(); // TODO: misses optimization opportunities? + int playing = false; + if ( osc.enabled && osc.volume && + (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) + playing = -1; + switch ( i ) + { + case 0: square1.run( last_time, time, playing ); break; + case 1: square2.run( last_time, time, playing ); break; + case 2: wave .run( last_time, time, playing ); break; + case 3: noise .run( last_time, time, playing ); break; + } } } last_time = time; @@ -121,7 +163,7 @@ void Gb_Apu::run_until( gb_time_t end_time ) if ( time == end_time ) break; - next_frame_time += 4194304 / 256; // 256 Hz + next_frame_time += frame_period; // 256 Hz actions square1.clock_length(); @@ -130,7 +172,8 @@ void Gb_Apu::run_until( gb_time_t end_time ) noise.clock_length(); frame_count = (frame_count + 1) & 3; - if ( frame_count == 0 ) { + if ( frame_count == 0 ) + { // 64 Hz actions square1.clock_envelope(); square2.clock_envelope(); @@ -142,7 +185,7 @@ void Gb_Apu::run_until( gb_time_t end_time ) } } -bool Gb_Apu::end_frame( gb_time_t end_time ) +void Gb_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until( end_time ); @@ -152,13 +195,9 @@ bool Gb_Apu::end_frame( gb_time_t end_time ) assert( last_time >= end_time ); last_time -= end_time; - - bool result = stereo_found; - stereo_found = false; - return result; } -void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) +void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) { require( (unsigned) data < 0x100 ); @@ -168,48 +207,39 @@ void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) run_until( time ); + int old_reg = regs [reg]; regs [reg] = data; - if ( addr < 0xff24 ) + if ( addr < vol_reg ) { - // oscillator - int index = reg / 5; - oscs [index]->write_register( reg - index * 5, data ); + write_osc( reg / 5, reg, data ); } - // added - else if ( addr == 0xff24 ) + else if ( addr == vol_reg && data != old_reg ) // global volume { - int global_volume = data & 7; - int old_volume = square1.global_volume; - if ( old_volume != global_volume ) + // return all oscs to 0 + for ( int i = 0; i < osc_count; i++ ) { - int any_enabled = false; - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - if ( osc.enabled ) - { - if ( osc.last_amp ) - { - int new_amp = osc.last_amp * global_volume / osc.global_volume; - if ( osc.output ) - square_synth.offset( time, new_amp - osc.last_amp, osc.output ); - osc.last_amp = new_amp; - } - any_enabled |= osc.volume; - } - osc.global_volume = global_volume; - } - - if ( !any_enabled && square1.outputs [3] ) - square_synth.offset( time, (global_volume - old_volume) * 15 * 2, square1.outputs [3] ); + Gb_Osc& osc = *oscs [i]; + int amp = osc.last_amp; + osc.last_amp = 0; + if ( amp && osc.enabled && osc.output ) + other_synth.offset( time, -amp, osc.output ); } + + if ( wave.outputs [3] ) + other_synth.offset( time, 30, wave.outputs [3] ); + + update_volume(); + + if ( wave.outputs [3] ) + other_synth.offset( time, -30, wave.outputs [3] ); + + // oscs will update with new amplitude when next run } - - else if ( addr == 0xff25 || addr == 0xff26 ) + else if ( addr == 0xFF25 || addr == status_reg ) { - int mask = (regs [0xff26 - start_addr] & 0x80) ? ~0 : 0; - int flags = regs [0xff25 - start_addr] & mask; + int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; + int flags = regs [0xFF25 - start_addr] & mask; // left/right assignments for ( int i = 0; i < osc_count; i++ ) @@ -220,42 +250,57 @@ void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) Blip_Buffer* old_output = osc.output; osc.output_select = (bits >> 3 & 2) | (bits & 1); osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output && osc.last_amp ) + if ( osc.output != old_output ) { - if ( old_output ) - square_synth.offset( time, -osc.last_amp, old_output ); + int amp = osc.last_amp; osc.last_amp = 0; + if ( amp && old_output ) + other_synth.offset( time, -amp, old_output ); + } + } + + if ( addr == status_reg && data != old_reg ) + { + if ( !(data & 0x80) ) + { + for ( unsigned i = 0; i < sizeof powerup_regs; i++ ) + { + if ( i != status_reg - start_addr ) + write_register( time, i + start_addr, powerup_regs [i] ); + } + } + else + { + //debug_printf( "APU powered on\n" ); } } } - else if ( addr >= 0xff30 ) + else if ( addr >= 0xFF30 ) { - int index = (addr & 0x0f) * 2; + int index = (addr & 0x0F) * 2; wave.wave [index] = data >> 4; - wave.wave [index + 1] = data & 0x0f; + wave.wave [index + 1] = data & 0x0F; } } -int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr ) +int Gb_Apu::read_register( blip_time_t time, unsigned addr ) { - // function now takes actual address, i.e. 0xFFXX - require( start_addr <= addr && addr < end_addr ); - run_until( time ); - int data = regs [addr - start_addr]; + int index = addr - start_addr; + require( (unsigned) index < register_count ); + int data = regs [index]; - if ( addr == 0xff26 ) + if ( addr == status_reg ) { - data &= 0xf0; + data = (data & 0x80) | 0x70; for ( int i = 0; i < osc_count; i++ ) { const Gb_Osc& osc = *oscs [i]; - if ( osc.enabled && (osc.length || !osc.length_enabled) ) + if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) data |= 1 << i; } } return data; } - diff --git a/plugins/papu/gb_apu/Gb_Apu.h b/plugins/papu/gb_apu/Gb_Apu.h index e2f940b94..e74ebc55b 100644 --- a/plugins/papu/gb_apu/Gb_Apu.h +++ b/plugins/papu/gb_apu/Gb_Apu.h @@ -1,20 +1,13 @@ - // Nintendo Game Boy PAPU sound chip emulator -// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Gb_Snd_Emu 0.1.5 #ifndef GB_APU_H #define GB_APU_H -typedef long gb_time_t; // clock cycle count -typedef unsigned gb_addr_t; // 16-bit address - #include "Gb_Oscs.h" class Gb_Apu { public: - Gb_Apu(); - ~Gb_Apu(); // Set overall volume of all oscillators, where 1.0 is full volume void volume( double ); @@ -22,63 +15,76 @@ public: // Set treble equalization void treble_eq( const blip_eq_t& ); - // Reset oscillators and internal state - void reset(); + // Outputs can be assigned to a single buffer for mono output, or to three + // buffers for stereo output (using Stereo_Buffer to do the mixing). // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silence all oscillators. + // is NULL, silences all oscillators. void output( Blip_Buffer* mono ); void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Wave, and Noise. - // If buffer is NULL, silence oscillator. + // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, + // silences oscillator. enum { osc_count = 4 }; void osc_output( int index, Blip_Buffer* mono ); void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + // Reset oscillators and internal state + void reset(); + // Reads and writes at addr must satisfy start_addr <= addr <= end_addr - enum { start_addr = 0xff10 }; - enum { end_addr = 0xff3f }; + enum { start_addr = 0xFF10 }; + enum { end_addr = 0xFF3F }; enum { register_count = end_addr - start_addr + 1 }; // Write 'data' to address at specified time - void write_register( gb_time_t, gb_addr_t, int data ); + void write_register( blip_time_t, unsigned addr, int data ); // Read from address at specified time - int read_register( gb_time_t, gb_addr_t ); + int read_register( blip_time_t, unsigned addr ); // Run all oscillators up to specified time, end current time frame, then - // start a new frame at time 0. Return true if any oscillators added - // sound to one of the left/right buffers, false if they only added - // to the center buffer. - bool end_frame( gb_time_t ); + // start a new frame at time 0. + void end_frame( blip_time_t ); + void set_tempo( double ); + +public: + Gb_Apu(); private: // noncopyable Gb_Apu( const Gb_Apu& ); Gb_Apu& operator = ( const Gb_Apu& ); Gb_Osc* oscs [osc_count]; - gb_time_t next_frame_time; - gb_time_t last_time; + blip_time_t next_frame_time; + blip_time_t last_time; + blip_time_t frame_period; + double volume_unit; int frame_count; - bool stereo_found; Gb_Square square1; Gb_Square square2; Gb_Wave wave; Gb_Noise noise; BOOST::uint8_t regs [register_count]; - Gb_Square::Synth square_synth; // shared between squares - Gb_Wave::Synth other_synth; // shared between wave and noise + Gb_Square::Synth square_synth; // used by squares + Gb_Wave::Synth other_synth; // used by wave and noise - void run_until( gb_time_t ); + void update_volume(); + void run_until( blip_time_t ); + void write_osc( int index, int reg, int data ); }; -inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, NULL, NULL ); } +inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } -inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, NULL, NULL ); } +inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } + +inline void Gb_Apu::volume( double vol ) +{ + volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; + update_volume(); +} #endif - diff --git a/plugins/papu/gb_apu/Gb_Oscs.cpp b/plugins/papu/gb_apu/Gb_Oscs.cpp index bf8d72fcc..735653fa9 100644 --- a/plugins/papu/gb_apu/Gb_Oscs.cpp +++ b/plugins/papu/gb_apu/Gb_Oscs.cpp @@ -1,144 +1,100 @@ - -// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ #include "Gb_Apu.h" #include -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* Copyright (C) 2003-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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 */ -#include BLARGG_SOURCE_BEGIN - -const int trigger = 0x80; +#include "blargg_source.h" // Gb_Osc -Gb_Osc::Gb_Osc() -{ - output = NULL; - outputs [0] = NULL; - outputs [1] = NULL; - outputs [2] = NULL; - outputs [3] = NULL; -} - void Gb_Osc::reset() { delay = 0; last_amp = 0; - period = 2048; - volume = 0; - global_volume = 7; // added - frequency = 0; length = 0; - enabled = false; - length_enabled = false; output_select = 3; output = outputs [output_select]; } void Gb_Osc::clock_length() { - if ( length_enabled && length ) - --length; -} - -void Gb_Osc::write_register( int reg, int value ) -{ - if ( reg == 4 ) - length_enabled = value & 0x40; + if ( (regs [4] & len_enabled_mask) && length ) + length--; } // Gb_Env -void Gb_Env::reset() -{ - env_period = 0; - env_dir = 0; - env_delay = 0; - new_volume = 0; - Gb_Osc::reset(); -} - -Gb_Env::Gb_Env() -{ -} - void Gb_Env::clock_envelope() { if ( env_delay && !--env_delay ) { - env_delay = env_period; - if ( env_dir ) - { - if ( volume < 15 ) - ++volume; - } - else if ( volume > 0 ) - { - --volume; - } + env_delay = regs [2] & 7; + int v = volume - 1 + (regs [2] >> 2 & 2); + if ( (unsigned) v < 15 ) + volume = v; } } -void Gb_Env::write_register( int reg, int value ) +bool Gb_Env::write_register( int reg, int data ) { - if ( reg == 2 ) { - env_period = value & 7; - env_dir = value & 8; - volume = new_volume = value >> 4; + switch ( reg ) + { + case 1: + length = 64 - (regs [1] & 0x3F); + break; + + case 2: + if ( !(data >> 4) ) + enabled = false; + break; + + case 4: + if ( data & trigger ) + { + env_delay = regs [2] & 7; + volume = regs [2] >> 4; + enabled = true; + if ( length == 0 ) + length = 64; + return true; + } } - else if ( reg == 4 && (value & trigger) ) { - env_delay = env_period; - volume = new_volume; - enabled = true; - } - Gb_Osc::write_register( reg, value ); + return false; } // Gb_Square void Gb_Square::reset() { - phase = 1; - duty = 1; - - sweep_period = 0; - sweep_delay = 0; - sweep_shift = 0; - sweep_dir = 0; + phase = 0; sweep_freq = 0; - - new_length = 0; - + sweep_delay = 0; Gb_Env::reset(); } -Gb_Square::Gb_Square() -{ - has_sweep = false; -} - void Gb_Square::clock_sweep() { + int sweep_period = (regs [0] & period_mask) >> 4; if ( sweep_period && sweep_delay && !--sweep_delay ) { sweep_delay = sweep_period; - frequency = sweep_freq; + regs [3] = sweep_freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); - period = (2048 - frequency) * 4; - - int offset = sweep_freq >> sweep_shift; - if ( sweep_dir ) + int offset = sweep_freq >> (regs [0] & shift_mask); + if ( regs [0] & 0x08 ) offset = -offset; sweep_freq += offset; @@ -148,304 +104,233 @@ void Gb_Square::clock_sweep() } else if ( sweep_freq >= 2048 ) { - sweep_delay = 0; - sweep_freq = 2048; // stop sound output + sweep_delay = 0; // don't modify channel frequency any further + sweep_freq = 2048; // silence sound immediately } } } -void Gb_Square::write_register( int reg, int value ) +void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) { - static unsigned char const duty_table [4] = { 1, 2, 4, 6 }; + if ( sweep_freq == 2048 ) + playing = false; - switch ( reg ) + static unsigned char const table [4] = { 1, 2, 4, 6 }; + int const duty = table [regs [1] >> 6]; + int amp = volume & playing; + if ( phase >= duty ) + amp = -amp; + + int frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 { - case 0: - sweep_period = (value >> 4) & 7; // changed - sweep_shift = value & 7; - sweep_dir = value & 0x08; - break; - - case 1: - new_length = length = 64 - (value & 0x3f); - duty = duty_table [value >> 6]; - break; - - case 3: - frequency = (frequency & ~0xFF) + value; - length = new_length; - break; - - case 4: - frequency = (value & 7) * 0x100 + (frequency & 0xFF); - length = new_length; - if ( value & trigger ) - { - sweep_freq = frequency; - if ( has_sweep && sweep_period && sweep_shift ) - { - sweep_delay = 1; - clock_sweep(); - } - } - break; + // really high frequency results in DC at half volume + amp = volume >> 1; + playing = false; } - period = (2048 - frequency) * 4; - - Gb_Env::write_register( reg, value ); -} - -void Gb_Square::run( gb_time_t time, gb_time_t end_time ) -{ - // to do: when frequency goes above 20000 Hz output should actually be 1/2 volume - // rather than 0 - - if ( !enabled || (!length && length_enabled) || !volume || sweep_freq == 2048 || - !frequency || period < 27 ) { - if ( last_amp ) + int delta = amp - last_amp; + if ( delta ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - delay = 0; - } - else - { - int amp = (phase < duty) ? volume : -volume; - amp *= global_volume; - if ( amp != last_amp ) - { - synth->offset( time, amp - last_amp, output ); last_amp = amp; + synth->offset( time, delta, output ); } - - time += delay; - if ( time < end_time ) + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + int const period = (2048 - frequency) * 4; + Blip_Buffer* const output = this->output; + int phase = this->phase; + int delta = amp * 2; + do { - Blip_Buffer* const output = this->output; - const int duty = this->duty; - int phase = this->phase; - amp *= 2; - do + phase = (phase + 1) & 7; + if ( phase == 0 || phase == duty ) { - phase = (phase + 1) & 7; - if ( phase == 0 || phase == duty ) - { - amp = -amp; - synth->offset_inline( time, amp, output ); - } - time += period; + delta = -delta; + synth->offset_inline( time, delta, output ); } - while ( time < end_time ); - - this->phase = phase; - last_amp = amp >> 1; + time += period; } - delay = time - end_time; - } -} - - -// Gb_Wave - -void Gb_Wave::reset() -{ - volume_shift = 0; - wave_pos = 0; - new_length = 0; - memset( wave, 0, sizeof wave ); - Gb_Osc::reset(); -} - -Gb_Wave::Gb_Wave() { -} - -void Gb_Wave::write_register( int reg, int value ) -{ - switch ( reg ) - { - case 0: - new_enabled = value & 0x80; - enabled &= new_enabled; - break; - - case 1: - new_length = length = 256 - value; - break; - - case 2: - volume = ((value >> 5) & 3); - volume_shift = (volume - 1) & 7; // silence = 7 - break; - - case 3: - frequency = (frequency & ~0xFF) + value; - break; - - case 4: - frequency = (value & 7) * 0x100 + (frequency & 0xFF); - if ( new_enabled && (value & trigger) ) - { - wave_pos = 0; - length = new_length; - enabled = true; - } - break; - } - - period = (2048 - frequency) * 2; - - Gb_Osc::write_register( reg, value ); -} - -void Gb_Wave::run( gb_time_t time, gb_time_t end_time ) -{ - // to do: when frequency goes above 20000 Hz output should actually be 1/2 volume - // rather than 0 - if ( !enabled || (!length && length_enabled) || !volume || !frequency || period < 7 ) - { - if ( last_amp ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - delay = 0; - } - else - { - int const vol_factor = global_volume * 2; + while ( time < end_time ); - // wave data or shift may have changed - int diff = (wave [wave_pos] >> volume_shift) * vol_factor - last_amp; - if ( diff ) - { - last_amp += diff; - synth->offset( time, diff, output ); - } - - time += delay; - if ( time < end_time ) - { - int const volume_shift = this->volume_shift; - int wave_pos = this->wave_pos; - - do - { - wave_pos = unsigned (wave_pos + 1) % wave_size; - int amp = (wave [wave_pos] >> volume_shift) * vol_factor; - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->wave_pos = wave_pos; - } - delay = time - end_time; + this->phase = phase; + last_amp = delta >> 1; } + delay = time - end_time; } - // Gb_Noise -void Gb_Noise::reset() +void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) { - bits = 1; - tap = 14; - Gb_Env::reset(); -} - -Gb_Noise::Gb_Noise() { -} - -void Gb_Noise::write_register( int reg, int value ) -{ - if ( reg == 1 ) { - new_length = length = 64 - (value & 0x3f); - } - else if ( reg == 2 ) { - // based on VBA code, noise is the only exception to the envelope code - // while the volume level here is applied when the channel is enabled, - // current volume is only affected by writes to this register if volume - // is zero and direction is up... (definitely needs verification) - int temp = volume; - Gb_Env::write_register( reg, value ); - if ( ( value & 0xF8 ) != 0 ) volume = temp; - return; - } - else if ( reg == 3 ) { - tap = 14 - (value & 8); - // noise formula and frequency tested against Metroid 2 and Zelda LA - int divisor = (value & 7) * 16; - if ( !divisor ) - divisor = 8; - period = divisor << (value >> 4); - } - else if ( reg == 4 && value & trigger ) { - bits = ~0u; - length = new_length; + int amp = volume & playing; + int tap = 13 - (regs [3] & 8); + if ( bits >> tap & 2 ) + amp = -amp; + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } } - Gb_Env::write_register( reg, value ); + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; + int period = table [regs [3] & 7] << (regs [3] >> 4); + + // keep parallel resampled time to eliminate time conversion in the loop + Blip_Buffer* const output = this->output; + const blip_resampled_time_t resampled_period = + output->resampled_duration( period ); + blip_resampled_time_t resampled_time = output->resampled_time( time ); + unsigned bits = this->bits; + int delta = amp * 2; + + do + { + unsigned changed = (bits >> tap) + 1; + time += period; + bits <<= 1; + if ( changed & 2 ) + { + delta = -delta; + bits |= 1; + synth->offset_resampled( resampled_time, delta, output ); + } + resampled_time += resampled_period; + } + while ( time < end_time ); + + this->bits = bits; + last_amp = delta >> 1; + } + delay = time - end_time; } -#include BLARGG_ENABLE_OPTIMIZER +// Gb_Wave -void Gb_Noise::run( gb_time_t time, gb_time_t end_time ) +inline void Gb_Wave::write_register( int reg, int data ) { - if ( !enabled || (!length && length_enabled) || !volume ) { - if ( last_amp ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - delay = 0; - } - else + switch ( reg ) { - int amp = bits & 1 ? -volume : volume; - amp *= global_volume; - if ( amp != last_amp ) { - synth->offset( time, amp - last_amp, output ); - last_amp = amp; + case 0: + if ( !(data & 0x80) ) + enabled = false; + break; + + case 1: + length = 256 - regs [1]; + break; + + case 2: + volume = data >> 5 & 3; + break; + + case 4: + if ( data & trigger & regs [0] ) + { + wave_pos = 0; + enabled = true; + if ( length == 0 ) + length = 256; + } + } +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 + int frequency; + { + int amp = (wave [wave_pos] >> volume_shift & playing) * 2; + + frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 + { + amp = 30 >> volume_shift & playing; + playing = false; } - time += delay; - if ( time < end_time ) + int delta = amp - last_amp; + if ( delta ) { - Blip_Buffer* const output = this->output; - // keep parallel resampled time to eliminate multiplication in the loop - const blip_resampled_time_t resampled_period = - output->resampled_duration( period ); - blip_resampled_time_t resampled_time = output->resampled_time( time ); - const unsigned mask = ~(1u << tap); - unsigned bits = this->bits; - amp *= 2; - - do { - unsigned feedback = bits; - bits >>= 1; - feedback = 1 & (feedback ^ bits); - time += period; - bits = (feedback << tap) | (bits & mask); - // feedback just happens to be true only when the level needs to change - // (the previous and current bits are different) - if ( feedback ) { - amp = -amp; - synth->offset_resampled( resampled_time, amp, output ); - } - resampled_time += resampled_period; - } - while ( time < end_time ); - - this->bits = bits; - last_amp = amp >> 1; + last_amp = amp; + synth->offset( time, delta, output ); } - delay = time - end_time; } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + int const period = (2048 - frequency) * 2; + int wave_pos = (this->wave_pos + 1) & (wave_size - 1); + + do + { + int amp = (wave [wave_pos] >> volume_shift) * 2; + wave_pos = (wave_pos + 1) & (wave_size - 1); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->wave_pos = (wave_pos - 1) & (wave_size - 1); + } + delay = time - end_time; } +// Gb_Apu::write_osc + +void Gb_Apu::write_osc( int index, int reg, int data ) +{ + reg -= index * 5; + Gb_Square* sq = &square2; + switch ( index ) + { + case 0: + sq = &square1; + case 1: + if ( sq->write_register( reg, data ) && index == 0 ) + { + square1.sweep_freq = square1.frequency(); + if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) + { + square1.sweep_delay = 1; // cause sweep to recalculate now + square1.clock_sweep(); + } + } + break; + + case 2: + wave.write_register( reg, data ); + break; + + case 3: + if ( noise.write_register( reg, data ) ) + noise.bits = 0x7FFF; + } +} diff --git a/plugins/papu/gb_apu/Gb_Oscs.h b/plugins/papu/gb_apu/Gb_Oscs.h index 0c0092a8d..d7f88ea14 100644 --- a/plugins/papu/gb_apu/Gb_Oscs.h +++ b/plugins/papu/gb_apu/Gb_Oscs.h @@ -1,100 +1,83 @@ - // Private oscillators used by Gb_Apu -// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Gb_Snd_Emu 0.1.5 #ifndef GB_OSCS_H #define GB_OSCS_H +#include "blargg_common.h" #include "Blip_Buffer.h" -enum { gb_apu_max_vol = 7 }; - -struct Gb_Osc { +struct Gb_Osc +{ + enum { trigger = 0x80 }; + enum { len_enabled_mask = 0x40 }; + Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* output; int output_select; + BOOST::uint8_t* regs; // osc's 5 registers int delay; int last_amp; - int period; int volume; - int global_volume; - int frequency; int length; - int new_length; - bool enabled; - bool length_enabled; + int enabled; - Gb_Osc(); - - void clock_length(); void reset(); - virtual void run( gb_time_t begin, gb_time_t end ) = 0; - virtual void write_register( int reg, int value ); + void clock_length(); + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } }; -struct Gb_Env : Gb_Osc { - int env_period; - int env_dir; +struct Gb_Env : Gb_Osc +{ int env_delay; - int new_volume; - Gb_Env(); void reset(); void clock_envelope(); - void write_register( int, int ); + bool write_register( int, int ); }; -struct Gb_Square : Gb_Env { - int phase; - int duty; +struct Gb_Square : Gb_Env +{ + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; - int sweep_period; + typedef Blip_Synth Synth; + Synth const* synth; int sweep_delay; - int sweep_shift; - int sweep_dir; int sweep_freq; - bool has_sweep; + int phase; - typedef Blip_Synth Synth; - const Synth* synth; - - Gb_Square(); void reset(); - void run( gb_time_t, gb_time_t ); - void write_register( int, int ); void clock_sweep(); + void run( blip_time_t, blip_time_t, int playing ); }; -struct Gb_Wave : Gb_Osc { - int volume_shift; - unsigned wave_pos; +struct Gb_Noise : Gb_Env +{ + typedef Blip_Synth Synth; + Synth const* synth; + unsigned bits; + + void run( blip_time_t, blip_time_t, int playing ); +}; + +struct Gb_Wave : Gb_Osc +{ + typedef Blip_Synth Synth; + Synth const* synth; + int wave_pos; enum { wave_size = 32 }; - bool new_enabled; BOOST::uint8_t wave [wave_size]; - typedef Blip_Synth Synth; - const Synth* synth; - - Gb_Wave(); - void reset(); - void run( gb_time_t, gb_time_t ); void write_register( int, int ); + void run( blip_time_t, blip_time_t, int playing ); }; -struct Gb_Noise : Gb_Env { - unsigned bits; - int tap; - - typedef Blip_Synth Synth; - const Synth* synth; - - Gb_Noise(); - void reset(); - void run( gb_time_t, gb_time_t ); - void write_register( int, int ); -}; +inline void Gb_Env::reset() +{ + env_delay = 0; + Gb_Osc::reset(); +} #endif - diff --git a/plugins/papu/gb_apu/Multi_Buffer.cpp b/plugins/papu/gb_apu/Multi_Buffer.cpp index e4383cde8..57f93b317 100644 --- a/plugins/papu/gb_apu/Multi_Buffer.cpp +++ b/plugins/papu/gb_apu/Multi_Buffer.cpp @@ -1,20 +1,23 @@ - -// Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ #include "Multi_Buffer.h" -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* Copyright (C) 2003-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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 */ -#include BLARGG_SOURCE_BEGIN +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) { @@ -23,48 +26,33 @@ Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) channels_changed_count_ = 1; } -blargg_err_t Multi_Buffer::set_channel_count( int ) -{ - return blargg_success; -} - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ -} - -Mono_Buffer::~Mono_Buffer() -{ -} - -blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) -{ - BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} +blargg_err_t Multi_Buffer::set_channel_count( int ) { return 0; } // Silent_Buffer Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse { - chan.left = NULL; - chan.center = NULL; - chan.right = NULL; + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = 0; + chan.center = 0; + chan.right = 0; } // Mono_Buffer -Mono_Buffer::channel_t Mono_Buffer::channel( int index ) +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) { - channel_t ch; - ch.center = &buf; - ch.left = &buf; - ch.right = &buf; - return ch; + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; } -void Mono_Buffer::end_frame( blip_time_t t, bool ) +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) { - buf.end_frame( t ); + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); } // Stereo_Buffer @@ -76,14 +64,12 @@ Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) chan.right = &bufs [2]; } -Stereo_Buffer::~Stereo_Buffer() -{ -} +Stereo_Buffer::~Stereo_Buffer() { } blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) { for ( int i = 0; i < buf_count; i++ ) - BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); } @@ -101,18 +87,20 @@ void Stereo_Buffer::bass_freq( int bass ) void Stereo_Buffer::clear() { - stereo_added = false; - was_stereo = false; + stereo_added = 0; + was_stereo = false; for ( int i = 0; i < buf_count; i++ ) bufs [i].clear(); } -void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) +void Stereo_Buffer::end_frame( blip_time_t clock_count ) { + stereo_added = 0; for ( unsigned i = 0; i < buf_count; i++ ) + { + stereo_added |= bufs [i].clear_modified() << i; bufs [i].end_frame( clock_count ); - - stereo_added |= stereo; + } } long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) @@ -125,91 +113,120 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) count = avail; if ( count ) { - if ( stereo_added || was_stereo ) + int bufs_used = stereo_added | was_stereo; + //debug_printf( "%X\n", bufs_used ); + if ( bufs_used <= 1 ) + { + mix_mono( out, count ); + bufs [0].remove_samples( count ); + bufs [1].remove_silence( count ); + bufs [2].remove_silence( count ); + } + else if ( bufs_used & 1 ) { mix_stereo( out, count ); - bufs [0].remove_samples( count ); bufs [1].remove_samples( count ); bufs [2].remove_samples( count ); } else { - mix_mono( out, count ); - - bufs [0].remove_samples( count ); - - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); + mix_stereo_no_center( out, count ); + bufs [0].remove_silence( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); } // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) { - was_stereo = stereo_added; - stereo_added = false; + if ( !bufs [0].samples_avail() ) + { + was_stereo = stereo_added; + stereo_added = 0; } } return count * 2; } -#include BLARGG_ENABLE_OPTIMIZER - -void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) +void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) { - Blip_Reader left; - Blip_Reader right; - Blip_Reader center; + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + BLIP_READER_BEGIN( center, bufs [0] ); - left.begin( bufs [1] ); - right.begin( bufs [2] ); - int bass = center.begin( bufs [0] ); - - while ( count-- ) + for ( ; count; --count ) { - int c = center.read(); - long l = c + left.read(); - long r = c + right.read(); - center.next( bass ); + int c = BLIP_READER_READ( center ); + blargg_long l = c + BLIP_READER_READ( left ); + blargg_long r = c + BLIP_READER_READ( right ); + if ( (BOOST::int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + BLIP_READER_NEXT( center, bass ); + if ( (BOOST::int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + out [0] = l; out [1] = r; out += 2; - - if ( (BOOST::int16_t) l != l ) - out [-2] = 0x7FFF - (l >> 24); - - left.next( bass ); - right.next( bass ); - - if ( (BOOST::int16_t) r != r ) - out [-1] = 0x7FFF - (r >> 24); } - center.end( bufs [0] ); - right.end( bufs [2] ); - left.end( bufs [1] ); + BLIP_READER_END( center, bufs [0] ); + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); } -void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) +void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) { - Blip_Reader in; - int bass = in.begin( bufs [0] ); + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); - while ( count-- ) + for ( ; count; --count ) { - long s = in.read(); - in.next( bass ); + blargg_long l = BLIP_READER_READ( left ); + if ( (BOOST::int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + blargg_long r = BLIP_READER_READ( right ); + if ( (BOOST::int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + + out [0] = l; + out [1] = r; + out += 2; + } + + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); +} + +void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( center, bufs [0] ); + + for ( ; count; --count ) + { + blargg_long s = BLIP_READER_READ( center ); + if ( (BOOST::int16_t) s != s ) + s = 0x7FFF - (s >> 24); + + BLIP_READER_NEXT( center, bass ); out [0] = s; out [1] = s; out += 2; - - if ( (BOOST::int16_t) s != s ) { - s = 0x7FFF - (s >> 24); - out [-2] = s; - out [-1] = s; - } } - in.end( bufs [0] ); + BLIP_READER_END( center, bufs [0] ); } - diff --git a/plugins/papu/gb_apu/Multi_Buffer.h b/plugins/papu/gb_apu/Multi_Buffer.h index 5e3c478e0..82c8b3ab5 100644 --- a/plugins/papu/gb_apu/Multi_Buffer.h +++ b/plugins/papu/gb_apu/Multi_Buffer.h @@ -1,11 +1,10 @@ - // Multi-channel sound buffer interface, and basic mono and stereo buffers -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Blip_Buffer 0.4.1 #ifndef MULTI_BUFFER_H #define MULTI_BUFFER_H +#include "blargg_common.h" #include "Blip_Buffer.h" // Interface to one or more Blip_Buffers mapped to one or more channels @@ -24,7 +23,9 @@ public: Blip_Buffer* left; Blip_Buffer* right; }; - virtual channel_t channel( int index ) = 0; + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual channel_t channel( int index, int type ) = 0; // See Blip_Buffer.h virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; @@ -36,10 +37,8 @@ public: // Length of buffer, in milliseconds int length() const; - // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' - // if nothing was added to the left and right buffers of any channel for - // this time frame. - virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; + // See Blip_Buffer.h + virtual void end_frame( blip_time_t ) = 0; // Number of samples per output frame (1 = mono, 2 = stereo) int samples_per_frame() const; @@ -52,6 +51,8 @@ public: virtual long read_samples( blip_sample_t*, long ) = 0; virtual long samples_avail() const = 0; +public: + BLARGG_DISABLE_NOTHROW protected: void channels_changed() { channels_changed_count_++; } private: @@ -68,55 +69,56 @@ private: // Uses a single buffer and outputs mono samples. class Mono_Buffer : public Multi_Buffer { Blip_Buffer buf; + channel_t chan; public: - Mono_Buffer(); - ~Mono_Buffer(); - // Buffer used for all channels Blip_Buffer* center() { return &buf; } - // See Multi_Buffer +public: + Mono_Buffer(); + ~Mono_Buffer(); blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); + void clock_rate( long rate ) { buf.clock_rate( rate ); } + void bass_freq( int freq ) { buf.bass_freq( freq ); } + void clear() { buf.clear(); } + long samples_avail() const { return buf.samples_avail(); } + long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t t ) { buf.end_frame( t ); } }; // Uses three buffers (one for center) and outputs stereo sample pairs. class Stereo_Buffer : public Multi_Buffer { public: - Stereo_Buffer(); - ~Stereo_Buffer(); // Buffers used for all channels Blip_Buffer* center() { return &bufs [0]; } Blip_Buffer* left() { return &bufs [1]; } Blip_Buffer* right() { return &bufs [2]; } - // See Multi_Buffer +public: + Stereo_Buffer(); + ~Stereo_Buffer(); blargg_err_t set_sample_rate( long, int msec = blip_default_length ); void clock_rate( long ); void bass_freq( int ); void clear(); - channel_t channel( int index ); - void end_frame( blip_time_t, bool added_stereo = true ); + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ); - long samples_avail() const; + long samples_avail() const { return bufs [0].samples_avail() * 2; } long read_samples( blip_sample_t*, long ); private: enum { buf_count = 3 }; Blip_Buffer bufs [buf_count]; channel_t chan; - bool stereo_added; - bool was_stereo; + int stereo_added; + int was_stereo; - void mix_stereo( blip_sample_t*, long ); - void mix_mono( blip_sample_t*, long ); + void mix_stereo_no_center( blip_sample_t*, blargg_long ); + void mix_stereo( blip_sample_t*, blargg_long ); + void mix_mono( blip_sample_t*, blargg_long ); }; // Silent_Buffer generates no samples, useful where no sound is wanted @@ -124,51 +126,33 @@ class Silent_Buffer : public Multi_Buffer { channel_t chan; public: Silent_Buffer(); - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); void clock_rate( long ) { } void bass_freq( int ) { } void clear() { } - channel_t channel( int ) { return chan; } - void end_frame( blip_time_t, bool unused = true ) { } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ) { } long samples_avail() const { return 0; } long read_samples( blip_sample_t*, long ) { return 0; } }; -// End of public interface +inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return 0; +} inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) { return Multi_Buffer::set_sample_rate( rate, msec ); } -inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return blargg_success; -} - inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } -inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; } - -inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) { return chan; } - inline long Multi_Buffer::sample_rate() const { return sample_rate_; } inline int Multi_Buffer::length() const { return length_; } -inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } - -inline void Mono_Buffer::clear() { buf.clear(); } - -inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } - -inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - -inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } - #endif - diff --git a/plugins/papu/gb_apu/blargg_common.h b/plugins/papu/gb_apu/blargg_common.h index 519bba7c4..ed218a8da 100644 --- a/plugins/papu/gb_apu/blargg_common.h +++ b/plugins/papu/gb_apu/blargg_common.h @@ -1,178 +1,196 @@ - // Sets up common environment for Shay Green's libraries. -// -// Don't modify this file directly; #define HAVE_CONFIG_H and put your -// configuration into "config.h". - -// Copyright (C) 2004-2005 Shay Green. +// To change configuration options, modify blargg_config.h, not this file. #ifndef BLARGG_COMMON_H #define BLARGG_COMMON_H -// Allow prefix configuration file *which can re-include blargg_common.h* -// (probably indirectly). -#ifdef HAVE_CONFIG_H - #undef BLARGG_COMMON_H - #include "config.h" - #define BLARGG_COMMON_H +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT #endif -// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code -#ifndef BLARGG_ENABLE_OPTIMIZER - #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" -#endif - -// Source files have #include BLARGG_SOURCE_BEGIN at the beginning -#ifndef BLARGG_SOURCE_BEGIN - #define BLARGG_SOURCE_BEGIN "blargg_source.h" -#endif - -// Determine compiler's language support - -#if defined (__MWERKS__) - // Metrowerks CodeWarrior - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (_MSC_VER) - // Microsoft Visual C++ - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (__GNUC__) - // GNU C++ - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif defined (__MINGW32__) - // Mingw? - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif __cplusplus < 199711 - // Pre-ISO C++ compiler - #define BLARGG_COMPILER_HAS_BOOL 0 - #define STATIC_CAST( type ) (type) - -#endif - -// STATIC_CAST(T) (expr) -> static_cast< T > (expr) +// STATIC_CAST(T,expr): Used in place of static_cast (expr) #ifndef STATIC_CAST - #define STATIC_CAST( type ) static_cast< type > + #define STATIC_CAST(T,expr) ((T) (expr)) #endif -// Set up boost -#include "boost/config.hpp" -#ifndef BOOST_MINIMAL - #define BOOST boost - #ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 1 +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + void* p = realloc( begin_, n * sizeof (T) ); + if ( !p && n ) + return "Out of memory"; + begin_ = (T*) p; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || __GNUC__ >= 3 + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) #endif - #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) #endif #endif -// Bool support +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 -#elif !BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file typedef int bool; const bool true = 1; const bool false = 0; #endif -// Set up namespace support +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough -#ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 0 -#endif - -#ifndef BLARGG_USE_NAMESPACE - #define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE -#endif - -#ifndef BOOST - #if BLARGG_USE_NAMESPACE - #define BOOST boost - #else - #define BOOST - #endif -#endif - -#undef BLARGG_BEGIN_NAMESPACE -#undef BLARGG_END_NAMESPACE -#if BLARGG_USE_NAMESPACE - #define BLARGG_BEGIN_NAMESPACE( name ) namespace name { - #define BLARGG_END_NAMESPACE } +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; #else - #define BLARGG_BEGIN_NAMESPACE( name ) - #define BLARGG_END_NAMESPACE + typedef int blargg_long; #endif -#if BLARGG_USE_NAMESPACE - #define STD std +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; #else - #define STD + typedef unsigned blargg_ulong; #endif -// BOOST::uint8_t, BOOST::int16_t, etc. -#include "boost/cstdint.hpp" +// BOOST::int8_t etc. -// BOOST_STATIC_ASSERT( expr ) -#include "boost/static_assert.hpp" +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST -// Common standard headers -#if BLARGG_COMPILER_HAS_NAMESPACE - #include - #include #else - #include - #include + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; #endif -// blargg_err_t (NULL on success, otherwise error string) -typedef const char* blargg_err_t; -const blargg_err_t blargg_success = 0; - -// BLARGG_NEW is used in place of 'new' to create objects. By default, -// plain new is used. -#ifndef BLARGG_NEW - #define BLARGG_NEW new +#if __GNUC__ >= 3 + #define BLARGG_DEPRECATED __attribute__ ((deprecated)) +#else + #define BLARGG_DEPRECATED #endif -// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN -// Only needed if modules are used which must know byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) - #if defined (__powerc) || defined (macintosh) - #define BLARGG_BIG_ENDIAN 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_LITTLE_ENDIAN 1 - - #endif -#endif - -// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features) -#ifndef BLARGG_NONPORTABLE - #define BLARGG_NONPORTABLE 0 -#endif -#ifdef BLARGG_MOST_PORTABLE - #error "BLARGG_MOST_PORTABLE has been removed; use BLARGG_NONPORTABLE." -#endif - -// BLARGG_CPU_* -#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) - #if defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_CPU_X86 1 - - #endif +// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib. +// During development, BLARGG_PURE( x ) expands to = 0; +// virtual int func() BLARGG_PURE( { return 0; } ) +#ifndef BLARGG_PURE + #define BLARGG_PURE( def ) def #endif #endif - +#endif diff --git a/plugins/papu/gb_apu/blargg_config.h b/plugins/papu/gb_apu/blargg_config.h new file mode 100644 index 000000000..377dd2d8c --- /dev/null +++ b/plugins/papu/gb_apu/blargg_config.h @@ -0,0 +1,43 @@ +// Library configuration. Modify this file as necessary. + +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to use zlib for transparent decompression of gzipped files +//#define HAVE_ZLIB_H + +// Uncomment and edit list to support only the listed game music types, +// so that the others don't get linked in at all. +/* +#define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type +*/ + +// Uncomment to enable platform-specific optimizations +//#define BLARGG_NONPORTABLE 1 + +// Uncomment to use faster, lower quality sound synthesis +//#define BLIP_BUFFER_FAST 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/plugins/papu/gb_apu/blargg_source.h b/plugins/papu/gb_apu/blargg_source.h index fa77dc0f7..b011777ad 100644 --- a/plugins/papu/gb_apu/blargg_source.h +++ b/plugins/papu/gb_apu/blargg_source.h @@ -1,7 +1,7 @@ - -// By default, #included at beginning of library source files - -// Copyright (C) 2005 Shay Green. +/* Included at the beginning of library source files, after all other #include lines. +Sets up helpful macros and services used in my source code. They don't need +module an annoying module prefix on their names since they are defined after +all other #include lines. */ #ifndef BLARGG_SOURCE_H #define BLARGG_SOURCE_H @@ -16,13 +16,14 @@ // module. A failed requirement indicates a bug outside the module. // void require( bool expr ); #undef require -#define require( expr ) assert((/* "unmet requirement",*/ expr )) +#define require( expr ) assert( expr ) // Like printf() except output goes to debug log file. Might be defined to do // nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -#undef dprintf -#define dprintf (1) ? ((void) 0) : (void) +// void debug_printf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef debug_printf +#define debug_printf (1) ? (void) 0 : blargg_dprintf_ // If enabled, evaluate expr and if false, make debug log entry with source file // and line. Meant for finding situations that should be examined further, but that @@ -30,22 +31,40 @@ #undef check #define check( expr ) ((void) 0) -// If expr returns non-NULL error string, return it from current function, otherwise continue. -#define BLARGG_RETURN_ERR( expr ) do { \ +// If expr yields error string, return it from current function, otherwise continue. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ blargg_err_t blargg_return_err_ = (expr); \ if ( blargg_return_err_ ) return blargg_return_err_; \ } while ( 0 ) -// If ptr is NULL, return out of memory error string. -#define BLARGG_CHECK_ALLOC( ptr ) do { if ( !(ptr) ) return "Out of memory"; } while ( 0 ) +// If ptr is 0, return out of memory error string. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) // Avoid any macros which evaluate their arguments multiple times #undef min #undef max +#define DEF_MIN_MAX( type ) \ + static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ + static inline type max( type x, type y ) { if ( y < x ) return x; return y; } + +DEF_MIN_MAX( int ) +DEF_MIN_MAX( unsigned ) +DEF_MIN_MAX( long ) +DEF_MIN_MAX( unsigned long ) +DEF_MIN_MAX( float ) +DEF_MIN_MAX( double ) + +#undef DEF_MIN_MAX + +/* // using const references generates crappy code, and I am currenly only using these // for built-in types, so they take arguments by value +// TODO: remove +inline int min( int x, int y ) template inline T min( T x, T y ) { @@ -61,6 +80,31 @@ inline T max( T x, T y ) return y; return x; } +*/ +// TODO: good idea? bad idea? +#undef byte +#define byte byte_ +typedef unsigned char byte; + +// Setup compiler defines useful for exporting required public API symbols in gme.cpp +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && defined(BLARGG_BUILD_DLL) + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (LIBGME_VISIBILITY) + #define BLARGG_EXPORT __attribute__((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif #endif +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index 0f2035696..b555310a0 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -70,7 +70,7 @@ papuInstrument::papuInstrument( InstrumentTrack * _instrument_track ) : tr( "Sweep RtShift amount" ) ), m_ch1WavePatternDutyModel( 2.0f, 0.0f, 3.0f, 1.0f, this, tr( "Wave Pattern Duty" ) ), - m_ch1VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this, + m_ch1VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this, tr( "Channel 1 volume" ) ), m_ch1VolSweepDirModel( false, this, tr( "Volume sweep direction" ) ), @@ -396,7 +396,7 @@ void papuInstrument::playNote( NotePlayHandle * _n, } datalen = framesleft>avail?avail:framesleft; datalen = datalen>buf_size?buf_size:datalen; - + long count = papu->read_samples( buf, datalen*2)/2; for( fpp_t frame = 0; frame < count; ++frame ) From d87a8b3da4976b669600972c4e731f8a4f6a6fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Tue, 25 Jul 2017 21:48:24 -0300 Subject: [PATCH 03/14] Fix recording of sustained midi notes (#3710) --- src/core/NotePlayHandle.cpp | 9 ++++++--- src/tracks/InstrumentTrack.cpp | 27 +++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 82be6b92f..8f437eb5a 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -378,10 +378,13 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) } // inform attached components about MIDI finished (used for recording in Piano Roll) - if( m_origin == OriginMidiInput ) + if (!instrumentTrack()->isSustainPedalPressed()) { - setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); - m_instrumentTrack->midiNoteOff( *this ); + if( m_origin == OriginMidiInput ) + { + setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); + m_instrumentTrack->midiNoteOff( *this ); + } } } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index b847c85e6..e9e4aac45 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -273,7 +273,14 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti // be deleted later automatically) Engine::mixer()->requestChangeInModel(); m_notes[event.key()]->noteOff( offset ); - m_notes[event.key()] = NULL; + + if (!(isSustainPedalPressed()) || + !(m_notes[event.key()]->origin() == + m_notes[event.key()]->OriginMidiInput)) + { + m_notes[event.key()] = NULL; + } + Engine::mixer()->doneChangeInModel(); } eventHandled = true; @@ -302,8 +309,24 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti { m_sustainPedalPressed = true; } - else + else if (isSustainPedalPressed()) { + for (NotePlayHandle*& nph : m_notes) + { + if (nph && nph->isReleased()) + { + if( nph->origin() == + nph->OriginMidiInput) + { + nph->setLength( + MidiTime( static_cast( + nph->totalFramesPlayed() / + Engine::framesPerTick() ) ) ); + midiNoteOff( *nph ); + } + nph = NULL; + } + } m_sustainPedalPressed = false; } } From b83c1bdebe3cf94101280165e310bbc13cd63545 Mon Sep 17 00:00:00 2001 From: irrenhaus3 Date: Wed, 26 Jul 2017 12:48:25 +0200 Subject: [PATCH 04/14] Respect build options in export dialogs (#3714) * Respect build options in ExportProjectDialog * Use QItem user data instead of hard ordering to identify export format in ExportProjectDialog * For compatibility with QVariant, ExportFileFormats is now explicitly an int. * Don't break out of format identifier loop prematurely in Song export. --- include/ProjectRenderer.h | 2 +- src/core/Song.cpp | 7 ++-- src/gui/ExportProjectDialog.cpp | 64 +++++++++++++++------------------ 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/include/ProjectRenderer.h b/include/ProjectRenderer.h index 15d043e51..543eb43c3 100644 --- a/include/ProjectRenderer.h +++ b/include/ProjectRenderer.h @@ -35,7 +35,7 @@ class ProjectRenderer : public QThread { Q_OBJECT public: - enum ExportFileFormats + enum ExportFileFormats: int { WaveFile, OggFile, diff --git a/src/core/Song.cpp b/src/core/Song.cpp index af359a881..75b5adff6 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1337,10 +1337,11 @@ void Song::exportProject( bool multiExport ) efd.setFileMode( FileDialog::AnyFile ); int idx = 0; QStringList types; - while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats && - ProjectRenderer::fileEncodeDevices[idx].isAvailable()) + while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats) { - types << tr( ProjectRenderer::fileEncodeDevices[idx].m_description ); + if(ProjectRenderer::fileEncodeDevices[idx].isAvailable()) { + types << tr(ProjectRenderer::fileEncodeDevices[idx].m_description); + } ++idx; } efd.setNameFilters( types ); diff --git a/src/gui/ExportProjectDialog.cpp b/src/gui/ExportProjectDialog.cpp index 83c7883cc..0bd815010 100644 --- a/src/gui/ExportProjectDialog.cpp +++ b/src/gui/ExportProjectDialog.cpp @@ -65,7 +65,9 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name, // add to combo box fileFormatCB->addItem( ProjectRenderer::tr( - ProjectRenderer::fileEncodeDevices[i].m_description ) ); + ProjectRenderer::fileEncodeDevices[i].m_description ), + QVariant(ProjectRenderer::fileEncodeDevices[i].m_fileFormat) // format tag; later used for identification + ); // if this is our extension, select it if( QString::compare( renderExt, fileExt, @@ -187,29 +189,16 @@ void ExportProjectDialog::startExport() } -ProjectRenderer::ExportFileFormats convertIndexToExportFileFormat(int index) -{ - switch (index) - { - case 0: - return ProjectRenderer::WaveFile; - case 1: - return ProjectRenderer::OggFile; - case 2: - return ProjectRenderer::MP3File; - default: - Q_ASSERT(false); - break; - } - - return ProjectRenderer::NumFileFormats; -} - - void ExportProjectDialog::onFileFormatChanged(int index) { - ProjectRenderer::ExportFileFormats exportFormat = - convertIndexToExportFileFormat(index); + // Extract the format tag from the currently selected item, + // and adjust the UI properly. + QVariant format_tag = fileFormatCB->itemData(index); + bool successful_conversion = false; + auto exportFormat = static_cast( + format_tag.toInt(&successful_conversion) + ); + Q_ASSERT(successful_conversion); bool stereoModeVisible = exportFormat == ProjectRenderer::MP3File; @@ -236,28 +225,31 @@ void ExportProjectDialog::startBtnClicked() { m_ft = ProjectRenderer::NumFileFormats; + //Get file format from current menu selection. + bool successful_conversion = false; + QVariant tag = fileFormatCB->itemData(fileFormatCB->currentIndex()); + m_ft = static_cast(tag.toInt(&successful_conversion)); + + if( !successful_conversion ) + { + QMessageBox::information( this, tr( "Error" ), + tr( "Error while determining file-encoder device. " + "Please try to choose a different output " + "format." ) ); + reject(); + return; + } + + // Find proper file extension. for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) { - if( fileFormatCB->currentText() == - ProjectRenderer::tr( - ProjectRenderer::fileEncodeDevices[i].m_description ) ) + if (m_ft == ProjectRenderer::fileEncodeDevices[i].m_fileFormat) { - m_ft = ProjectRenderer::fileEncodeDevices[i].m_fileFormat; m_fileExtension = QString( QLatin1String( ProjectRenderer::fileEncodeDevices[i].m_extension ) ); break; } } - if( m_ft == ProjectRenderer::NumFileFormats ) - { - QMessageBox::information( this, tr( "Error" ), - tr( "Error while determining file-encoder device. " - "Please try to choose a different output " - "format." ) ); - reject(); - return; - } - startButton->setEnabled( false ); progressBar->setEnabled( true ); From 2e841e4917312bca9f08178aa42dadb97c5a1a6e Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Sun, 30 Jul 2017 11:43:48 +0200 Subject: [PATCH 05/14] Fixes to recover file system (#3722) Don't auto-save while playing by default. On weaker machines (xp?) we see glitches so better turn this on after need. Remove the last of Limited Sessin which was removed in 290556e. --- include/MainWindow.h | 2 -- src/core/main.cpp | 1 - src/gui/MainWindow.cpp | 16 ---------------- src/gui/SetupDialog.cpp | 4 ++-- 4 files changed, 2 insertions(+), 21 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 41fc06d5d..69b430d33 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -105,8 +105,6 @@ public: return m_autoSaveTimer.interval(); } - void runAutoSave(); - enum SessionState { Normal, diff --git a/src/core/main.cpp b/src/core/main.cpp index 8d30a1912..b36ca0055 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -919,7 +919,6 @@ int main( int argc, char * * argv ) if( autoSaveEnabled ) { gui->mainWindow()->autoSaveTimerReset(); - gui->mainWindow()->autoSave(); } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 8c2898da3..63b931823 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -821,7 +821,6 @@ void MainWindow::createNewProject() { Engine::getSong()->createNewProject(); } - runAutoSave(); } @@ -840,7 +839,6 @@ void MainWindow::createNewProjectFromTemplate( QAction * _idx ) Engine::getSong()->createNewProjectFromTemplate( dirBase + _idx->text() + ".mpt" ); } - runAutoSave(); } @@ -865,7 +863,6 @@ void MainWindow::openProject() setCursor( Qt::ArrowCursor ); } } - runAutoSave(); } @@ -917,7 +914,6 @@ void MainWindow::openRecentlyOpenedProject( QAction * _action ) Engine::getSong()->loadProject( f ); setCursor( Qt::ArrowCursor ); } - runAutoSave(); } @@ -1557,15 +1553,3 @@ void MainWindow::autoSave() } } } - - -// For the occasional auto save action that isn't run -// from the timer where we need to do extra tests. -void MainWindow::runAutoSave() -{ - if( ConfigManager::inst()->value( "ui", "enableautosave" ).toInt() ) - { - autoSave(); - autoSaveTimerReset(); // Reset timer - } -} diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 9f135784e..879c1b7b9 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -120,7 +120,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_backgroundArtwork( QDir::toNativeSeparators( ConfigManager::inst()->backgroundArtwork() ) ), m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ), m_enableAutoSave( ConfigManager::inst()->value( "ui", "enableautosave", "1" ).toInt() ), - m_enableRunningAutoSave( ConfigManager::inst()->value( "ui", "enablerunningautosave", "1" ).toInt() ), + m_enableRunningAutoSave( ConfigManager::inst()->value( "ui", "enablerunningautosave", "0" ).toInt() ), m_saveInterval( ConfigManager::inst()->value( "ui", "saveinterval" ).toInt() < 1 ? MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES : ConfigManager::inst()->value( "ui", "saveinterval" ).toInt() ), @@ -1518,7 +1518,7 @@ void SetupDialog::resetAutoSave() { setAutoSaveInterval( MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES ); m_autoSave->setChecked( true ); - m_runningAutoSave->setChecked( true ); + m_runningAutoSave->setChecked( false ); } From 31126b0aa4b721b21c881ac4d342da8ec1951d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 2 Aug 2017 02:20:09 -0300 Subject: [PATCH 06/14] Midi sustain working when envelope is on (#3730) * midi sustain working when envelope is on * pressing sustain pedal again doesn't pause release [cherry-picked from master] --- include/NotePlayHandle.h | 6 ++++++ src/core/InstrumentSoundShaping.cpp | 3 ++- src/core/NotePlayHandle.cpp | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index bf5c266ca..344980eb6 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -156,6 +156,11 @@ public: return m_released; } + bool isReleaseStarted() const + { + return m_releaseStarted; + } + /*! Returns total numbers of frames played so far */ f_cnt_t totalFramesPlayed() const { @@ -297,6 +302,7 @@ private: // release of note NotePlayHandleList m_subNotes; // used for chords and arpeggios volatile bool m_released; // indicates whether note is released + bool m_releaseStarted; bool m_hasParent; // indicates whether note has parent NotePlayHandle * m_parent; // parent note bool m_hadChildren; diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 2a5a71e86..22327ae8e 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -137,7 +137,8 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, const f_cnt_t envTotalFrames = n->totalFramesPlayed(); f_cnt_t envReleaseBegin = envTotalFrames - n->releaseFramesDone() + n->framesBeforeRelease(); - if( n->isReleased() == false ) + if( !n->isReleased() || ( n->instrumentTrack()->isSustainPedalPressed() && + !n->isReleaseStarted() ) ) { envReleaseBegin += frames; } diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 8f437eb5a..84d888fee 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -62,6 +62,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, m_releaseFramesDone( 0 ), m_subNotes(), m_released( false ), + m_releaseStarted( false ), m_hasParent( parent != NULL ), m_parent( parent ), m_hadChildren( false ), @@ -248,8 +249,11 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) m_instrumentTrack->playNote( this, _working_buffer ); } - if( m_released ) + if( m_released && (!instrumentTrack()->isSustainPedalPressed() || + m_releaseStarted) ) { + m_releaseStarted = true; + f_cnt_t todo = framesThisPeriod; // if this note is base-note for arpeggio, always set From 08e64dc637312bcf6c2de0eb3236f787456d37fb Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 2 Aug 2017 14:56:28 +0900 Subject: [PATCH 07/14] Fix deadlock when exporting is finished, if the project has any Sample TCO(s). (#3742) --- include/Mixer.h | 1 + src/core/ProjectRenderer.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/Mixer.h b/include/Mixer.h index 18510f363..8368e8434 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -421,6 +421,7 @@ private: friend class LmmsCore; friend class MixerWorkerThread; + friend class ProjectRenderer; } ; diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 3c16bce96..7588401c2 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -201,6 +201,9 @@ void ProjectRenderer::run() } } + // notify mixer of the end of processing + Engine::mixer()->stopProcessing(); + Engine::getSong()->stopExport(); // if the user aborted export-process, the file has to be deleted From 4d044439abb333e989da7a10f2df89ef3b01a405 Mon Sep 17 00:00:00 2001 From: Umcaruje Date: Mon, 7 Aug 2017 10:35:02 +0200 Subject: [PATCH 08/14] Fix file permissions for theme files (#3752) --- data/themes/default/add.png | Bin data/themes/default/add_automation.png | Bin data/themes/default/add_bb_track.png | Bin data/themes/default/add_folder.png | Bin data/themes/default/add_sample_track.png | Bin data/themes/default/analysis.png | Bin data/themes/default/apply-selected.png | Bin data/themes/default/apply.png | Bin data/themes/default/arp_down.png | Bin data/themes/default/arp_free.png | Bin data/themes/default/arp_random.png | Bin data/themes/default/arp_sort.png | Bin data/themes/default/arp_sync.png | Bin data/themes/default/arp_up.png | Bin data/themes/default/arp_up_and_down.png | Bin data/themes/default/automation.png | Bin data/themes/default/automation_track.png | Bin data/themes/default/autoscroll_off.png | Bin data/themes/default/autoscroll_on.png | Bin data/themes/default/back_to_start.png | Bin data/themes/default/back_to_zero.png | Bin data/themes/default/bb_track.png | Bin data/themes/default/bb_track_btn.png | Bin data/themes/default/black_key.png | Bin data/themes/default/black_key_pressed.png | Bin data/themes/default/cancel.png | Bin data/themes/default/chord.png | Bin data/themes/default/clock.png | Bin data/themes/default/colorize.png | Bin data/themes/default/combobox_arrow.png | Bin data/themes/default/combobox_arrow_selected.png | Bin data/themes/default/combobox_bg.png | Bin data/themes/default/computer.png | Bin data/themes/default/controller.png | Bin data/themes/default/cpuload_bg.png | Bin data/themes/default/cpuload_leds.png | Bin data/themes/default/dont_know.png | Bin data/themes/default/edit_copy.png | Bin data/themes/default/edit_cut.png | Bin data/themes/default/edit_draw.png | Bin data/themes/default/edit_erase.png | Bin data/themes/default/edit_move.png | Bin data/themes/default/edit_paste.png | Bin data/themes/default/edit_redo.png | Bin data/themes/default/edit_rename.png | Bin data/themes/default/edit_select.png | Bin data/themes/default/edit_undo.png | Bin data/themes/default/effect_plugin.png | Bin data/themes/default/error.png | Bin data/themes/default/exit.png | Bin data/themes/default/exp_wave_active.png | Bin data/themes/default/exp_wave_inactive.png | Bin data/themes/default/factory_files.png | Bin data/themes/default/fader_background.png | Bin data/themes/default/fader_knob.png | Bin data/themes/default/fader_leds.png | Bin data/themes/default/file.png | Bin data/themes/default/filter_2lp.png | Bin data/themes/default/filter_ap.png | Bin data/themes/default/filter_bp.png | Bin data/themes/default/filter_hp.png | Bin data/themes/default/filter_lp.png | Bin data/themes/default/filter_notch.png | Bin data/themes/default/flip_x.png | Bin data/themes/default/flip_y.png | Bin data/themes/default/folder.png | Bin data/themes/default/folder_locked.png | Bin data/themes/default/folder_opened.png | Bin data/themes/default/fx_mixer.png | Bin data/themes/default/hand.png | Bin data/themes/default/help.png | Bin data/themes/default/hint.png | Bin data/themes/default/home.png | Bin data/themes/default/hq_mode.png | Bin data/themes/default/instrument_track.png | Bin data/themes/default/keep_stop_position.png | Bin data/themes/default/knob01.png | Bin data/themes/default/knob02.png | Bin data/themes/default/knob03.png | Bin data/themes/default/knob05.png | Bin data/themes/default/lcd_19green.png | Bin data/themes/default/lcd_19red.png | Bin data/themes/default/lcd_21pink.png | Bin data/themes/default/led_blue.png | Bin data/themes/default/led_green.png | Bin data/themes/default/led_off.png | Bin data/themes/default/led_red.png | Bin data/themes/default/led_yellow.png | Bin data/themes/default/lfo_d100_active.png | Bin data/themes/default/lfo_d100_inactive.png | Bin data/themes/default/lfo_x100_active.png | Bin data/themes/default/lfo_x100_inactive.png | Bin data/themes/default/lfo_x1_active.png | Bin data/themes/default/lfo_x1_inactive.png | Bin data/themes/default/loop_point.png | Bin data/themes/default/loop_points_off.png | Bin data/themes/default/loop_points_on.png | Bin data/themes/default/main_slider.png | Bin data/themes/default/master_pitch.png | Bin data/themes/default/master_volume.png | Bin data/themes/default/metronome.png | Bin data/themes/default/midi_file.png | Bin data/themes/default/mixer_send_off.png | Bin data/themes/default/mixer_send_on.png | Bin data/themes/default/moog_saw_wave_active.png | Bin data/themes/default/moog_saw_wave_inactive.png | Bin data/themes/default/muted.png | Bin data/themes/default/new_channel.png | Bin data/themes/default/note.png | Bin data/themes/default/note_double_whole.png | Bin data/themes/default/note_eight.png | Bin data/themes/default/note_eighth.png | Bin data/themes/default/note_half.png | Bin data/themes/default/note_none.png | Bin data/themes/default/note_quarter.png | Bin data/themes/default/note_sixteenth.png | Bin data/themes/default/note_thirtysecond.png | Bin data/themes/default/note_tripleteighth.png | Bin data/themes/default/note_triplethalf.png | Bin data/themes/default/note_tripletquarter.png | Bin data/themes/default/note_tripletsixteenth.png | Bin data/themes/default/note_tripletthirtysecond.png | Bin data/themes/default/note_whole.png | Bin data/themes/default/pat_rec.png | Bin data/themes/default/pause.png | Bin data/themes/default/piano.png | Bin data/themes/default/play.png | Bin data/themes/default/playpos_marker.png | Bin data/themes/default/plugins.png | Bin data/themes/default/ports.png | Bin data/themes/default/pr_black_key.png | Bin data/themes/default/pr_black_key_pressed.png | Bin data/themes/default/pr_white_key_big.png | Bin data/themes/default/pr_white_key_big_pressed.png | Bin data/themes/default/pr_white_key_small.png | Bin data/themes/default/pr_white_key_small_pressed.png | Bin data/themes/default/preset_file.png | Bin data/themes/default/progression_cubic_hermite.png | Bin data/themes/default/progression_discrete.png | Bin data/themes/default/progression_linear.png | Bin data/themes/default/project_export.png | Bin data/themes/default/project_file.png | Bin data/themes/default/project_import.png | Bin data/themes/default/project_new.png | Bin data/themes/default/project_new_from_template.png | Bin data/themes/default/project_notes.png | Bin data/themes/default/project_open.png | Bin data/themes/default/project_open_recent.png | Bin data/themes/default/project_save.png | Bin data/themes/default/project_saveas.png | Bin data/themes/default/quantize.png | Bin data/themes/default/random_wave_active.png | Bin data/themes/default/random_wave_inactive.png | Bin data/themes/default/receive_bg_arrow.png | Bin data/themes/default/record.png | Bin data/themes/default/record_accompany.png | Bin data/themes/default/recover.png | Bin data/themes/default/reload.png | Bin data/themes/default/round_square_wave_active.png | Bin data/themes/default/round_square_wave_inactive.png | Bin data/themes/default/sample_file.png | Bin data/themes/default/sample_track.png | Bin data/themes/default/saw_wave_active.png | Bin data/themes/default/saw_wave_inactive.png | Bin data/themes/default/sbarrow_down.png | Bin data/themes/default/sbarrow_down_d.png | Bin data/themes/default/sbarrow_left.png | Bin data/themes/default/sbarrow_left_d.png | Bin data/themes/default/sbarrow_right.png | Bin data/themes/default/sbarrow_right_d.png | Bin data/themes/default/sbarrow_up.png | Bin data/themes/default/sbarrow_up_d.png | Bin data/themes/default/scale.png | Bin data/themes/default/send_bg_arrow.png | Bin data/themes/default/setup_audio.png | Bin data/themes/default/setup_directories.png | Bin data/themes/default/setup_general.png | Bin data/themes/default/setup_midi.png | Bin data/themes/default/setup_performance.png | Bin data/themes/default/shadow_c.png | Bin data/themes/default/sin_wave_active.png | Bin data/themes/default/sin_wave_inactive.png | Bin data/themes/default/songeditor.png | Bin data/themes/default/soundfont_file.png | Bin data/themes/default/splash.png | Bin data/themes/default/square_wave_active.png | Bin data/themes/default/square_wave_inactive.png | Bin data/themes/default/step_btn_add.png | Bin data/themes/default/step_btn_duplicate.png | Bin data/themes/default/step_btn_off.png | Bin data/themes/default/step_btn_off_light.png | Bin data/themes/default/step_btn_remove.png | Bin data/themes/default/stepper-down-press.png | Bin data/themes/default/stepper-down.png | Bin data/themes/default/stepper-left-press.png | Bin data/themes/default/stepper-left.png | Bin data/themes/default/stepper-right-press.png | Bin data/themes/default/stepper-right.png | Bin data/themes/default/stepper-up-press.png | Bin data/themes/default/stepper-up.png | Bin data/themes/default/stop.png | Bin data/themes/default/tempo_sync.png | Bin data/themes/default/text_block.png | Bin data/themes/default/text_bold.png | Bin data/themes/default/text_center.png | Bin data/themes/default/text_italic.png | Bin data/themes/default/text_left.png | Bin data/themes/default/text_right.png | Bin data/themes/default/text_under.png | Bin data/themes/default/toolbar_bg.png | Bin data/themes/default/track_op_grip.png | Bin data/themes/default/track_shadow_c.png | Bin data/themes/default/trackop.png | Bin data/themes/default/trackop_c.png | Bin data/themes/default/trackop_h.png | Bin data/themes/default/triangle_wave_active.png | Bin data/themes/default/triangle_wave_inactive.png | Bin data/themes/default/uhoh.png | Bin data/themes/default/unavailable_sound.png | Bin data/themes/default/unknown_file.png | Bin data/themes/default/usr_wave_active.png | Bin data/themes/default/usr_wave_inactive.png | Bin data/themes/default/vst_plugin_file.png | Bin data/themes/default/whatsthis.png | Bin data/themes/default/white_key.png | Bin data/themes/default/white_key_pressed.png | Bin data/themes/default/white_noise_wave_active.png | Bin data/themes/default/white_noise_wave_inactive.png | Bin data/themes/default/zoom.png | Bin data/themes/default/zoom_x.png | Bin data/themes/default/zoom_y.png | Bin 231 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 data/themes/default/add.png mode change 100755 => 100644 data/themes/default/add_automation.png mode change 100755 => 100644 data/themes/default/add_bb_track.png mode change 100755 => 100644 data/themes/default/add_folder.png mode change 100755 => 100644 data/themes/default/add_sample_track.png mode change 100755 => 100644 data/themes/default/analysis.png mode change 100755 => 100644 data/themes/default/apply-selected.png mode change 100755 => 100644 data/themes/default/apply.png mode change 100755 => 100644 data/themes/default/arp_down.png mode change 100755 => 100644 data/themes/default/arp_free.png mode change 100755 => 100644 data/themes/default/arp_random.png mode change 100755 => 100644 data/themes/default/arp_sort.png mode change 100755 => 100644 data/themes/default/arp_sync.png mode change 100755 => 100644 data/themes/default/arp_up.png mode change 100755 => 100644 data/themes/default/arp_up_and_down.png mode change 100755 => 100644 data/themes/default/automation.png mode change 100755 => 100644 data/themes/default/automation_track.png mode change 100755 => 100644 data/themes/default/autoscroll_off.png mode change 100755 => 100644 data/themes/default/autoscroll_on.png mode change 100755 => 100644 data/themes/default/back_to_start.png mode change 100755 => 100644 data/themes/default/back_to_zero.png mode change 100755 => 100644 data/themes/default/bb_track.png mode change 100755 => 100644 data/themes/default/bb_track_btn.png mode change 100755 => 100644 data/themes/default/black_key.png mode change 100755 => 100644 data/themes/default/black_key_pressed.png mode change 100755 => 100644 data/themes/default/cancel.png mode change 100755 => 100644 data/themes/default/chord.png mode change 100755 => 100644 data/themes/default/clock.png mode change 100755 => 100644 data/themes/default/colorize.png mode change 100755 => 100644 data/themes/default/combobox_arrow.png mode change 100755 => 100644 data/themes/default/combobox_arrow_selected.png mode change 100755 => 100644 data/themes/default/combobox_bg.png mode change 100755 => 100644 data/themes/default/computer.png mode change 100755 => 100644 data/themes/default/controller.png mode change 100755 => 100644 data/themes/default/cpuload_bg.png mode change 100755 => 100644 data/themes/default/cpuload_leds.png mode change 100755 => 100644 data/themes/default/dont_know.png mode change 100755 => 100644 data/themes/default/edit_copy.png mode change 100755 => 100644 data/themes/default/edit_cut.png mode change 100755 => 100644 data/themes/default/edit_draw.png mode change 100755 => 100644 data/themes/default/edit_erase.png mode change 100755 => 100644 data/themes/default/edit_move.png mode change 100755 => 100644 data/themes/default/edit_paste.png mode change 100755 => 100644 data/themes/default/edit_redo.png mode change 100755 => 100644 data/themes/default/edit_rename.png mode change 100755 => 100644 data/themes/default/edit_select.png mode change 100755 => 100644 data/themes/default/edit_undo.png mode change 100755 => 100644 data/themes/default/effect_plugin.png mode change 100755 => 100644 data/themes/default/error.png mode change 100755 => 100644 data/themes/default/exit.png mode change 100755 => 100644 data/themes/default/exp_wave_active.png mode change 100755 => 100644 data/themes/default/exp_wave_inactive.png mode change 100755 => 100644 data/themes/default/factory_files.png mode change 100755 => 100644 data/themes/default/fader_background.png mode change 100755 => 100644 data/themes/default/fader_knob.png mode change 100755 => 100644 data/themes/default/fader_leds.png mode change 100755 => 100644 data/themes/default/file.png mode change 100755 => 100644 data/themes/default/filter_2lp.png mode change 100755 => 100644 data/themes/default/filter_ap.png mode change 100755 => 100644 data/themes/default/filter_bp.png mode change 100755 => 100644 data/themes/default/filter_hp.png mode change 100755 => 100644 data/themes/default/filter_lp.png mode change 100755 => 100644 data/themes/default/filter_notch.png mode change 100755 => 100644 data/themes/default/flip_x.png mode change 100755 => 100644 data/themes/default/flip_y.png mode change 100755 => 100644 data/themes/default/folder.png mode change 100755 => 100644 data/themes/default/folder_locked.png mode change 100755 => 100644 data/themes/default/folder_opened.png mode change 100755 => 100644 data/themes/default/fx_mixer.png mode change 100755 => 100644 data/themes/default/hand.png mode change 100755 => 100644 data/themes/default/help.png mode change 100755 => 100644 data/themes/default/hint.png mode change 100755 => 100644 data/themes/default/home.png mode change 100755 => 100644 data/themes/default/hq_mode.png mode change 100755 => 100644 data/themes/default/instrument_track.png mode change 100755 => 100644 data/themes/default/keep_stop_position.png mode change 100755 => 100644 data/themes/default/knob01.png mode change 100755 => 100644 data/themes/default/knob02.png mode change 100755 => 100644 data/themes/default/knob03.png mode change 100755 => 100644 data/themes/default/knob05.png mode change 100755 => 100644 data/themes/default/lcd_19green.png mode change 100755 => 100644 data/themes/default/lcd_19red.png mode change 100755 => 100644 data/themes/default/lcd_21pink.png mode change 100755 => 100644 data/themes/default/led_blue.png mode change 100755 => 100644 data/themes/default/led_green.png mode change 100755 => 100644 data/themes/default/led_off.png mode change 100755 => 100644 data/themes/default/led_red.png mode change 100755 => 100644 data/themes/default/led_yellow.png mode change 100755 => 100644 data/themes/default/lfo_d100_active.png mode change 100755 => 100644 data/themes/default/lfo_d100_inactive.png mode change 100755 => 100644 data/themes/default/lfo_x100_active.png mode change 100755 => 100644 data/themes/default/lfo_x100_inactive.png mode change 100755 => 100644 data/themes/default/lfo_x1_active.png mode change 100755 => 100644 data/themes/default/lfo_x1_inactive.png mode change 100755 => 100644 data/themes/default/loop_point.png mode change 100755 => 100644 data/themes/default/loop_points_off.png mode change 100755 => 100644 data/themes/default/loop_points_on.png mode change 100755 => 100644 data/themes/default/main_slider.png mode change 100755 => 100644 data/themes/default/master_pitch.png mode change 100755 => 100644 data/themes/default/master_volume.png mode change 100755 => 100644 data/themes/default/metronome.png mode change 100755 => 100644 data/themes/default/midi_file.png mode change 100755 => 100644 data/themes/default/mixer_send_off.png mode change 100755 => 100644 data/themes/default/mixer_send_on.png mode change 100755 => 100644 data/themes/default/moog_saw_wave_active.png mode change 100755 => 100644 data/themes/default/moog_saw_wave_inactive.png mode change 100755 => 100644 data/themes/default/muted.png mode change 100755 => 100644 data/themes/default/new_channel.png mode change 100755 => 100644 data/themes/default/note.png mode change 100755 => 100644 data/themes/default/note_double_whole.png mode change 100755 => 100644 data/themes/default/note_eight.png mode change 100755 => 100644 data/themes/default/note_eighth.png mode change 100755 => 100644 data/themes/default/note_half.png mode change 100755 => 100644 data/themes/default/note_none.png mode change 100755 => 100644 data/themes/default/note_quarter.png mode change 100755 => 100644 data/themes/default/note_sixteenth.png mode change 100755 => 100644 data/themes/default/note_thirtysecond.png mode change 100755 => 100644 data/themes/default/note_tripleteighth.png mode change 100755 => 100644 data/themes/default/note_triplethalf.png mode change 100755 => 100644 data/themes/default/note_tripletquarter.png mode change 100755 => 100644 data/themes/default/note_tripletsixteenth.png mode change 100755 => 100644 data/themes/default/note_tripletthirtysecond.png mode change 100755 => 100644 data/themes/default/note_whole.png mode change 100755 => 100644 data/themes/default/pat_rec.png mode change 100755 => 100644 data/themes/default/pause.png mode change 100755 => 100644 data/themes/default/piano.png mode change 100755 => 100644 data/themes/default/play.png mode change 100755 => 100644 data/themes/default/playpos_marker.png mode change 100755 => 100644 data/themes/default/plugins.png mode change 100755 => 100644 data/themes/default/ports.png mode change 100755 => 100644 data/themes/default/pr_black_key.png mode change 100755 => 100644 data/themes/default/pr_black_key_pressed.png mode change 100755 => 100644 data/themes/default/pr_white_key_big.png mode change 100755 => 100644 data/themes/default/pr_white_key_big_pressed.png mode change 100755 => 100644 data/themes/default/pr_white_key_small.png mode change 100755 => 100644 data/themes/default/pr_white_key_small_pressed.png mode change 100755 => 100644 data/themes/default/preset_file.png mode change 100755 => 100644 data/themes/default/progression_cubic_hermite.png mode change 100755 => 100644 data/themes/default/progression_discrete.png mode change 100755 => 100644 data/themes/default/progression_linear.png mode change 100755 => 100644 data/themes/default/project_export.png mode change 100755 => 100644 data/themes/default/project_file.png mode change 100755 => 100644 data/themes/default/project_import.png mode change 100755 => 100644 data/themes/default/project_new.png mode change 100755 => 100644 data/themes/default/project_new_from_template.png mode change 100755 => 100644 data/themes/default/project_notes.png mode change 100755 => 100644 data/themes/default/project_open.png mode change 100755 => 100644 data/themes/default/project_open_recent.png mode change 100755 => 100644 data/themes/default/project_save.png mode change 100755 => 100644 data/themes/default/project_saveas.png mode change 100755 => 100644 data/themes/default/quantize.png mode change 100755 => 100644 data/themes/default/random_wave_active.png mode change 100755 => 100644 data/themes/default/random_wave_inactive.png mode change 100755 => 100644 data/themes/default/receive_bg_arrow.png mode change 100755 => 100644 data/themes/default/record.png mode change 100755 => 100644 data/themes/default/record_accompany.png mode change 100755 => 100644 data/themes/default/recover.png mode change 100755 => 100644 data/themes/default/reload.png mode change 100755 => 100644 data/themes/default/round_square_wave_active.png mode change 100755 => 100644 data/themes/default/round_square_wave_inactive.png mode change 100755 => 100644 data/themes/default/sample_file.png mode change 100755 => 100644 data/themes/default/sample_track.png mode change 100755 => 100644 data/themes/default/saw_wave_active.png mode change 100755 => 100644 data/themes/default/saw_wave_inactive.png mode change 100755 => 100644 data/themes/default/sbarrow_down.png mode change 100755 => 100644 data/themes/default/sbarrow_down_d.png mode change 100755 => 100644 data/themes/default/sbarrow_left.png mode change 100755 => 100644 data/themes/default/sbarrow_left_d.png mode change 100755 => 100644 data/themes/default/sbarrow_right.png mode change 100755 => 100644 data/themes/default/sbarrow_right_d.png mode change 100755 => 100644 data/themes/default/sbarrow_up.png mode change 100755 => 100644 data/themes/default/sbarrow_up_d.png mode change 100755 => 100644 data/themes/default/scale.png mode change 100755 => 100644 data/themes/default/send_bg_arrow.png mode change 100755 => 100644 data/themes/default/setup_audio.png mode change 100755 => 100644 data/themes/default/setup_directories.png mode change 100755 => 100644 data/themes/default/setup_general.png mode change 100755 => 100644 data/themes/default/setup_midi.png mode change 100755 => 100644 data/themes/default/setup_performance.png mode change 100755 => 100644 data/themes/default/shadow_c.png mode change 100755 => 100644 data/themes/default/sin_wave_active.png mode change 100755 => 100644 data/themes/default/sin_wave_inactive.png mode change 100755 => 100644 data/themes/default/songeditor.png mode change 100755 => 100644 data/themes/default/soundfont_file.png mode change 100755 => 100644 data/themes/default/splash.png mode change 100755 => 100644 data/themes/default/square_wave_active.png mode change 100755 => 100644 data/themes/default/square_wave_inactive.png mode change 100755 => 100644 data/themes/default/step_btn_add.png mode change 100755 => 100644 data/themes/default/step_btn_duplicate.png mode change 100755 => 100644 data/themes/default/step_btn_off.png mode change 100755 => 100644 data/themes/default/step_btn_off_light.png mode change 100755 => 100644 data/themes/default/step_btn_remove.png mode change 100755 => 100644 data/themes/default/stepper-down-press.png mode change 100755 => 100644 data/themes/default/stepper-down.png mode change 100755 => 100644 data/themes/default/stepper-left-press.png mode change 100755 => 100644 data/themes/default/stepper-left.png mode change 100755 => 100644 data/themes/default/stepper-right-press.png mode change 100755 => 100644 data/themes/default/stepper-right.png mode change 100755 => 100644 data/themes/default/stepper-up-press.png mode change 100755 => 100644 data/themes/default/stepper-up.png mode change 100755 => 100644 data/themes/default/stop.png mode change 100755 => 100644 data/themes/default/tempo_sync.png mode change 100755 => 100644 data/themes/default/text_block.png mode change 100755 => 100644 data/themes/default/text_bold.png mode change 100755 => 100644 data/themes/default/text_center.png mode change 100755 => 100644 data/themes/default/text_italic.png mode change 100755 => 100644 data/themes/default/text_left.png mode change 100755 => 100644 data/themes/default/text_right.png mode change 100755 => 100644 data/themes/default/text_under.png mode change 100755 => 100644 data/themes/default/toolbar_bg.png mode change 100755 => 100644 data/themes/default/track_op_grip.png mode change 100755 => 100644 data/themes/default/track_shadow_c.png mode change 100755 => 100644 data/themes/default/trackop.png mode change 100755 => 100644 data/themes/default/trackop_c.png mode change 100755 => 100644 data/themes/default/trackop_h.png mode change 100755 => 100644 data/themes/default/triangle_wave_active.png mode change 100755 => 100644 data/themes/default/triangle_wave_inactive.png mode change 100755 => 100644 data/themes/default/uhoh.png mode change 100755 => 100644 data/themes/default/unavailable_sound.png mode change 100755 => 100644 data/themes/default/unknown_file.png mode change 100755 => 100644 data/themes/default/usr_wave_active.png mode change 100755 => 100644 data/themes/default/usr_wave_inactive.png mode change 100755 => 100644 data/themes/default/vst_plugin_file.png mode change 100755 => 100644 data/themes/default/whatsthis.png mode change 100755 => 100644 data/themes/default/white_key.png mode change 100755 => 100644 data/themes/default/white_key_pressed.png mode change 100755 => 100644 data/themes/default/white_noise_wave_active.png mode change 100755 => 100644 data/themes/default/white_noise_wave_inactive.png mode change 100755 => 100644 data/themes/default/zoom.png mode change 100755 => 100644 data/themes/default/zoom_x.png mode change 100755 => 100644 data/themes/default/zoom_y.png diff --git a/data/themes/default/add.png b/data/themes/default/add.png old mode 100755 new mode 100644 diff --git a/data/themes/default/add_automation.png b/data/themes/default/add_automation.png old mode 100755 new mode 100644 diff --git a/data/themes/default/add_bb_track.png b/data/themes/default/add_bb_track.png old mode 100755 new mode 100644 diff --git a/data/themes/default/add_folder.png b/data/themes/default/add_folder.png old mode 100755 new mode 100644 diff --git a/data/themes/default/add_sample_track.png b/data/themes/default/add_sample_track.png old mode 100755 new mode 100644 diff --git a/data/themes/default/analysis.png b/data/themes/default/analysis.png old mode 100755 new mode 100644 diff --git a/data/themes/default/apply-selected.png b/data/themes/default/apply-selected.png old mode 100755 new mode 100644 diff --git a/data/themes/default/apply.png b/data/themes/default/apply.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_down.png b/data/themes/default/arp_down.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_free.png b/data/themes/default/arp_free.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_random.png b/data/themes/default/arp_random.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_sort.png b/data/themes/default/arp_sort.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_sync.png b/data/themes/default/arp_sync.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_up.png b/data/themes/default/arp_up.png old mode 100755 new mode 100644 diff --git a/data/themes/default/arp_up_and_down.png b/data/themes/default/arp_up_and_down.png old mode 100755 new mode 100644 diff --git a/data/themes/default/automation.png b/data/themes/default/automation.png old mode 100755 new mode 100644 diff --git a/data/themes/default/automation_track.png b/data/themes/default/automation_track.png old mode 100755 new mode 100644 diff --git a/data/themes/default/autoscroll_off.png b/data/themes/default/autoscroll_off.png old mode 100755 new mode 100644 diff --git a/data/themes/default/autoscroll_on.png b/data/themes/default/autoscroll_on.png old mode 100755 new mode 100644 diff --git a/data/themes/default/back_to_start.png b/data/themes/default/back_to_start.png old mode 100755 new mode 100644 diff --git a/data/themes/default/back_to_zero.png b/data/themes/default/back_to_zero.png old mode 100755 new mode 100644 diff --git a/data/themes/default/bb_track.png b/data/themes/default/bb_track.png old mode 100755 new mode 100644 diff --git a/data/themes/default/bb_track_btn.png b/data/themes/default/bb_track_btn.png old mode 100755 new mode 100644 diff --git a/data/themes/default/black_key.png b/data/themes/default/black_key.png old mode 100755 new mode 100644 diff --git a/data/themes/default/black_key_pressed.png b/data/themes/default/black_key_pressed.png old mode 100755 new mode 100644 diff --git a/data/themes/default/cancel.png b/data/themes/default/cancel.png old mode 100755 new mode 100644 diff --git a/data/themes/default/chord.png b/data/themes/default/chord.png old mode 100755 new mode 100644 diff --git a/data/themes/default/clock.png b/data/themes/default/clock.png old mode 100755 new mode 100644 diff --git a/data/themes/default/colorize.png b/data/themes/default/colorize.png old mode 100755 new mode 100644 diff --git a/data/themes/default/combobox_arrow.png b/data/themes/default/combobox_arrow.png old mode 100755 new mode 100644 diff --git a/data/themes/default/combobox_arrow_selected.png b/data/themes/default/combobox_arrow_selected.png old mode 100755 new mode 100644 diff --git a/data/themes/default/combobox_bg.png b/data/themes/default/combobox_bg.png old mode 100755 new mode 100644 diff --git a/data/themes/default/computer.png b/data/themes/default/computer.png old mode 100755 new mode 100644 diff --git a/data/themes/default/controller.png b/data/themes/default/controller.png old mode 100755 new mode 100644 diff --git a/data/themes/default/cpuload_bg.png b/data/themes/default/cpuload_bg.png old mode 100755 new mode 100644 diff --git a/data/themes/default/cpuload_leds.png b/data/themes/default/cpuload_leds.png old mode 100755 new mode 100644 diff --git a/data/themes/default/dont_know.png b/data/themes/default/dont_know.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_copy.png b/data/themes/default/edit_copy.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_cut.png b/data/themes/default/edit_cut.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_draw.png b/data/themes/default/edit_draw.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_erase.png b/data/themes/default/edit_erase.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_move.png b/data/themes/default/edit_move.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_paste.png b/data/themes/default/edit_paste.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_redo.png b/data/themes/default/edit_redo.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_rename.png b/data/themes/default/edit_rename.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_select.png b/data/themes/default/edit_select.png old mode 100755 new mode 100644 diff --git a/data/themes/default/edit_undo.png b/data/themes/default/edit_undo.png old mode 100755 new mode 100644 diff --git a/data/themes/default/effect_plugin.png b/data/themes/default/effect_plugin.png old mode 100755 new mode 100644 diff --git a/data/themes/default/error.png b/data/themes/default/error.png old mode 100755 new mode 100644 diff --git a/data/themes/default/exit.png b/data/themes/default/exit.png old mode 100755 new mode 100644 diff --git a/data/themes/default/exp_wave_active.png b/data/themes/default/exp_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/exp_wave_inactive.png b/data/themes/default/exp_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/factory_files.png b/data/themes/default/factory_files.png old mode 100755 new mode 100644 diff --git a/data/themes/default/fader_background.png b/data/themes/default/fader_background.png old mode 100755 new mode 100644 diff --git a/data/themes/default/fader_knob.png b/data/themes/default/fader_knob.png old mode 100755 new mode 100644 diff --git a/data/themes/default/fader_leds.png b/data/themes/default/fader_leds.png old mode 100755 new mode 100644 diff --git a/data/themes/default/file.png b/data/themes/default/file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/filter_2lp.png b/data/themes/default/filter_2lp.png old mode 100755 new mode 100644 diff --git a/data/themes/default/filter_ap.png b/data/themes/default/filter_ap.png old mode 100755 new mode 100644 diff --git a/data/themes/default/filter_bp.png b/data/themes/default/filter_bp.png old mode 100755 new mode 100644 diff --git a/data/themes/default/filter_hp.png b/data/themes/default/filter_hp.png old mode 100755 new mode 100644 diff --git a/data/themes/default/filter_lp.png b/data/themes/default/filter_lp.png old mode 100755 new mode 100644 diff --git a/data/themes/default/filter_notch.png b/data/themes/default/filter_notch.png old mode 100755 new mode 100644 diff --git a/data/themes/default/flip_x.png b/data/themes/default/flip_x.png old mode 100755 new mode 100644 diff --git a/data/themes/default/flip_y.png b/data/themes/default/flip_y.png old mode 100755 new mode 100644 diff --git a/data/themes/default/folder.png b/data/themes/default/folder.png old mode 100755 new mode 100644 diff --git a/data/themes/default/folder_locked.png b/data/themes/default/folder_locked.png old mode 100755 new mode 100644 diff --git a/data/themes/default/folder_opened.png b/data/themes/default/folder_opened.png old mode 100755 new mode 100644 diff --git a/data/themes/default/fx_mixer.png b/data/themes/default/fx_mixer.png old mode 100755 new mode 100644 diff --git a/data/themes/default/hand.png b/data/themes/default/hand.png old mode 100755 new mode 100644 diff --git a/data/themes/default/help.png b/data/themes/default/help.png old mode 100755 new mode 100644 diff --git a/data/themes/default/hint.png b/data/themes/default/hint.png old mode 100755 new mode 100644 diff --git a/data/themes/default/home.png b/data/themes/default/home.png old mode 100755 new mode 100644 diff --git a/data/themes/default/hq_mode.png b/data/themes/default/hq_mode.png old mode 100755 new mode 100644 diff --git a/data/themes/default/instrument_track.png b/data/themes/default/instrument_track.png old mode 100755 new mode 100644 diff --git a/data/themes/default/keep_stop_position.png b/data/themes/default/keep_stop_position.png old mode 100755 new mode 100644 diff --git a/data/themes/default/knob01.png b/data/themes/default/knob01.png old mode 100755 new mode 100644 diff --git a/data/themes/default/knob02.png b/data/themes/default/knob02.png old mode 100755 new mode 100644 diff --git a/data/themes/default/knob03.png b/data/themes/default/knob03.png old mode 100755 new mode 100644 diff --git a/data/themes/default/knob05.png b/data/themes/default/knob05.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lcd_19green.png b/data/themes/default/lcd_19green.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lcd_19red.png b/data/themes/default/lcd_19red.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lcd_21pink.png b/data/themes/default/lcd_21pink.png old mode 100755 new mode 100644 diff --git a/data/themes/default/led_blue.png b/data/themes/default/led_blue.png old mode 100755 new mode 100644 diff --git a/data/themes/default/led_green.png b/data/themes/default/led_green.png old mode 100755 new mode 100644 diff --git a/data/themes/default/led_off.png b/data/themes/default/led_off.png old mode 100755 new mode 100644 diff --git a/data/themes/default/led_red.png b/data/themes/default/led_red.png old mode 100755 new mode 100644 diff --git a/data/themes/default/led_yellow.png b/data/themes/default/led_yellow.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lfo_d100_active.png b/data/themes/default/lfo_d100_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lfo_d100_inactive.png b/data/themes/default/lfo_d100_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lfo_x100_active.png b/data/themes/default/lfo_x100_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lfo_x100_inactive.png b/data/themes/default/lfo_x100_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lfo_x1_active.png b/data/themes/default/lfo_x1_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/lfo_x1_inactive.png b/data/themes/default/lfo_x1_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/loop_point.png b/data/themes/default/loop_point.png old mode 100755 new mode 100644 diff --git a/data/themes/default/loop_points_off.png b/data/themes/default/loop_points_off.png old mode 100755 new mode 100644 diff --git a/data/themes/default/loop_points_on.png b/data/themes/default/loop_points_on.png old mode 100755 new mode 100644 diff --git a/data/themes/default/main_slider.png b/data/themes/default/main_slider.png old mode 100755 new mode 100644 diff --git a/data/themes/default/master_pitch.png b/data/themes/default/master_pitch.png old mode 100755 new mode 100644 diff --git a/data/themes/default/master_volume.png b/data/themes/default/master_volume.png old mode 100755 new mode 100644 diff --git a/data/themes/default/metronome.png b/data/themes/default/metronome.png old mode 100755 new mode 100644 diff --git a/data/themes/default/midi_file.png b/data/themes/default/midi_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/mixer_send_off.png b/data/themes/default/mixer_send_off.png old mode 100755 new mode 100644 diff --git a/data/themes/default/mixer_send_on.png b/data/themes/default/mixer_send_on.png old mode 100755 new mode 100644 diff --git a/data/themes/default/moog_saw_wave_active.png b/data/themes/default/moog_saw_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/moog_saw_wave_inactive.png b/data/themes/default/moog_saw_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/muted.png b/data/themes/default/muted.png old mode 100755 new mode 100644 diff --git a/data/themes/default/new_channel.png b/data/themes/default/new_channel.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note.png b/data/themes/default/note.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_double_whole.png b/data/themes/default/note_double_whole.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_eight.png b/data/themes/default/note_eight.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_eighth.png b/data/themes/default/note_eighth.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_half.png b/data/themes/default/note_half.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_none.png b/data/themes/default/note_none.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_quarter.png b/data/themes/default/note_quarter.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_sixteenth.png b/data/themes/default/note_sixteenth.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_thirtysecond.png b/data/themes/default/note_thirtysecond.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_tripleteighth.png b/data/themes/default/note_tripleteighth.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_triplethalf.png b/data/themes/default/note_triplethalf.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_tripletquarter.png b/data/themes/default/note_tripletquarter.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_tripletsixteenth.png b/data/themes/default/note_tripletsixteenth.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_tripletthirtysecond.png b/data/themes/default/note_tripletthirtysecond.png old mode 100755 new mode 100644 diff --git a/data/themes/default/note_whole.png b/data/themes/default/note_whole.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pat_rec.png b/data/themes/default/pat_rec.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pause.png b/data/themes/default/pause.png old mode 100755 new mode 100644 diff --git a/data/themes/default/piano.png b/data/themes/default/piano.png old mode 100755 new mode 100644 diff --git a/data/themes/default/play.png b/data/themes/default/play.png old mode 100755 new mode 100644 diff --git a/data/themes/default/playpos_marker.png b/data/themes/default/playpos_marker.png old mode 100755 new mode 100644 diff --git a/data/themes/default/plugins.png b/data/themes/default/plugins.png old mode 100755 new mode 100644 diff --git a/data/themes/default/ports.png b/data/themes/default/ports.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pr_black_key.png b/data/themes/default/pr_black_key.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pr_black_key_pressed.png b/data/themes/default/pr_black_key_pressed.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pr_white_key_big.png b/data/themes/default/pr_white_key_big.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pr_white_key_big_pressed.png b/data/themes/default/pr_white_key_big_pressed.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pr_white_key_small.png b/data/themes/default/pr_white_key_small.png old mode 100755 new mode 100644 diff --git a/data/themes/default/pr_white_key_small_pressed.png b/data/themes/default/pr_white_key_small_pressed.png old mode 100755 new mode 100644 diff --git a/data/themes/default/preset_file.png b/data/themes/default/preset_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/progression_cubic_hermite.png b/data/themes/default/progression_cubic_hermite.png old mode 100755 new mode 100644 diff --git a/data/themes/default/progression_discrete.png b/data/themes/default/progression_discrete.png old mode 100755 new mode 100644 diff --git a/data/themes/default/progression_linear.png b/data/themes/default/progression_linear.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_export.png b/data/themes/default/project_export.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_file.png b/data/themes/default/project_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_import.png b/data/themes/default/project_import.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_new.png b/data/themes/default/project_new.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_new_from_template.png b/data/themes/default/project_new_from_template.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_notes.png b/data/themes/default/project_notes.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_open.png b/data/themes/default/project_open.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_open_recent.png b/data/themes/default/project_open_recent.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_save.png b/data/themes/default/project_save.png old mode 100755 new mode 100644 diff --git a/data/themes/default/project_saveas.png b/data/themes/default/project_saveas.png old mode 100755 new mode 100644 diff --git a/data/themes/default/quantize.png b/data/themes/default/quantize.png old mode 100755 new mode 100644 diff --git a/data/themes/default/random_wave_active.png b/data/themes/default/random_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/random_wave_inactive.png b/data/themes/default/random_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/receive_bg_arrow.png b/data/themes/default/receive_bg_arrow.png old mode 100755 new mode 100644 diff --git a/data/themes/default/record.png b/data/themes/default/record.png old mode 100755 new mode 100644 diff --git a/data/themes/default/record_accompany.png b/data/themes/default/record_accompany.png old mode 100755 new mode 100644 diff --git a/data/themes/default/recover.png b/data/themes/default/recover.png old mode 100755 new mode 100644 diff --git a/data/themes/default/reload.png b/data/themes/default/reload.png old mode 100755 new mode 100644 diff --git a/data/themes/default/round_square_wave_active.png b/data/themes/default/round_square_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/round_square_wave_inactive.png b/data/themes/default/round_square_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sample_file.png b/data/themes/default/sample_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sample_track.png b/data/themes/default/sample_track.png old mode 100755 new mode 100644 diff --git a/data/themes/default/saw_wave_active.png b/data/themes/default/saw_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/saw_wave_inactive.png b/data/themes/default/saw_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_down.png b/data/themes/default/sbarrow_down.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_down_d.png b/data/themes/default/sbarrow_down_d.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_left.png b/data/themes/default/sbarrow_left.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_left_d.png b/data/themes/default/sbarrow_left_d.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_right.png b/data/themes/default/sbarrow_right.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_right_d.png b/data/themes/default/sbarrow_right_d.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_up.png b/data/themes/default/sbarrow_up.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sbarrow_up_d.png b/data/themes/default/sbarrow_up_d.png old mode 100755 new mode 100644 diff --git a/data/themes/default/scale.png b/data/themes/default/scale.png old mode 100755 new mode 100644 diff --git a/data/themes/default/send_bg_arrow.png b/data/themes/default/send_bg_arrow.png old mode 100755 new mode 100644 diff --git a/data/themes/default/setup_audio.png b/data/themes/default/setup_audio.png old mode 100755 new mode 100644 diff --git a/data/themes/default/setup_directories.png b/data/themes/default/setup_directories.png old mode 100755 new mode 100644 diff --git a/data/themes/default/setup_general.png b/data/themes/default/setup_general.png old mode 100755 new mode 100644 diff --git a/data/themes/default/setup_midi.png b/data/themes/default/setup_midi.png old mode 100755 new mode 100644 diff --git a/data/themes/default/setup_performance.png b/data/themes/default/setup_performance.png old mode 100755 new mode 100644 diff --git a/data/themes/default/shadow_c.png b/data/themes/default/shadow_c.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sin_wave_active.png b/data/themes/default/sin_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/sin_wave_inactive.png b/data/themes/default/sin_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/songeditor.png b/data/themes/default/songeditor.png old mode 100755 new mode 100644 diff --git a/data/themes/default/soundfont_file.png b/data/themes/default/soundfont_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/splash.png b/data/themes/default/splash.png old mode 100755 new mode 100644 diff --git a/data/themes/default/square_wave_active.png b/data/themes/default/square_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/square_wave_inactive.png b/data/themes/default/square_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/step_btn_add.png b/data/themes/default/step_btn_add.png old mode 100755 new mode 100644 diff --git a/data/themes/default/step_btn_duplicate.png b/data/themes/default/step_btn_duplicate.png old mode 100755 new mode 100644 diff --git a/data/themes/default/step_btn_off.png b/data/themes/default/step_btn_off.png old mode 100755 new mode 100644 diff --git a/data/themes/default/step_btn_off_light.png b/data/themes/default/step_btn_off_light.png old mode 100755 new mode 100644 diff --git a/data/themes/default/step_btn_remove.png b/data/themes/default/step_btn_remove.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-down-press.png b/data/themes/default/stepper-down-press.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-down.png b/data/themes/default/stepper-down.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-left-press.png b/data/themes/default/stepper-left-press.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-left.png b/data/themes/default/stepper-left.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-right-press.png b/data/themes/default/stepper-right-press.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-right.png b/data/themes/default/stepper-right.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-up-press.png b/data/themes/default/stepper-up-press.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stepper-up.png b/data/themes/default/stepper-up.png old mode 100755 new mode 100644 diff --git a/data/themes/default/stop.png b/data/themes/default/stop.png old mode 100755 new mode 100644 diff --git a/data/themes/default/tempo_sync.png b/data/themes/default/tempo_sync.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_block.png b/data/themes/default/text_block.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_bold.png b/data/themes/default/text_bold.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_center.png b/data/themes/default/text_center.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_italic.png b/data/themes/default/text_italic.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_left.png b/data/themes/default/text_left.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_right.png b/data/themes/default/text_right.png old mode 100755 new mode 100644 diff --git a/data/themes/default/text_under.png b/data/themes/default/text_under.png old mode 100755 new mode 100644 diff --git a/data/themes/default/toolbar_bg.png b/data/themes/default/toolbar_bg.png old mode 100755 new mode 100644 diff --git a/data/themes/default/track_op_grip.png b/data/themes/default/track_op_grip.png old mode 100755 new mode 100644 diff --git a/data/themes/default/track_shadow_c.png b/data/themes/default/track_shadow_c.png old mode 100755 new mode 100644 diff --git a/data/themes/default/trackop.png b/data/themes/default/trackop.png old mode 100755 new mode 100644 diff --git a/data/themes/default/trackop_c.png b/data/themes/default/trackop_c.png old mode 100755 new mode 100644 diff --git a/data/themes/default/trackop_h.png b/data/themes/default/trackop_h.png old mode 100755 new mode 100644 diff --git a/data/themes/default/triangle_wave_active.png b/data/themes/default/triangle_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/triangle_wave_inactive.png b/data/themes/default/triangle_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/uhoh.png b/data/themes/default/uhoh.png old mode 100755 new mode 100644 diff --git a/data/themes/default/unavailable_sound.png b/data/themes/default/unavailable_sound.png old mode 100755 new mode 100644 diff --git a/data/themes/default/unknown_file.png b/data/themes/default/unknown_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/usr_wave_active.png b/data/themes/default/usr_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/usr_wave_inactive.png b/data/themes/default/usr_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/vst_plugin_file.png b/data/themes/default/vst_plugin_file.png old mode 100755 new mode 100644 diff --git a/data/themes/default/whatsthis.png b/data/themes/default/whatsthis.png old mode 100755 new mode 100644 diff --git a/data/themes/default/white_key.png b/data/themes/default/white_key.png old mode 100755 new mode 100644 diff --git a/data/themes/default/white_key_pressed.png b/data/themes/default/white_key_pressed.png old mode 100755 new mode 100644 diff --git a/data/themes/default/white_noise_wave_active.png b/data/themes/default/white_noise_wave_active.png old mode 100755 new mode 100644 diff --git a/data/themes/default/white_noise_wave_inactive.png b/data/themes/default/white_noise_wave_inactive.png old mode 100755 new mode 100644 diff --git a/data/themes/default/zoom.png b/data/themes/default/zoom.png old mode 100755 new mode 100644 diff --git a/data/themes/default/zoom_x.png b/data/themes/default/zoom_x.png old mode 100755 new mode 100644 diff --git a/data/themes/default/zoom_y.png b/data/themes/default/zoom_y.png old mode 100755 new mode 100644 From 88cc58600cbe0e96eec16f31117608b9816c14df Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 12 Aug 2017 10:16:34 +0900 Subject: [PATCH 09/14] Fix producing of NaN from Env/LFO parameter change while playing (#3761) Guarantee thread safety to ensure fillLevel() not to read value from wrong buffer address. --- include/EnvelopeAndLfoParameters.h | 1 + src/core/EnvelopeAndLfoParameters.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/EnvelopeAndLfoParameters.h b/include/EnvelopeAndLfoParameters.h index 4bafbde4b..747bb94c2 100644 --- a/include/EnvelopeAndLfoParameters.h +++ b/include/EnvelopeAndLfoParameters.h @@ -121,6 +121,7 @@ private: static LfoInstances * s_lfoInstances; bool m_used; + QMutex m_paramMutex; FloatModel m_predelayModel; FloatModel m_attackModel; diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index d044f98f3..d1fbaeb03 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -291,6 +291,8 @@ void EnvelopeAndLfoParameters::fillLevel( float * _buf, f_cnt_t _frame, const f_cnt_t _release_begin, const fpp_t _frames ) { + QMutexLocker m(&m_paramMutex); + if( _frame < 0 || _release_begin < 0 ) { return; @@ -402,6 +404,8 @@ void EnvelopeAndLfoParameters::loadSettings( const QDomElement & _this ) void EnvelopeAndLfoParameters::updateSampleVars() { + QMutexLocker m(&m_paramMutex); + const float frames_per_env_seg = SECS_PER_ENV_SEGMENT * Engine::mixer()->processingSampleRate(); // TODO: Remove the expKnobVals, time should be linear From a602bf9b291e7a20385ce4ab181dd876309d88dc Mon Sep 17 00:00:00 2001 From: Diego Ramos Ruggeri Date: Sun, 13 Aug 2017 11:34:44 -0300 Subject: [PATCH 10/14] Fix link to "Compiling" wiki page on README (#3765) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 661b13121..9c65c4be7 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Features Building --------- -See [Compiling LMMS](https://github.com/LMMS/lmms/wiki/Compiling-lmms) on our +See [Compiling LMMS](https://github.com/LMMS/lmms/wiki/Compiling) on our wiki for information on how to build LMMS. From 94646e28fe5652dba8de568f56a8d3ebb858f0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Tue, 15 Aug 2017 20:35:04 -0300 Subject: [PATCH 11/14] "misc" view now shows the model state, of the track use of master pitch (#3753) [cherry-picked from master] --- include/InstrumentMidiIOView.h | 5 +++++ include/InstrumentTrack.h | 1 + src/tracks/InstrumentTrack.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/include/InstrumentMidiIOView.h b/include/InstrumentMidiIOView.h index ca5bf5812..38f441942 100644 --- a/include/InstrumentMidiIOView.h +++ b/include/InstrumentMidiIOView.h @@ -72,6 +72,11 @@ public: InstrumentMiscView( InstrumentTrack *it, QWidget* parent ); ~InstrumentMiscView(); + GroupBox * pitchGroupBox() + { + return m_pitchGroupBox; + } + private: GroupBox * m_pitchGroupBox; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index a6974a1a1..6d2e42c3d 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -27,6 +27,7 @@ #define INSTRUMENT_TRACK_H #include "AudioPort.h" +#include "GroupBox.h" #include "InstrumentFunctions.h" #include "InstrumentSoundShaping.h" #include "MidiEventProcessor.h" diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index e9e4aac45..dc101dbf4 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1575,6 +1575,7 @@ void InstrumentTrackWindow::modelChanged() m_arpeggioView->setModel( &m_track->m_arpeggio ); m_midiView->setModel( &m_track->m_midiPort ); m_effectView->setModel( m_track->m_audioPort.effects() ); + m_miscView->pitchGroupBox()->setModel(&m_track->m_useMasterPitchModel); updateName(); } From 2981651cb257f58f1be7f9737b3785588ec73b14 Mon Sep 17 00:00:00 2001 From: Umcaruje Date: Wed, 16 Aug 2017 15:00:34 +0200 Subject: [PATCH 12/14] Add a border on non-fixed BB patterns (#3758) --- src/tracks/Pattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 0e24efc8b..4072c1d09 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -1094,7 +1094,7 @@ void PatternView::paintEvent( QPaintEvent * ) } // inner border - if( !beatPattern ) + if( !( fixedTCOs() && beatPattern ) ) { p.setPen( c.lighter( current ? 160 : 130 ) ); p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, From 206a08e4a9311d2a7b5dec9743912ef6b5ade265 Mon Sep 17 00:00:00 2001 From: Umcaruje Date: Thu, 17 Aug 2017 13:57:10 +0200 Subject: [PATCH 13/14] Paint a black rectangle under patterns to prevent glitches (#3759) --- data/themes/default/style.css | 4 ++-- src/gui/AutomationPatternView.cpp | 10 ++++------ src/tracks/BBTrack.cpp | 5 ++++- src/tracks/Pattern.cpp | 3 +++ src/tracks/SampleTrack.cpp | 3 +++ 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index a4fd6abd9..fc25d6180 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -615,12 +615,12 @@ TrackContainerView QLabel /* common pattern colors */ TrackContentObjectView { - qproperty-mutedColor: rgb(255,255,255,100); + qproperty-mutedColor: rgba(255,255,255,100); qproperty-mutedBackgroundColor: #373d48; qproperty-selectedColor: #006B65; qproperty-BBPatternBackground: #373d48; qproperty-textColor: #fff; - qproperty-textShadowColor: rgb(0,0,0,200); + qproperty-textShadowColor: rgba(0,0,0,200); qproperty-gradient: false; /* boolean property, set true to have a gradient */ } diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 66543816c..68bdb4bf2 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -257,7 +257,10 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) lingrad.setColorAt( 1, c.darker( 300 ) ); lingrad.setColorAt( 0, c ); - + + // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds + p.fillRect( rect(), QColor( 0, 0, 0 ) ); + if( gradient() ) { p.fillRect( rect(), lingrad ); @@ -492,8 +495,3 @@ void AutomationPatternView::scaleTimemapToFit( float oldMin, float oldMax ) m_pat->generateTangents(); } - - - - - diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 2e6e34078..a4217d23d 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -237,7 +237,10 @@ void BBTCOView::paintEvent( QPaintEvent * ) lingrad.setColorAt( 0, c.light( 130 ) ); lingrad.setColorAt( 1, c.light( 70 ) ); - + + // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds + p.fillRect( rect(), QColor( 0, 0, 0 ) ); + if( gradient() ) { p.fillRect( rect(), lingrad ); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 4072c1d09..3fa01462b 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -890,6 +890,9 @@ void PatternView::paintEvent( QPaintEvent * ) lingrad.setColorAt( beatPattern ? 0 : 1, c.darker( 300 ) ); lingrad.setColorAt( beatPattern ? 1 : 0, c ); + // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds + p.fillRect( rect(), QColor( 0, 0, 0 ) ); + if( gradient() ) { p.fillRect( rect(), lingrad ); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 45f587607..0dd491c64 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -475,6 +475,9 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) lingrad.setColorAt( 1, c.darker( 300 ) ); lingrad.setColorAt( 0, c ); + // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds + p.fillRect( rect(), QColor( 0, 0, 0 ) ); + if( gradient() ) { p.fillRect( rect(), lingrad ); From efd0d34f2babd0020d0a1d76c4dc3c4458e67458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Sat, 26 Aug 2017 01:01:01 -0300 Subject: [PATCH 14/14] same note layering when sustain pedal is pressed (#3774) [cherry-picked from master] --- src/core/NotePlayHandle.cpp | 21 ++++++++++----------- src/tracks/InstrumentTrack.cpp | 27 ++------------------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 84d888fee..0dff48fc0 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -252,8 +252,17 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) if( m_released && (!instrumentTrack()->isSustainPedalPressed() || m_releaseStarted) ) { - m_releaseStarted = true; + if (m_releaseStarted == false) + { + if( m_origin == OriginMidiInput ) + { + setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); + m_instrumentTrack->midiNoteOff( *this ); + } + + m_releaseStarted = true; + } f_cnt_t todo = framesThisPeriod; // if this note is base-note for arpeggio, always set @@ -380,16 +389,6 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) MidiTime::fromFrames( _s, Engine::framesPerTick() ), _s ); } - - // inform attached components about MIDI finished (used for recording in Piano Roll) - if (!instrumentTrack()->isSustainPedalPressed()) - { - if( m_origin == OriginMidiInput ) - { - setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); - m_instrumentTrack->midiNoteOff( *this ); - } - } } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index dc101dbf4..71cc43fb5 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -273,14 +273,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti // be deleted later automatically) Engine::mixer()->requestChangeInModel(); m_notes[event.key()]->noteOff( offset ); - - if (!(isSustainPedalPressed()) || - !(m_notes[event.key()]->origin() == - m_notes[event.key()]->OriginMidiInput)) - { - m_notes[event.key()] = NULL; - } - + m_notes[event.key()] = NULL; Engine::mixer()->doneChangeInModel(); } eventHandled = true; @@ -309,24 +302,8 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti { m_sustainPedalPressed = true; } - else if (isSustainPedalPressed()) + else { - for (NotePlayHandle*& nph : m_notes) - { - if (nph && nph->isReleased()) - { - if( nph->origin() == - nph->OriginMidiInput) - { - nph->setLength( - MidiTime( static_cast( - nph->totalFramesPlayed() / - Engine::framesPerTick() ) ) ); - midiNoteOff( *nph ); - } - nph = NULL; - } - } m_sustainPedalPressed = false; } }