diff --git a/AUTHORS b/AUTHORS index ea0fce5d0..5dd0d2536 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,6 +2,9 @@ Tobias Doerffel Maintainer, main-development, artwork etc. +Dany McRae + +development Sebastian Tilsch diff --git a/ChangeLog b/ChangeLog index 90e8d59cd..c9648619d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +2005-10-05 Dany McRae + + * resources/note_double_whole.png: + * resources/note_eighth.png: + * resources/note_half.png: + * resources/note_none.png: + * resources/note_quarter.png: + * resources/note_sixteenth.png: + * resources/note_thirtysecond.png: + * resources/note_whole.png: + * resources/xclock.png: + added icons for context-menu of tempoSyncKnob + + * src/widgets/lcd_spinbox.cpp: + emit valueChanged()-signal in wheelEvent()-method + + * include/song_editor.h: + * src/core/song_editor.cpp: + - added getBPM()-method + - emit signal if BPM is changed + + * include/arp_and_chords_tab_widget.h: + * include/envelope_and_lfo_widget.h: + * src/core/arp_and_chords_tab_widget.cpp: + * src/core/envelope_and_lfo_widget.cpp: + use new tempoSyncKnob-widget instead of traditional time-knob + + * include/tempo_sync_knob.h: + * src/widgets/tempo_sync_knob.cpp: + added tempo-sync-knob which automatically converts fixed note-length's + to a fixed time in ms everytime BPM is changed + +2005-10-03 Tobias Doerffel + + * include/basic_filters.h: + - added another moog-filter which sounds a bit better but needs MUCH + more CPU-time... + - cleaned up different filter-code-branches + +2005-10-02 Tobias Doerffel + + * include/oscillator.h: + cast to int instead of floor()ing value in oscillator::phase() which + makes the whole thing faster again... + 2005-09-29 Tobias Doerffel * src/widgets/tab_widget.cpp: diff --git a/Makefile.am b/Makefile.am index 928c50e4c..7d5e83254 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,6 +80,7 @@ lmms_MOC = \ ./tab_bar.moc \ ./tab_button.moc \ ./tab_widget.moc \ + ./tempo_sync_knob.moc \ ./timeline.moc \ ./track_container.moc \ ./track.moc \ @@ -161,6 +162,7 @@ lmms_SOURCES = \ $(srcdir)/src/widgets/tab_bar.cpp \ $(srcdir)/src/widgets/tab_widget.cpp \ $(srcdir)/src/widgets/text_float.cpp \ + $(srcdir)/src/widgets/tempo_sync_knob.cpp \ $(srcdir)/src/widgets/tooltip.cpp \ $(srcdir)/src/widgets/visualization_widget.cpp \ $(srcdir)/include/pch.h \ @@ -252,6 +254,7 @@ lmms_SOURCES = \ $(srcdir)/include/tooltip.h \ $(srcdir)/include/led_checkbox.h \ $(srcdir)/include/text_float.h \ + $(srcdir)/include/tempo_sync_knob.h \ $(srcdir)/include/setup_dialog.h \ $(srcdir)/include/empty_sg_plugin.h diff --git a/TODO b/TODO index fb63aea6f..9565199f4 100644 --- a/TODO +++ b/TODO @@ -5,7 +5,6 @@ - make usable with Qt4 - make LMMS an ALSA-sequencer-client - adchannel-toolbutton -> popup-menu with available soundgenerator-plugins -- tempo-based arpeggio - pre-listen when opening sample with QFileDialog - level-meters in output-graph and channel-track - panning-editing in piano-roll diff --git a/configure.in b/configure.in index 1335f3b64..2d3d4f5d5 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.1.1-cvs20050929, tobydox@users.sourceforge.net) -AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20050929) +AC_INIT(lmms, 0.1.1-cvs20051005, tobydox@users.sourceforge.net) +AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051005) AM_CONFIG_HEADER(config.h) diff --git a/include/arp_and_chords_tab_widget.h b/include/arp_and_chords_tab_widget.h index 5f094eb1e..ef9a7bcd2 100644 --- a/include/arp_and_chords_tab_widget.h +++ b/include/arp_and_chords_tab_widget.h @@ -46,13 +46,15 @@ #include "types.h" +class QComboBox; +class QPixmap; + class channelTrack; class groupBox; class knob; -class pixmapButton; -class QComboBox; -class QPixmap; class notePlayHandle; +class pixmapButton; +class tempoSyncKnob; const int MAX_CHORD_POLYPHONY = 10; @@ -123,7 +125,7 @@ private: groupBox * m_arpGroupBox; QComboBox * m_arpComboBox; knob * m_arpRangeKnob; - knob * m_arpTimeKnob; + tempoSyncKnob * m_arpTimeKnob; knob * m_arpGateKnob; QLabel * m_arpDirectionLbl; diff --git a/include/basic_filters.h b/include/basic_filters.h index d9da2e790..5f6bbf306 100644 --- a/include/basic_filters.h +++ b/include/basic_filters.h @@ -38,6 +38,7 @@ #include "mixer.h" #include "templates.h" +const int MOOG_VOLTAGE = 40000; template class basicFilters @@ -53,10 +54,23 @@ public: NOTCH, ALLPASS, MOOG, - DOUBLE_LOWPASS, - DOUBLE_MOOG + MOOG2, + SIMPLE_FLT_CNT, + DOUBLE_LOWPASS = 16+LOWPASS, + DOUBLE_MOOG = 16+MOOG, + DOUBLE_MOOG2 = 16+MOOG2 } ; + static inline filterTypes getFilterType( const int _idx ) + { + if( _idx < SIMPLE_FLT_CNT ) + { + return( static_cast( _idx ) ); + } + return( static_cast( DOUBLE_LOWPASS + _idx - + SIMPLE_FLT_CNT ) ); + } + inline basicFilters( const float _sampleRate ) : m_b0a0( 0.0f ), m_b1a0( 0.0f ), @@ -87,40 +101,112 @@ public: inline sampleType update( sampleType _in0, Uint8 _chnl ) { sampleType out; - if( m_type != MOOG ) + switch( m_type ) { - // filter - out = m_b0a0*_in0 + m_b1a0*m_in1[_chnl] + - m_b2a0*m_in2[_chnl] - m_a1a0*m_ou1[_chnl] - - m_a2a0*m_ou2[_chnl]; - - // push in/out buffers - m_in2[_chnl] = m_in1[_chnl]; - m_in1[_chnl] = _in0; - m_ou2[_chnl] = m_ou1[_chnl]; - - m_ou1[_chnl] = out; - } - else - { - sampleType x = _in0 - m_r*m_y4[_chnl]; + case MOOG: + case DOUBLE_MOOG: + { + sampleType x = _in0 - m_r*m_y4[_chnl]; - // Four cascaded onepole filters (bilinear transform) - m_y1[_chnl] = x*m_p + m_oldx[_chnl]*m_p - + // four cascaded onepole filters + // (bilinear transform) + m_y1[_chnl] = x*m_p + m_oldx[_chnl]*m_p - m_k*m_y1[_chnl]; - m_y2[_chnl] = m_y1[_chnl]*m_p+m_oldy1[_chnl]*m_p - - m_k*m_y2[_chnl]; - m_y3[_chnl] = m_y2[_chnl]*m_p+m_oldy2[_chnl]*m_p - - m_k*m_y3[_chnl]; - m_y4[_chnl] = m_y3[_chnl]*m_p+m_oldy3[_chnl]*m_p - - m_k*m_y4[_chnl]; + m_y2[_chnl] = m_y1[_chnl]*m_p+m_oldy1[_chnl]* + m_p - m_k*m_y2[_chnl]; + m_y3[_chnl] = m_y2[_chnl]*m_p+m_oldy2[_chnl]* + m_p - m_k*m_y3[_chnl]; + m_y4[_chnl] = m_y3[_chnl]*m_p+m_oldy3[_chnl]* + m_p - m_k*m_y4[_chnl]; - m_oldx[_chnl] = x; - m_oldy1[_chnl] = m_y1[_chnl]; - m_oldy2[_chnl] = m_y2[_chnl]; - m_oldy3[_chnl] = m_y3[_chnl]; - out = m_y4[_chnl] - m_y4[_chnl] * m_y4[_chnl] * - m_y4[_chnl] * ( 1.0f/6.0f ); + m_oldx[_chnl] = x; + m_oldy1[_chnl] = m_y1[_chnl]; + m_oldy2[_chnl] = m_y2[_chnl]; + m_oldy3[_chnl] = m_y3[_chnl]; + out = m_y4[_chnl] - m_y4[_chnl] * m_y4[_chnl] * + m_y4[_chnl] * ( 1.0f / 6.0f ); + break; + } + case MOOG2: + case DOUBLE_MOOG2: + { + const float x1 = ( _in0 - m_r * + m_oldx[_chnl] ) / MOOG_VOLTAGE; + const float tanh1 = tanhf( x1 ); + const float x2 = m_oldy1[_chnl] / MOOG_VOLTAGE; + const float tanh2 = tanhf( x2 ); + m_y1[_chnl] = m_oldy1[_chnl] + m_p * + ( tanh1 - tanh2 ); + m_oldy1[_chnl] = m_y1[_chnl]; + m_y2[_chnl] = m_oldy2[_chnl] + m_p * + ( tanhf( m_y1[_chnl] / + MOOG_VOLTAGE ) - + tanhf( m_oldy2[_chnl] / + MOOG_VOLTAGE ) ); + m_oldy2[_chnl] = m_y2[_chnl]; + m_y3[_chnl] = m_oldy3[_chnl] + m_p * + ( tanhf( m_y2[_chnl] / + MOOG_VOLTAGE ) - + tanhf( m_oldy3[_chnl] / + MOOG_VOLTAGE ) ); + m_oldy3[_chnl] = m_y3[_chnl]; + m_y4[_chnl] = m_ou1[_chnl] + m_p * + ( tanhf( m_y3[_chnl] / + MOOG_VOLTAGE ) - + tanhf( m_ou1[_chnl] / + MOOG_VOLTAGE ) ); + m_ou1[_chnl] = m_y4[_chnl]; + + m_oldx[_chnl] = ( m_y4[_chnl] + + m_ou2[_chnl] ) * 0.5f; + m_ou2[_chnl] = m_y4[_chnl]; + + // the same code again... + m_y1[_chnl] = m_oldy1[_chnl] + m_p * + ( tanh1 - tanh2 ); + m_oldy1[_chnl] = m_y1[_chnl]; + m_y2[_chnl] = m_oldy2[_chnl] + m_p * + ( tanhf( m_y1[_chnl] / + MOOG_VOLTAGE ) - + tanhf( m_oldy2[_chnl] / + MOOG_VOLTAGE ) ); + m_oldy2[_chnl] = m_y2[_chnl]; + m_y3[_chnl] = m_oldy3[_chnl] + m_p * + ( tanhf( m_y2[_chnl] / + MOOG_VOLTAGE ) - + tanhf( m_oldy3[_chnl] / + MOOG_VOLTAGE ) ); + m_oldy3[_chnl] = m_y3[_chnl]; + m_y4[_chnl] = m_ou1[_chnl] + m_p * + ( tanhf( m_y3[_chnl] / + MOOG_VOLTAGE ) - + tanhf( m_ou1[_chnl] / + MOOG_VOLTAGE ) ); + m_ou1[_chnl] = m_y4[_chnl]; + + m_oldx[_chnl] = ( m_y4[_chnl] + + m_ou2[_chnl] ) * 0.5f; + m_ou2[_chnl] = m_y4[_chnl]; + + out = m_oldx[_chnl]; + break; + } + + default: + // filter + out = m_b0a0*_in0 + + m_b1a0*m_in1[_chnl] + + m_b2a0*m_in2[_chnl] - + m_a1a0*m_ou1[_chnl] - + m_a2a0*m_ou2[_chnl]; + + // push in/out buffers + m_in2[_chnl] = m_in1[_chnl]; + m_in1[_chnl] = _in0; + m_ou2[_chnl] = m_ou1[_chnl]; + + m_ou1[_chnl] = out; + break; } if( m_subFilter != NULL ) { @@ -140,13 +226,9 @@ public: _freq = tMax( _freq, 0.01f );// limit freq for not getting // bad noise out of the filter... - if( m_type == MOOG || m_type == DOUBLE_MOOG ) + switch( m_type ) { - const float f = 2 * _freq / m_sampleRate; // [0 - 1] - m_k = 3.6f*f - 1.6f*f*f - 1; // (Empirical tunning) - m_p = (m_k+1)*0.5f; - m_r = _q*powf( M_E, ( ( 1-m_p ) * 1.386249f ) ); - if( m_type == DOUBLE_MOOG ) + case DOUBLE_MOOG: { if( m_subFilter == NULL ) { @@ -157,78 +239,121 @@ public: m_subFilter->calcFilterCoeffs( MOOG, _freq, _q ); } - } - else - { - // other filters - const float omega = 2.0f * M_PI * _freq / - m_sampleRate; - const float tsin = sinf( omega ); - const float tcos = cosf( omega ); - //float alpha; - - //if (q_is_bandwidth) - //alpha = tsin*sinhf(logf(2.0f)/2.0f*q*omega/tsin); - //else - const float alpha = tsin / ( 2.0f*_q ); - const float a0 = 1.0f / ( 1.0f+alpha ); - - if( m_type == LOWPASS || m_type == DOUBLE_LOWPASS ) + case MOOG: { - m_b0a0 = ((1.0f-tcos)/2.0f)*a0; - m_b1a0 = (1.0f-tcos)*a0; - m_b2a0 = m_b0a0;//((1.0f-tcos)/2.0f)*a0; - m_a1a0 = (-2.0f*tcos)*a0; - if( m_type == DOUBLE_LOWPASS ) + // [ 0 - 1 ] + const float f = 2 * _freq / m_sampleRate; + // (Empirical tunning) + m_k = 3.6f*f - 1.6f*f*f - 1; + m_p = (m_k+1)*0.5f; + m_r = _q*powf( M_E, ( ( 1-m_p ) * 1.386249f ) ); + break; + } + + case DOUBLE_MOOG2: + { + if( m_subFilter == NULL ) { - if( m_subFilter == NULL ) + m_subFilter = + new basicFilters( + m_sampleRate ); + } + m_subFilter->calcFilterCoeffs( MOOG2, _freq, + _q ); + } + + case MOOG2: + { + const float kfc = 2 * _freq / m_sampleRate; + const float kf = _freq / m_sampleRate; + const float kfcr = 1.8730 * ( kfc*kfc*kfc ) + + 0.4955 * ( kfc*kfc ) + + 0.6490 * kfc + 0.9988; + const float kacr = -3.9364 * ( kfc*kfc ) + + 1.8409 * kfc + 0.9968; + m_p = MOOG_VOLTAGE * ( 1 - expf( -2.0 * M_PI * + kfcr * kf ) ); + m_r = 4 * _q * kacr; + break; + } + + default: + { + // other filters + const float omega = 2.0f * M_PI * _freq / + m_sampleRate; + const float tsin = sinf( omega ); + const float tcos = cosf( omega ); + //float alpha; + + //if (q_is_bandwidth) + //alpha = tsin*sinhf(logf(2.0f)/2.0f*q*omega/ + // tsin); + //else + const float alpha = tsin / ( 2.0f * _q ); + + const float a0 = 1.0f / ( 1.0f+alpha ); + + if( m_type == LOWPASS || + m_type == DOUBLE_LOWPASS ) + { + m_b0a0 = ((1.0f-tcos)/2.0f)*a0; + m_b1a0 = (1.0f-tcos)*a0; + m_b2a0 = m_b0a0;//((1.0f-tcos)/2.0f)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + if( m_type == DOUBLE_LOWPASS ) { - m_subFilter = + if( m_subFilter == NULL ) + { + m_subFilter = new basicFilters( m_sampleRate ); - } - m_subFilter->calcFilterCoeffs( LOWPASS, + } + m_subFilter->calcFilterCoeffs( + LOWPASS, _freq, _q ); + } } + else if( m_type == HIPASS ) + { + m_b0a0 = ((1.0f+tcos)/2.0f)*a0; + m_b1a0 = (-1.0f-tcos)*a0; + m_b2a0 = m_b0a0;//((1.0f+tcos)/2.0f)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + } + else if( m_type == BANDPASS_CSG ) + { + m_b0a0 = (tsin/2.0f)*a0; + m_b1a0 = 0.0f; + m_b2a0 = (-tsin/2.0f)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + } + else if( m_type == BANDPASS_CZPG ) + { + m_b0a0 = alpha*a0; + m_b1a0 = 0.0f; + m_b2a0 = (-alpha)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + } + else if( m_type == NOTCH ) + { + m_b0a0 = a0; + m_b1a0 = (-2.0f*tcos)*a0; + m_b2a0 = a0; + m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0; + } + else if( m_type == ALLPASS ) + { + m_b0a0 = (1.0f-alpha)*a0; + m_b1a0 = (-2.0f*tcos)*a0; + m_b2a0 = 1.0;//(1.0f+alpha)*a0; + m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0; + //m_a2a0 = m_b0a0;//(1.0f-alpha)*a0; + } + m_a2a0 = (1.0f-alpha)*a0; + break; } - else if( m_type == HIPASS ) - { - m_b0a0 = ((1.0f+tcos)/2.0f)*a0; - m_b1a0 = (-1.0f-tcos)*a0; - m_b2a0 = m_b0a0;//((1.0f+tcos)/2.0f)*a0; - m_a1a0 = (-2.0f*tcos)*a0; - } - else if( m_type == BANDPASS_CSG ) - { - m_b0a0 = (tsin/2.0f)*a0; - m_b1a0 = 0.0f; - m_b2a0 = (-tsin/2.0f)*a0; - m_a1a0 = (-2.0f*tcos)*a0; - } - else if( m_type == BANDPASS_CZPG ) - { - m_b0a0 = alpha*a0; - m_b1a0 = 0.0f; - m_b2a0 = (-alpha)*a0; - m_a1a0 = (-2.0f*tcos)*a0; - } - else if( m_type == NOTCH ) - { - m_b0a0 = a0; - m_b1a0 = (-2.0f*tcos)*a0; - m_b2a0 = a0; - m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0; - } - else if( m_type == ALLPASS ) - { - m_b0a0 = (1.0f-alpha)*a0; - m_b1a0 = (-2.0f*tcos)*a0; - m_b2a0 = 1.0;//(1.0f+alpha)*a0; - m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0; - //m_a2a0 = m_b0a0;//(1.0f-alpha)*a0; - } - m_a2a0 = (1.0f-alpha)*a0; } } diff --git a/include/envelope_and_lfo_widget.h b/include/envelope_and_lfo_widget.h index 0a1d7ecf0..e1488760f 100644 --- a/include/envelope_and_lfo_widget.h +++ b/include/envelope_and_lfo_widget.h @@ -55,7 +55,7 @@ class envelopeTabWidget; class knob; class ledCheckBox; class pixmapButton; - +class tempoSyncKnob; class envelopeAndLFOWidget : public QWidget, public settings, @@ -138,7 +138,7 @@ private: // LFO-stuff knob * m_lfoPredelayKnob; knob * m_lfoAttackKnob; - knob * m_lfoSpeedKnob; + tempoSyncKnob * m_lfoSpeedKnob; knob * m_lfoAmountKnob; pixmapButton * m_sinLfoBtn; pixmapButton * m_triangleLfoBtn; diff --git a/include/knob.h b/include/knob.h index e1db04e58..f5d387e5c 100644 --- a/include/knob.h +++ b/include/knob.h @@ -1,7 +1,8 @@ /* * knob.h - powerful knob-widget * - * This file is based on the knob-widget of the Qwt Widget Library by Josef Wilgen + * This file is based on the knob-widget of the Qwt Widget Library by + * Josef Wilgen * * Linux MultiMedia Studio * Copyright (c) 2004-2005 Tobias Doerffel @@ -132,8 +133,11 @@ protected: void drawKnob( QPainter * _p ); void setPosition( const QPoint & _p ); - -private: + +// TODO: Need to figure out what is really used by tempoSyncKnob +// to get the private/protected attributes sorted out. Right +// now, just make everything protected. +//private: void layoutKnob( bool _update = TRUE ); float getValue( const QPoint & _p ); void getScrollMode( const QPoint & _p, int & _scroll_mode, diff --git a/include/oscillator.h b/include/oscillator.h index 03ff8879e..1737bb281 100644 --- a/include/oscillator.h +++ b/include/oscillator.h @@ -103,8 +103,7 @@ public: static oscillator * FASTCALL createOsc( waveShapes _wave_shape, modulationAlgos _modulation_algo, float _freq, Sint16 _phase_offset, float _volume_factor, - oscillator * _m_subOsc = NULL ); - + oscillator * _m_subOsc = NULL ); inline bool syncOk( void ) { const float v1 = m_sample * m_oscCoeff; @@ -112,19 +111,20 @@ public: // check whether v2 is in next period return( floorf( v2 ) > floorf( v1 ) ); } +#define FLOAT_TO_INT(in,out) \ + register const float round_const = -0.5f; \ + __asm__ __volatile__ ("fadd %%st,%%st(0)\n" \ + "fadd %2\n" \ + "fistpl %0\n" \ + "shrl $1,%0" : "=m" (out) : "t" (in),"m"(round_const) : "st") ; - static inline float phase( float _sample ) + static inline float phase( const float _sample ) { -#ifndef modff - float t; -#else - double t; -#endif - return( modff( _sample, &t ) ); - //return( _sample - floorf( _sample ) ); + return( _sample - static_cast( _sample ) ); } // now follow the wave-shape-routines... + static inline sampleType sinSample( float _sample ) { return( sinf( _sample * static_cast( 2.0f * M_PI @@ -157,7 +157,7 @@ public: static inline sampleType moogSawSample( float _sample ) { - const float ph= phase( _sample ); + const float ph = phase( _sample ); if( ph < 0.5f ) { return( -1.0f + ph * 4.0f ); diff --git a/include/song_editor.h b/include/song_editor.h index 07ff27ce3..ea55094cb 100644 --- a/include/song_editor.h +++ b/include/song_editor.h @@ -154,6 +154,8 @@ public: return( m_playPos[_pm] ); } + int getBPM( void ); + // every function that replaces current file (e.g. creates new file, // opens another file...) has to call this before and may only process // if this function returns true diff --git a/include/tempo_sync_knob.h b/include/tempo_sync_knob.h new file mode 100644 index 000000000..db0d122fd --- /dev/null +++ b/include/tempo_sync_knob.h @@ -0,0 +1,109 @@ +/* + * tempo_sync_knob.h - adds bpm to ms conversion for knob class + * + * This derived from the knob-widget by Tobias Doerffel + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Danny McRae + * + * 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 + * version 2 of the License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TEMPO_SYNC_KNOB_H +#define _TEMPO_SYNC_KNOB_H + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include "knob.h" + + +enum tempoSyncMode +{ + NO_SYNC, + DOUBLE_WHOLE_NOTE, + WHOLE_NOTE, + HALF_NOTE, + QUARTER_NOTE, + EIGHTH_NOTE, + SIXTEENTH_NOTE, + THIRTYSECOND_NOTE +} ; + + +class tempoSyncKnob : public knob +{ + Q_OBJECT +public: + tempoSyncKnob( int _knob_num, QWidget * _parent, const QString & _name, + float _scale = 1.0f ); + virtual ~tempoSyncKnob(); + + tempoSyncMode getSyncMode( void ); + void setSyncMode( tempoSyncMode _new_mode ); + + float getScale( void ); + void setScale( float _new_scale ); + + const QString & getSyncDescription( void ); + void setSyncDescription( const QString & _new_description ); + + const QPixmap & getSyncIcon( void ); + void setSyncIcon( const QPixmap & _new_pix ); + + +signals: + void syncModeChanged( tempoSyncMode _new_mode ); + void scaleChanged( float _new_scale ); + void syncDescriptionChanged( const QString & _new_description ); + void syncIconChanged( void ); + + +public slots: + void setTempoSync( int _note_type ); + + +protected: + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void contextMenuEvent( QContextMenuEvent * _me ); + virtual void wheelEvent( QWheelEvent * _me ); + + +protected slots: + void calculateTempoSyncTime( int _bpm ); + + +private: + tempoSyncMode m_tempoSyncMode; + float m_scale; + QPixmap m_tempoSyncIcon; + QString m_tempoSyncDescription; + + tempoSyncMode m_tempoLastSyncMode; + +} ; + + +#endif diff --git a/src/core/arp_and_chords_tab_widget.cpp b/src/core/arp_and_chords_tab_widget.cpp index 2167dbe43..875f4c85d 100644 --- a/src/core/arp_and_chords_tab_widget.cpp +++ b/src/core/arp_and_chords_tab_widget.cpp @@ -53,7 +53,7 @@ #include "knob.h" #include "tooltip.h" #include "gui_templates.h" - +#include "tempo_sync_knob.h" #ifdef HAVE_STDLIB_H #include @@ -286,7 +286,7 @@ arpAndChordsTabWidget::arpAndChordsTabWidget( channelTrack * _channel_track, "The selected arpeggio will be played within specified " "amount of octaves." ) ); - m_arpTimeKnob = new knob( knobBright_26, m_arpGroupBox, + m_arpTimeKnob = new tempoSyncKnob( knobBright_26, m_arpGroupBox, tr( "Arpeggio time" ) ); m_arpTimeKnob->setLabel( tr( "TIME" ) ); m_arpTimeKnob->setRange( 10.0, 1000.0, 1.0 ); @@ -634,6 +634,9 @@ void arpAndChordsTabWidget::saveSettings( QDomDocument & _doc, m_arpGateKnob->value() ) ); elw_de.setAttribute( "arpdir", QString::number( m_arpDirection ) ); + elw_de.setAttribute( "arpsyncmode", QString::number( + ( int ) m_arpTimeKnob->getSyncMode() ) ); + _parent.appendChild( elw_de ); } @@ -661,6 +664,8 @@ void arpAndChordsTabWidget::loadSettings( const QDomElement & _this ) m_arpGateKnob->setValue( _this.attribute( "arpgate" ).toFloat() ); m_arpDirection = static_cast( _this.attribute( "arpdir" ).toInt() ); + m_arpTimeKnob->setSyncMode( + ( tempoSyncMode ) _this.attribute( "arpsyncmode" ).toInt() ); m_arpGroupBox->setState( m_arpDirection != OFF && !_this.attribute( "arpdisabled" ).toInt() ); diff --git a/src/core/envelope_and_lfo_widget.cpp b/src/core/envelope_and_lfo_widget.cpp index f8d6c0b35..05a1d2825 100644 --- a/src/core/envelope_and_lfo_widget.cpp +++ b/src/core/envelope_and_lfo_widget.cpp @@ -59,7 +59,7 @@ #include "tooltip.h" #include "gui_templates.h" #include "led_checkbox.h" - +#include "tempo_sync_knob.h" // how long should be each envelope-segment maximal (e.g. attack)? const float SECS_PER_ENV_SEGMENT = 5.0f; @@ -297,7 +297,8 @@ envelopeAndLFOWidget::envelopeAndLFOWidget( float _value_for_zero_amount, connect( m_lfoAttackKnob, SIGNAL( valueChanged( float ) ), this, SLOT( updateAfterKnobChange( float ) ) ); - m_lfoSpeedKnob = new knob( knobBright_26, this, tr( "LFO-speed" ) ); + m_lfoSpeedKnob = new tempoSyncKnob( knobBright_26, this, tr( "LFO-speed" ) , + 20000.0 ); m_lfoSpeedKnob->setLabel( tr( "SPD" ) ); m_lfoSpeedKnob->setRange( 0.01, 1.0, 0.0001 ); m_lfoSpeedKnob->setValue( 0.1, TRUE ); @@ -584,6 +585,8 @@ void envelopeAndLFOWidget::saveSettings( QDomDocument & , m_x100Cb->isChecked() ) ); _parent.setAttribute( "ctlenvamt", QString::number( m_controlEnvAmountCb->isChecked() ) ); + _parent.setAttribute( "lfosyncmode", QString::number( + ( int ) m_lfoSpeedKnob->getSyncMode() ) ); } @@ -609,6 +612,8 @@ void envelopeAndLFOWidget::loadSettings( const QDomElement & _this ) m_x100Cb->setChecked( _this.attribute( "x100" ).toInt() ); m_controlEnvAmountCb->setChecked( _this.attribute( "ctlenvamt" ).toInt() ); + m_lfoSpeedKnob->setSyncMode( ( tempoSyncMode ) _this.attribute( + "lfosyncmode" ).toInt() ); switch( m_lfoShape ) { diff --git a/src/core/envelope_tab_widget.cpp b/src/core/envelope_tab_widget.cpp index fc62160ff..9a7c9b177 100644 --- a/src/core/envelope_tab_widget.cpp +++ b/src/core/envelope_tab_widget.cpp @@ -153,8 +153,10 @@ envelopeTabWidget::envelopeTabWidget( channelTrack * _channel_track, m_filterComboBox->addItem( tr( "Notch" ) ); m_filterComboBox->addItem( tr( "Allpass" ) ); m_filterComboBox->addItem( tr( "Moog" ) ); + m_filterComboBox->addItem( tr( "Moog 2" ) ); m_filterComboBox->addItem( tr( "2x LowPass" ) ); m_filterComboBox->addItem( tr( "2x Moog" ) ); + m_filterComboBox->addItem( tr( "2x Moog 2" ) ); #ifdef QT4 m_filterComboBox->setWhatsThis( @@ -269,7 +271,7 @@ void envelopeTabWidget::processAudioBuffer( sampleFrame * _ab, Uint32 _frames, int old_filter_cut = 0; int old_filter_res = 0; - basicFilters<>::filterTypes filter = static_cast::filterTypes>( m_filterComboBox-> + basicFilters<>::filterTypes filter = basicFilters<>::getFilterType( m_filterComboBox-> #ifdef QT4 currentIndex() #else diff --git a/src/core/piano_roll.cpp b/src/core/piano_roll.cpp index 934ee009e..c58684d5e 100644 --- a/src/core/piano_roll.cpp +++ b/src/core/piano_roll.cpp @@ -1918,6 +1918,7 @@ void pianoRoll::keyReleaseEvent( QKeyEvent * ) void pianoRoll::wheelEvent( QWheelEvent * _we ) { + _we->accept(); if( m_controlPressed ) { if( _we->delta() > 0 ) diff --git a/src/core/song_editor.cpp b/src/core/song_editor.cpp index abfd961db..289d57ce2 100644 --- a/src/core/song_editor.cpp +++ b/src/core/song_editor.cpp @@ -609,6 +609,7 @@ void songEditor::scrolled( int _new_pos ) void songEditor::wheelEvent( QWheelEvent * _we ) { + _we->accept(); if( m_controlPressed ) { if( _we->delta() > 0 ) @@ -757,6 +758,7 @@ void songEditor::setBPM( int _new_bpm ) { m_bpmSpinBox->setValue( tLimit( _new_bpm, MIN_BPM, MAX_BPM ) ); setModified(); + emit bpmChanged( _new_bpm ); } @@ -1332,6 +1334,13 @@ float songEditor::framesPerTact( void ) const +int songEditor::getBPM( void ) +{ + return( m_bpmSpinBox->value() ); +} + + + bool songEditor::mayChangeProject( void ) { diff --git a/src/widgets/lcd_spinbox.cpp b/src/widgets/lcd_spinbox.cpp index 9d7adfdd5..ca6099ae5 100644 --- a/src/widgets/lcd_spinbox.cpp +++ b/src/widgets/lcd_spinbox.cpp @@ -166,7 +166,9 @@ void lcdSpinBox::mouseReleaseEvent( QMouseEvent * _me ) void lcdSpinBox::wheelEvent( QWheelEvent * _we ) { + _we->accept(); setValue( value() + _we->delta() / 120 * m_step ); + emit valueChanged( value() ); } diff --git a/src/widgets/led_checkbox.cpp b/src/widgets/led_checkbox.cpp index 0561a0867..7f71bff55 100644 --- a/src/widgets/led_checkbox.cpp +++ b/src/widgets/led_checkbox.cpp @@ -94,7 +94,7 @@ void ledCheckBox::paintEvent( QPaintEvent * ) QPainter p( this ); #else QPixmap draw_pm( rect().size() ); - draw_pm.fill( this, rect().topLeft() ); + //draw_pm.fill( this, rect().topLeft() ); QPainter p( &draw_pm, this ); #endif diff --git a/src/widgets/tab_widget.cpp b/src/widgets/tab_widget.cpp index d1f95f0c2..4fb871835 100644 --- a/src/widgets/tab_widget.cpp +++ b/src/widgets/tab_widget.cpp @@ -207,6 +207,7 @@ void tabWidget::paintEvent( QPaintEvent * _pe ) void tabWidget::wheelEvent( QWheelEvent * _we ) { + _we->accept(); int dir = ( _we->delta() > 0 ) ? 1 : -1; int tab = m_activeTab; while( tab > -1 && static_cast( tab ) < m_widgets.count() ) diff --git a/src/widgets/tempo_sync_knob.cpp b/src/widgets/tempo_sync_knob.cpp new file mode 100644 index 000000000..5778f0a34 --- /dev/null +++ b/src/widgets/tempo_sync_knob.cpp @@ -0,0 +1,385 @@ +/* + * tempo_sync_knob.h - adds bpm to ms conversion for knob class + * + * This file is derived from the knob-widget by Tobias Doerffel + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Danny McRae + * + * 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 + * version 2 of the License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include + +#define addSeparator insertSeparator +#define addMenu insertItem + +#endif + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +#include "tempo_sync_knob.h" +#include "song_editor.h" +#include "midi_device.h" +#include "embed.h" +#include "tooltip.h" +#include "config_mgr.h" +#include "text_float.h" + + +const int WHEEL_DELTA = 120; + + + + +tempoSyncKnob::tempoSyncKnob( int _knob_num, QWidget * _parent, const QString & _name, + float _scale ) : + knob( _knob_num, _parent, _name ), + m_tempoSyncMode( NO_SYNC ), + m_scale( _scale ), + m_tempoSyncIcon( embed::getIconPixmap( "xclock" ) ), + m_tempoSyncDescription( tr( "Tempo Sync" ) ), + m_tempoLastSyncMode( NO_SYNC ) +{ + connect( songEditor::inst(), SIGNAL( bpmChanged( int ) ), + this, SLOT( calculateTempoSyncTime( int ) ) ); +} + + + + +tempoSyncKnob::~tempoSyncKnob() +{ +} + + + + +void tempoSyncKnob::contextMenuEvent( QContextMenuEvent * ) +{ + QMenu contextMenu( this ); +#ifdef QT4 + contextMenu.setTitle( accessibleName() ); +#else + QLabel * caption = new QLabel( "" + + QString( accessibleName() ) + "", this ); + caption->setPaletteBackgroundColor( QColor( 0, 0, 192 ) ); + caption->setAlignment( Qt::AlignCenter ); + contextMenu.addAction( caption ); +#endif + contextMenu.addAction( embed::getIconPixmap( "reload" ), + tr( "&Reset (%1%2)" ).arg( m_initValue ).arg( + m_hintTextAfterValue ), + this, SLOT( reset() ) ); + contextMenu.addSeparator(); + contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), + tr( "&Copy value (%1%2)" ).arg( value() ).arg( + m_hintTextAfterValue ), + this, SLOT( copyValue() ) ); + contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), + tr( "&Paste value (%1%2)" + ).arg( s_copiedValue ).arg( + m_hintTextAfterValue ), + this, SLOT( pasteValue() ) ); + contextMenu.addSeparator(); + + QMenu * syncMenu = new QMenu( this ); + int menuId; + menuId = syncMenu->addAction( embed::getIconPixmap( "note_none" ), + tr( "No Sync" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) NO_SYNC ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_double_whole" ), + tr( "Eight beats" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) DOUBLE_WHOLE_NOTE ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_whole" ), + tr( "Whole note" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) WHOLE_NOTE ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_half" ), + tr( "Half note" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) HALF_NOTE ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_quarter" ), + tr( "Quarter note" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) QUARTER_NOTE ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_eighth" ), + tr( "8th note" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) EIGHTH_NOTE ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_sixteenth" ), + tr( "16th note" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) SIXTEENTH_NOTE ); + menuId = syncMenu->addAction( embed::getIconPixmap( "note_thirtysecond" ), + tr( "32nd note" ), + this, SLOT( setTempoSync( int ) ) ); + syncMenu->setItemParameter( menuId, ( int ) THIRTYSECOND_NOTE ); + + contextMenu.addMenu( m_tempoSyncIcon, m_tempoSyncDescription, syncMenu ); + contextMenu.addSeparator(); + + contextMenu.addAction( tr( "Connect to MIDI-device" ), this, + SLOT( connectToMidiDevice() ) ); + contextMenu.addSeparator(); + contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ), + this, SLOT( displayHelp() ) ); + contextMenu.exec( QCursor::pos() ); + + delete syncMenu; +} + + + + +void tempoSyncKnob::mouseMoveEvent( QMouseEvent * _me ) +{ + if( m_scrollMode == ScrMouse ) + { + m_tempoSyncMode = NO_SYNC; + calculateTempoSyncTime( songEditor::inst()->getBPM() ); + + setPosition( _me->pos() ); + if( value() != m_prevValue ) + { + emit sliderMoved( value() ); + } + if( !configManager::inst()->value( "knobs", "classicalusability" + ).toInt() ) + { + QCursor::setPos( mapToGlobal( m_origMousePos ) ); + } + } + songEditor::inst()->setModified(); + + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + +} + + + + +void tempoSyncKnob::wheelEvent( QWheelEvent * _me ) +{ + _me->accept(); + const int inc = _me->delta() / WHEEL_DELTA; + incPages( inc ); + + m_tempoSyncMode = NO_SYNC; + calculateTempoSyncTime( songEditor::inst()->getBPM() ); + + songEditor::inst()->setModified(); + + s_textFloat->reparent( this ); + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + + QPoint( m_knobPixmap->width() + 2, 0 ) ); + s_textFloat->setVisibilityTimeOut( 1000 ); + + toolTip::add( this, m_hintTextBeforeValue+QString::number( value() ) + + m_hintTextAfterValue ); + + if( value() != m_prevValue ) + { + emit sliderMoved( value() ); + } +} + + + + +void tempoSyncKnob::setTempoSync( int _note_type ) +{ + m_tempoSyncMode = ( tempoSyncMode ) _note_type; + calculateTempoSyncTime( songEditor::inst()->getBPM() ); + songEditor::inst()->setModified(); +} + + + + +void tempoSyncKnob::calculateTempoSyncTime( int _bpm ) +{ + float conversionFactor = 1.0; + + if( m_tempoSyncMode ) + { + switch( m_tempoSyncMode ) + { + case DOUBLE_WHOLE_NOTE: + m_tempoSyncDescription = tr( "Synced to Eight Beats" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_double_whole" ); + conversionFactor = 0.125; + break; + case WHOLE_NOTE: + m_tempoSyncDescription = tr( "Synced to Whole Note" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_whole" ); + conversionFactor = 0.25; + break; + case HALF_NOTE: + m_tempoSyncDescription = tr( "Synced to Half Note" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_half" ); + conversionFactor = 0.5; + break; + case QUARTER_NOTE: + m_tempoSyncDescription = tr( "Synced to Quarter Note" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_quarter" ); + conversionFactor = 1.0; + break; + case EIGHTH_NOTE: + m_tempoSyncDescription = tr( "Synced to 8th Note" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_eighth" ); + conversionFactor = 2.0; + break; + case SIXTEENTH_NOTE: + m_tempoSyncDescription = tr( "Synced to 16th Note" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_sixteenth" ); + conversionFactor = 4.0; + break; + case THIRTYSECOND_NOTE: + m_tempoSyncDescription = tr( "Synced to 32nd Note" ); + m_tempoSyncIcon = embed::getIconPixmap( "note_thirtysecond" ); + conversionFactor = 8.0; + break; + default: + printf( "arpAndChordsTabWidget::calculateTempoSyncTime: invalid tempoSyncMode" ); + break; + } + setValue( 60000.0 / ( _bpm * conversionFactor * m_scale ), + FALSE ); + } + else + { + m_tempoSyncDescription = tr( "Tempo Sync" ); + m_tempoSyncIcon = embed::getIconPixmap( "xclock" ); + } + + if( m_tempoSyncMode != m_tempoLastSyncMode ) + { + emit syncModeChanged( m_tempoSyncMode ); + emit syncDescriptionChanged( m_tempoSyncDescription ); + emit syncIconChanged(); + } + + m_tempoLastSyncMode = m_tempoSyncMode; +} + + + + +tempoSyncMode tempoSyncKnob::getSyncMode( void ) +{ + return( m_tempoSyncMode ); +} + + + + +void tempoSyncKnob::setSyncMode( tempoSyncMode _new_mode ) +{ + m_tempoSyncMode = _new_mode; + calculateTempoSyncTime( songEditor::inst()->getBPM() ); +} + + + + +float tempoSyncKnob::getScale( void ) +{ + return( m_scale ); +} + + + + +void tempoSyncKnob::setScale( float _new_scale ) +{ + m_scale = _new_scale; + calculateTempoSyncTime( songEditor::inst()->getBPM() ); + emit scaleChanged( _new_scale ); +} + + + + +const QString & tempoSyncKnob::getSyncDescription( void ) +{ + return( m_tempoSyncDescription ); +} + + + + +void tempoSyncKnob::setSyncDescription( const QString & _new_description ) +{ + m_tempoSyncDescription = _new_description; + emit syncDescriptionChanged( _new_description ); +} + + + + +const QPixmap & tempoSyncKnob::getSyncIcon( void ) +{ + return( m_tempoSyncIcon ); +} + + + + +void tempoSyncKnob::setSyncIcon( const QPixmap & _new_icon ) +{ + m_tempoSyncIcon = _new_icon; + emit syncIconChanged(); +} + + + +#ifndef QT4 +#undef addSeparator +#endif + + +#include "tempo_sync_knob.moc" +