From c6d60e982ece9c213ad400a8b030db7ee2d4cde3 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 28 Feb 2020 19:39:13 +0100 Subject: [PATCH 001/180] Audio/MidiJack: Fix invalid read: (1) of #5408 This fixes reading from jack MIDI events in case where there are no jack MIDI events. --- src/core/midi/MidiJack.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/core/midi/MidiJack.cpp b/src/core/midi/MidiJack.cpp index 568b6dae164..664e1f16d0d 100644 --- a/src/core/midi/MidiJack.cpp +++ b/src/core/midi/MidiJack.cpp @@ -174,19 +174,22 @@ void MidiJack::JackMidiRead(jack_nframes_t nframes) jack_nframes_t event_index = 0; jack_nframes_t event_count = jack_midi_get_event_count(port_buf); - jack_midi_event_get(&in_event, port_buf, 0); - for(i=0; i Date: Wed, 15 Apr 2020 21:56:21 +0200 Subject: [PATCH 002/180] Audio/MidiJack: Fix invalid read: (2) of #5408 This patch * makes `m_stopped` atomic * initializes `m_stopped` correctly to `true` * moves the initialization of `m_stopped` to the point where jack ports are already connected --- include/AudioJack.h | 3 ++- src/core/audio/AudioJack.cpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/AudioJack.h b/include/AudioJack.h index 9bbb3bd4880..9c412e202f4 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -34,6 +34,7 @@ #include "AudioWeakJack.h" #endif +#include #include #include #include @@ -106,7 +107,7 @@ private slots: jack_client_t * m_client; bool m_active; - bool m_stopped; + std::atomic m_stopped; MidiJack *m_midiClient; QVector m_outputPorts; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index bca41356b93..ba0daae94d6 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -56,6 +56,8 @@ AudioJack::AudioJack( bool & _success_ful, Mixer* _mixer ) : m_framesDoneInCurBuf( 0 ), m_framesToDoInCurBuf( 0 ) { + m_stopped = true; + _success_ful = initJackClient(); if( _success_ful ) { @@ -201,8 +203,6 @@ bool AudioJack::initJackClient() void AudioJack::startProcessing() { - m_stopped = false; - if( m_active || m_client == NULL ) { return; @@ -245,6 +245,7 @@ void AudioJack::startProcessing() } } + m_stopped = false; free( ports ); } From e3cae805807dd512fe9c7a55e81486b87e9cb6ed Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 18 Apr 2020 10:04:21 +0200 Subject: [PATCH 003/180] Audio/MidiJack: Fix invalid read: (3) of #5408 This atomically unsets the MidiJack reference in AudioJack right before MidiJack is destroyed. This avoids AudioJack using a destroyed MidiJack object. --- include/AudioJack.h | 3 ++- src/core/audio/AudioJack.cpp | 4 ++-- src/core/midi/MidiJack.cpp | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/AudioJack.h b/include/AudioJack.h index 9c412e202f4..2006906afb2 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -58,6 +58,7 @@ class AudioJack : public QObject, public AudioDevice // the jack callback is handled here, we call the midi client so that it can read // it's midi data during the callback AudioJack * addMidiClient(MidiJack *midiClient); + void removeMidiClient(void) { m_midiClient = nullptr; } jack_client_t * jackClient() {return m_client;}; inline static QString name() @@ -109,7 +110,7 @@ private slots: bool m_active; std::atomic m_stopped; - MidiJack *m_midiClient; + std::atomic m_midiClient; QVector m_outputPorts; jack_default_audio_sample_t * * m_tempOutBufs; surroundSampleFrame * m_outBuf; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index ba0daae94d6..1c1628a0480 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -346,8 +346,8 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) // add to the following sound processing if( m_midiClient && _nframes > 0 ) { - m_midiClient->JackMidiRead(_nframes); - m_midiClient->JackMidiWrite(_nframes); + m_midiClient.load()->JackMidiRead(_nframes); + m_midiClient.load()->JackMidiWrite(_nframes); } for( int c = 0; c < channels(); ++c ) diff --git a/src/core/midi/MidiJack.cpp b/src/core/midi/MidiJack.cpp index 664e1f16d0d..f71ef4a4cbb 100644 --- a/src/core/midi/MidiJack.cpp +++ b/src/core/midi/MidiJack.cpp @@ -116,6 +116,9 @@ MidiJack::~MidiJack() { if(jackClient()) { + // remove ourselves first (atomically), so we will not get called again + m_jackAudio->removeMidiClient(); + if( jack_port_unregister( jackClient(), m_input_port) != 0){ printf("Failed to unregister jack midi input\n"); } From 737fcd3e55827f19fb7bda1813a80a9c0ff55920 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 18 Apr 2020 10:08:21 +0200 Subject: [PATCH 004/180] JackMidi: Remove confusing warning at shutdown Warning is: ``` jack_port_unregister called with an incorrect port 0 Failed to unregister jack midi output ``` --- src/core/midi/MidiJack.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/midi/MidiJack.cpp b/src/core/midi/MidiJack.cpp index f71ef4a4cbb..bd1e651113c 100644 --- a/src/core/midi/MidiJack.cpp +++ b/src/core/midi/MidiJack.cpp @@ -95,6 +95,8 @@ MidiJack::MidiJack() : /* jack midi out not implemented JackMidiWrite and sendByte needs to be functional before enabling this + If you enable this, also enable the + corresponding jack_port_unregister line below m_output_port = jack_port_register( jackClient(), "MIDI out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); @@ -123,9 +125,11 @@ MidiJack::~MidiJack() printf("Failed to unregister jack midi input\n"); } + /* Unused yet, see the corresponding jack_port_register call if( jack_port_unregister( jackClient(), m_output_port) != 0){ printf("Failed to unregister jack midi output\n"); } + */ if(m_jackClient) { From 953a0b17c154b5246d6162ec7c56681e7dc585d4 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Fri, 15 May 2020 19:16:06 +0200 Subject: [PATCH 005/180] Song Editor - Delete selected objects with backspace key (#5497) --- src/gui/editors/SongEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 716b2f73493..a8602880565 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -503,7 +503,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) { m_song->setPlayPos( 0, Song::Mode_PlaySong ); } - else if( ke->key() == Qt::Key_Delete ) + else if( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) { QVector tcoViews; QVector so = selectedObjects(); From 1e8fcbdf2df7bc5f4550dbed21a4ea5ea84fb6b4 Mon Sep 17 00:00:00 2001 From: Ibuki Date: Sun, 17 May 2020 11:15:39 +0900 Subject: [PATCH 006/180] Show VST parameter string (#5321) --- include/CustomTextKnob.h | 30 +++++++++++++ include/Knob.h | 2 +- include/aeffectx.h | 2 + plugins/VstEffect/VstEffectControls.cpp | 39 +++++++++++++++-- plugins/VstEffect/VstEffectControls.h | 5 ++- plugins/vestige/vestige.cpp | 42 ++++++++++++++++-- plugins/vestige/vestige.h | 5 ++- plugins/vst_base/RemoteVstPlugin.cpp | 57 +++++++++++++++++++++++++ plugins/vst_base/VstPlugin.cpp | 30 +++++++++++++ plugins/vst_base/VstPlugin.h | 14 ++++++ plugins/vst_base/communication.h | 2 + src/gui/CMakeLists.txt | 1 + src/gui/widgets/CustomTextKnob.cpp | 14 ++++++ 13 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 include/CustomTextKnob.h create mode 100644 src/gui/widgets/CustomTextKnob.cpp diff --git a/include/CustomTextKnob.h b/include/CustomTextKnob.h new file mode 100644 index 00000000000..baaf8820599 --- /dev/null +++ b/include/CustomTextKnob.h @@ -0,0 +1,30 @@ +/* Text customizable knob */ +#ifndef CUSTOM_TEXT_KNOB_H +#define CUSTOM_TEXT_KNOB_H + +#include "Knob.h" + +class LMMS_EXPORT CustomTextKnob : public Knob +{ +protected: + inline void setHintText( const QString & _txt_before, const QString & _txt_after ) {} // inaccessible +public: + CustomTextKnob( knobTypes _knob_num, QWidget * _parent = NULL, const QString & _name = QString(), const QString & _value_text = QString() ); + + CustomTextKnob( QWidget * _parent = NULL, const QString & _name = QString(), const QString & _value_text = QString() ); //!< default ctor + + CustomTextKnob( const Knob& other ) = delete; + + inline void setValueText(const QString & _value_text) + { + m_value_text = _value_text; + } + +private: + virtual QString displayValue() const; + +protected: + QString m_value_text; +} ; + +#endif diff --git a/include/Knob.h b/include/Knob.h index 4f806473118..0bba58afd44 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -143,7 +143,7 @@ private slots: void toggleScale(); private: - QString displayValue() const; + virtual QString displayValue() const; void doConnections() override; diff --git a/include/aeffectx.h b/include/aeffectx.h index 133d925acd8..b7a78dfad2b 100644 --- a/include/aeffectx.h +++ b/include/aeffectx.h @@ -97,6 +97,8 @@ const int effClose = 1; // currently unused const int effSetProgram = 2; // currently unused const int effGetProgram = 3; // currently unused const int effGetProgramName = 5; // currently unused +const int effGetParamLabel = 6; +const int effGetParamDisplay = 7; const int effGetParamName = 8; // currently unused const int effSetSampleRate = 10; const int effSetBlockSize = 11; diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index a0f97ce715e..ab740838855 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -350,7 +350,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * const QMap & dump = m_effect->m_plugin->parameterDump(); m_vi->paramCount = dump.size(); - vstKnobs = new Knob *[ m_vi->paramCount ]; + vstKnobs = new CustomTextKnob *[ m_vi->paramCount ]; bool hasKnobModel = true; if (m_vi->knobFModel == NULL) { @@ -366,8 +366,8 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * sprintf( paramStr, "param%d", i); s_dumpValues = dump[ paramStr ].split( ":" ); - vstKnobs[ i ] = new Knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); - vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + vstKnobs[ i ] = new CustomTextKnob( knobBright_26, widget, s_dumpValues.at( 1 ) ); + vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" ); vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); if( !hasKnobModel ) @@ -382,6 +382,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * [this, model]() { setParameter( model ); }, Qt::DirectConnection); vstKnobs[ i ] ->setModel( model ); } + syncParameterText(); int i = 0; for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ ) @@ -444,6 +445,7 @@ void manageVSTEffectView::syncPlugin( void ) m_vi2->knobFModel[ i ]->setInitValue( f_value ); } } + syncParameterText(); } @@ -479,9 +481,40 @@ void manageVSTEffectView::setParameter( Model * action ) if ( m_effect->m_plugin != NULL ) { m_effect->m_plugin->setParam( knobUNID, m_vi2->knobFModel[knobUNID]->value() ); + syncParameterText(); } } +void manageVSTEffectView::syncParameterText() +{ + m_effect->m_plugin->loadParameterLabels(); + m_effect->m_plugin->loadParameterDisplays(); + + QString paramLabelStr = m_effect->m_plugin->allParameterLabels(); + QString paramDisplayStr = m_effect->m_plugin->allParameterDisplays(); + + QStringList paramLabelList; + QStringList paramDisplayList; + + for( int i = 0; i < paramLabelStr.size(); ) + { + const int length = paramLabelStr[i].digitValue(); + paramLabelList.append(paramLabelStr.mid(i + 1, length)); + i += length + 1; + } + + for( int i = 0; i < paramDisplayStr.size(); ) + { + const int length = paramDisplayStr[i].digitValue(); + paramDisplayList.append(paramDisplayStr.mid(i + 1, length)); + i += length + 1; + } + + for( int i = 0; i < paramLabelList.size(); ++i ) + { + vstKnobs[i]->setValueText(paramDisplayList[i] + ' ' + paramLabelList[i]); + } +} diff --git a/plugins/VstEffect/VstEffectControls.h b/plugins/VstEffect/VstEffectControls.h index 092669f949f..cb98d0f439a 100644 --- a/plugins/VstEffect/VstEffectControls.h +++ b/plugins/VstEffect/VstEffectControls.h @@ -34,7 +34,7 @@ #include #include -#include "Knob.h" +#include "CustomTextKnob.h" #include #include #include @@ -111,6 +111,7 @@ protected slots: void syncPlugin( void ); void displayAutomatedOnly( void ); void setParameter( Model * action ); + void syncParameterText(); void closeWindow(); private: @@ -129,7 +130,7 @@ protected slots: QPushButton * m_syncButton; QPushButton * m_displayAutomatedOnly; QPushButton * m_closeButton; - Knob ** vstKnobs; + CustomTextKnob ** vstKnobs; } ; diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 9efd3c97b4d..0d75992a4e5 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -38,6 +38,8 @@ #include #include +#include + #include "ConfigManager.h" #include "BufferManager.h" #include "ConfigManager.h" @@ -965,7 +967,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume const QMap & dump = m_vi->m_plugin->parameterDump(); m_vi->paramCount = dump.size(); - vstKnobs = new Knob *[ m_vi->paramCount ]; + vstKnobs = new CustomTextKnob *[ m_vi->paramCount ]; bool hasKnobModel = true; if (m_vi->knobFModel == NULL) { @@ -981,8 +983,8 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume sprintf( paramStr, "param%d", i); s_dumpValues = dump[ paramStr ].split( ":" ); - vstKnobs[ i ] = new Knob( knobBright_26, this, s_dumpValues.at( 1 ) ); - vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + vstKnobs[ i ] = new CustomTextKnob( knobBright_26, this, s_dumpValues.at( 1 ) ); + vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" ); vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); if( !hasKnobModel ) @@ -997,6 +999,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume [this, model]() { setParameter( model ); }, Qt::DirectConnection); vstKnobs[i] ->setModel( model ); } + syncParameterText(); int i = 0; for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ ) @@ -1057,6 +1060,7 @@ void manageVestigeInstrumentView::syncPlugin( void ) m_vi->knobFModel[ i ]->setInitValue( f_value ); } } + syncParameterText(); } @@ -1132,6 +1136,38 @@ void manageVestigeInstrumentView::setParameter( Model * action ) if ( m_vi->m_plugin != NULL ) { m_vi->m_plugin->setParam( knobUNID, m_vi->knobFModel[knobUNID]->value() ); + syncParameterText(); + } +} + +void manageVestigeInstrumentView::syncParameterText() +{ + m_vi->m_plugin->loadParameterLabels(); + m_vi->m_plugin->loadParameterDisplays(); + + QString paramLabelStr = m_vi->m_plugin->allParameterLabels(); + QString paramDisplayStr = m_vi->m_plugin->allParameterDisplays(); + + QStringList paramLabelList; + QStringList paramDisplayList; + + for( int i = 0; i < paramLabelStr.size(); ) + { + const int length = paramLabelStr[i].digitValue(); + paramLabelList.append(paramLabelStr.mid(i + 1, length)); + i += length + 1; + } + + for( int i = 0; i < paramDisplayStr.size(); ) + { + const int length = paramDisplayStr[i].digitValue(); + paramDisplayList.append(paramDisplayStr.mid(i + 1, length)); + i += length + 1; + } + + for( int i = 0; i < paramLabelList.size(); ++i ) + { + vstKnobs[i]->setValueText(paramDisplayList[i] + ' ' + paramLabelList[i]); } } diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index 7090fee1e29..ae4f803cf14 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -34,7 +34,7 @@ #include "Instrument.h" #include "InstrumentView.h" #include "Note.h" -#include "Knob.h" +#include "CustomTextKnob.h" #include "SubWindow.h" #include "AutomatableModel.h" @@ -109,6 +109,7 @@ protected slots: void syncPlugin( void ); void displayAutomatedOnly( void ); void setParameter( Model * action ); + void syncParameterText(); void closeWindow(); @@ -128,7 +129,7 @@ protected slots: QPushButton * m_syncButton; QPushButton * m_displayAutomatedOnly; QPushButton * m_closeButton; - Knob ** vstKnobs; + CustomTextKnob ** vstKnobs; } ; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index a103c065282..942c9e960c2 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -70,6 +70,8 @@ #include #include #include +#include +#include #include @@ -223,6 +225,10 @@ class RemoteVstPlugin : public RemotePluginClient // determine name of current program const char * programName(); + void getParameterDisplays(); + + void getParameterLabels(); + // send name of current program back to host void sendCurrentProgramName(); @@ -660,6 +666,14 @@ bool RemoteVstPlugin::processMessage( const message & _m ) //sendMessage( IdVstSetParameter ); break; + case IdVstParameterDisplays: + getParameterDisplays(); + break; + + case IdVstParameterLabels: + getParameterLabels(); + break; + case IdVstIdleUpdate: { @@ -1068,6 +1082,49 @@ const char * RemoteVstPlugin::programName() +// join the ParameterDisplays (stringified values without units) and send them to host +void RemoteVstPlugin::getParameterDisplays() +{ + std::string paramDisplays; + static char buf[9]; // buffer for getting string + for (int i=0; i< m_plugin->numParams; ++i) + { + memset( buf, 0, sizeof( buf ) ); // fill with '\0' because got string may not to be ended with '\0' + pluginDispatch( effGetParamDisplay, i, 0, buf ); + buf[8] = 0; + + // each field shaped like: [length:number][content:string] + paramDisplays += '0' + strlen(buf); // add length descriptor (length is up to 8) + paramDisplays += buf; + } + + sendMessage( message( IdVstParameterDisplays ).addString( paramDisplays.c_str() ) ); +} + + + +// join the ParameterLabels (units) and send them to host +void RemoteVstPlugin::getParameterLabels() +{ + std::string paramLabels; + static char buf[9]; // buffer for getting string + for (int i=0; i< m_plugin->numParams; ++i) + { + memset( buf, 0, sizeof( buf ) ); // fill with '\0' because got string may not to be ended with '\0' + pluginDispatch( effGetParamLabel, i, 0, buf ); + buf[8] = 0; + + // each field shaped like: [length:number][content:string] + paramLabels += '0' + strlen(buf); // add length descriptor (length is up to 8) + paramLabels += buf; + } + + sendMessage( message( IdVstParameterLabels ).addString( paramLabels.c_str() ) ); +} + + + + void RemoteVstPlugin::sendCurrentProgramName() { char presName[64]; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 8d86f576f33..cd4844b8128 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -438,6 +438,14 @@ bool VstPlugin::processMessage( const message & _m ) m_allProgramNames = _m.getQString(); break; + case IdVstParameterLabels: + m_allParameterLabels = _m.getQString(); + break; + + case IdVstParameterDisplays: + m_allParameterDisplays = _m.getQString(); + break; + case IdVstPluginUniqueID: // TODO: display graphically in case of failure printf("unique ID: %s\n", _m.getString().c_str() ); @@ -531,6 +539,28 @@ void VstPlugin::loadProgramNames() +void VstPlugin::loadParameterLabels() +{ + lock(); + sendMessage( message( IdVstParameterLabels ) ); + waitForMessage( IdVstParameterLabels, true ); + unlock(); +} + + + + +void VstPlugin::loadParameterDisplays() +{ + lock(); + sendMessage( message( IdVstParameterDisplays ) ); + waitForMessage( IdVstParameterDisplays, true ); + unlock(); +} + + + + void VstPlugin::savePreset( ) { QString presName = currentProgramName().isEmpty() ? tr(": default") : currentProgramName(); diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 26e7fec369a..e05031256eb 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -91,6 +91,16 @@ class VSTBASE_EXPORT VstPlugin : public RemotePlugin, public JournallingObject return m_allProgramNames; } + inline const QString& allParameterLabels() const + { + return m_allParameterLabels; + } + + inline const QString& allParameterDisplays() const + { + return m_allParameterDisplays; + } + int currentProgram(); const QMap & parameterDump(); @@ -120,6 +130,8 @@ public slots: void setProgram( int index ); void rotateProgram( int offset ); void loadProgramNames(); + void loadParameterLabels(); + void loadParameterDisplays(); void savePreset( void ); void setParam( int i, float f ); void idleUpdate(); @@ -148,6 +160,8 @@ public slots: QString m_productString; QString m_currentProgramName; QString m_allProgramNames; + QString m_allParameterLabels; + QString m_allParameterDisplays; QString p_name; diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index 756fd75573e..c25b213d18d 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -67,6 +67,8 @@ enum VstRemoteMessageIDs IdVstSetProgram, IdVstRotateProgram, IdVstIdleUpdate, + IdVstParameterDisplays, + IdVstParameterLabels, // remoteVstPlugin -> vstPlugin IdVstFailedLoadingPlugin, diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a340867a837..759be387290 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -54,6 +54,7 @@ SET(LMMS_SRCS gui/widgets/ControllerView.cpp gui/widgets/Controls.cpp gui/widgets/CPULoadWidget.cpp + gui/widgets/CustomTextKnob.cpp gui/widgets/EffectRackView.cpp gui/widgets/EffectView.cpp gui/widgets/EnvelopeAndLfoView.cpp diff --git a/src/gui/widgets/CustomTextKnob.cpp b/src/gui/widgets/CustomTextKnob.cpp new file mode 100644 index 00000000000..149b3494be6 --- /dev/null +++ b/src/gui/widgets/CustomTextKnob.cpp @@ -0,0 +1,14 @@ +#include "CustomTextKnob.h" + +CustomTextKnob::CustomTextKnob( knobTypes _knob_num, QWidget * _parent, const QString & _name, const QString & _value_text ) : + Knob( _knob_num, _parent, _name ), + m_value_text( _value_text ) {} + +CustomTextKnob::CustomTextKnob( QWidget * _parent, const QString & _name, const QString & _value_text ) : //!< default ctor + Knob( _parent, _name ), + m_value_text( _value_text ) {} + +QString CustomTextKnob::displayValue() const +{ + return m_description.trimmed() + m_value_text; +} From b818234c153e68fbf4468a2b4f2f8eb0ba8c1bc4 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 17 May 2020 09:17:37 +0200 Subject: [PATCH 007/180] ControlLayout: Allow removing search bar's focus --- include/ControlLayout.h | 3 +++ include/LinkedModelGroupViews.h | 2 ++ src/gui/widgets/ControlLayout.cpp | 5 +++++ src/gui/widgets/LinkedModelGroupViews.cpp | 8 ++++++++ 4 files changed, 18 insertions(+) diff --git a/include/ControlLayout.h b/include/ControlLayout.h index 625b3f4681d..60182010792 100644 --- a/include/ControlLayout.h +++ b/include/ControlLayout.h @@ -110,6 +110,9 @@ class ControlLayout : public QLayout void setGeometry(const QRect &rect) override; QSize sizeHint() const override; QLayoutItem *takeAt(int index) override; + //! remove focus from QLineEdit search bar + //! this may be useful if the mouse is outside the layout + void removeFocusFromSearchBar(); private slots: void onTextChanged(const QString&); diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 79fab76afd5..390d2556890 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -71,6 +71,8 @@ class LinkedModelGroupView : public QWidget void removeControl(const QString &key); + void removeFocusFromSearchBar(); + private: class LinkedModelGroup* m_model; diff --git a/src/gui/widgets/ControlLayout.cpp b/src/gui/widgets/ControlLayout.cpp index ce2e9e4ef9b..aa265b14e5b 100644 --- a/src/gui/widgets/ControlLayout.cpp +++ b/src/gui/widgets/ControlLayout.cpp @@ -177,6 +177,11 @@ QLayoutItem *ControlLayout::takeAt(int index) return (itr == m_itemMap.end()) ? nullptr : m_itemMap.take(itr.key()); } +void ControlLayout::removeFocusFromSearchBar() +{ + m_searchBar->clearFocus(); +} + Qt::Orientations ControlLayout::expandingDirections() const { return Qt::Orientations(); diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 21d40efcc1e..1b4bd8bbd75 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -142,6 +142,14 @@ void LinkedModelGroupView::removeControl(const QString& key) } + + +void LinkedModelGroupView::removeFocusFromSearchBar() +{ + m_layout->removeFocusFromSearchBar(); +} + + /* LinkedModelGroupsViewBase */ From 29a5abc30b5d5efa30b7f6b73fe06d76ec851cca Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Mon, 18 May 2020 18:58:48 +0200 Subject: [PATCH 008/180] Fix Qt 5.15 build issues (#5498) Add missing QPainterPath includes --- plugins/Eq/EqSpectrumView.h | 1 + src/gui/AutomationPatternView.cpp | 1 + src/gui/LmmsStyle.cpp | 1 + src/gui/editors/AutomationEditor.cpp | 1 + 4 files changed, 4 insertions(+) diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index cd3f1775863..84feeff13e8 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -24,6 +24,7 @@ #define EQSPECTRUMVIEW_H #include +#include #include #include "fft_helpers.h" diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 448c233cb4c..9889a6b4604 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include "AutomationEditor.h" diff --git a/src/gui/LmmsStyle.cpp b/src/gui/LmmsStyle.cpp index e57e29e4790..4fe5cb503c7 100644 --- a/src/gui/LmmsStyle.cpp +++ b/src/gui/LmmsStyle.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 5e8ea8a0afc..74f15d358c6 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include From 7f9b4c2401975b4ab962cea6e30ba43248dec096 Mon Sep 17 00:00:00 2001 From: necrashter Date: Tue, 19 May 2020 05:34:54 +0300 Subject: [PATCH 009/180] Implement fade in to prevent Triple Osc from clicking (#5199) --- include/Instrument.h | 3 + include/NotePlayHandle.h | 3 + .../triple_oscillator/TripleOscillator.cpp | 1 + src/core/Instrument.cpp | 97 ++++++++++++++++++- 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/include/Instrument.h b/include/Instrument.h index 438197cd8f0..1dbf1ba5816 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -132,6 +132,9 @@ class LMMS_EXPORT Instrument : public Plugin protected: + // fade in to prevent clicks + void applyFadeIn(sampleFrame * buf, NotePlayHandle * n); + // instruments may use this to apply a soft fade out at the end of // notes - method does this only if really less or equal // desiredReleaseFrames() frames are left diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 3dba0f277d7..e17a5e3a25e 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -49,6 +49,9 @@ class LMMS_EXPORT NotePlayHandle : public PlayHandle, public Note void * m_pluginData; std::unique_ptr> m_filter; + // length of the declicking fade in + fpp_t m_fadeInLength; + // specifies origin of NotePlayHandle enum Origins { diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 2a1eccdd73c..8093d218c04 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -364,6 +364,7 @@ void TripleOscillator::playNote( NotePlayHandle * _n, osc_l->update( _working_buffer + offset, frames, 0 ); osc_r->update( _working_buffer + offset, frames, 1 ); + applyFadeIn(_working_buffer, _n); applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index ba608da14a2..3e8dc80742e 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -23,8 +23,12 @@ */ #include "Instrument.h" -#include "InstrumentTrack.h" + +#include + #include "DummyInstrument.h" +#include "InstrumentTrack.h" +#include "lmms_constants.h" Instrument::Instrument(InstrumentTrack * _instrument_track, @@ -78,8 +82,96 @@ bool Instrument::isFromTrack( const Track * _track ) const return( m_instrumentTrack == _track ); } +// helper function for Instrument::applyFadeIn +static int countZeroCrossings(sampleFrame *buf, fpp_t start, fpp_t frames) +{ + // zero point crossing counts of all channels + int zeroCrossings[DEFAULT_CHANNELS] = {0}; + // maximum zero point crossing of all channels + int maxZeroCrossings = 0; + + // determine the zero point crossing counts + for (fpp_t f = start; f < frames; ++f) + { + for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch) + { + // we don't want to count [-1, 0, 1] as two crossings + if ((buf[f - 1][ch] <= 0.0 && buf[f][ch] > 0.0) || + (buf[f - 1][ch] >= 0.0 && buf[f][ch] < 0.0)) + { + ++zeroCrossings[ch]; + if (zeroCrossings[ch] > maxZeroCrossings) + { + maxZeroCrossings = zeroCrossings[ch]; + } + } + } + } + + return maxZeroCrossings; +} + +// helper function for Instrument::applyFadeIn +fpp_t getFadeInLength(float maxLength, fpp_t frames, int zeroCrossings) +{ + // calculate the length of the fade in + // Length is inversely proportional to the max of zeroCrossings, + // because for low frequencies, we need a longer fade in to + // prevent clicking. + return (fpp_t) (maxLength / ((float) zeroCrossings / ((float) frames / 128.0f) + 1.0f)); +} + +void Instrument::applyFadeIn(sampleFrame * buf, NotePlayHandle * n) +{ + const static float MAX_FADE_IN_LENGTH = 85.0; + f_cnt_t total = n->totalFramesPlayed(); + if (total == 0) + { + const fpp_t frames = n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = n->offset(); + // We need to skip the first sample because it almost always + // produces a zero crossing; it's not helpful while + // determining the fade in length. Hence 1 + int maxZeroCrossings = countZeroCrossings(buf, offset + 1, offset + frames); + + fpp_t length = getFadeInLength(MAX_FADE_IN_LENGTH, frames, maxZeroCrossings); + n->m_fadeInLength = length; + + // apply fade in + length = length < frames ? length : frames; + for (fpp_t f = 0; f < length; ++f) + { + for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch) + { + buf[offset + f][ch] *= 0.5 - 0.5 * cosf(F_PI * (float) f / (float) n->m_fadeInLength); + } + } + } + else if (total < n->m_fadeInLength) + { + const fpp_t frames = n->framesLeftForCurrentPeriod(); + + int new_zc = countZeroCrossings(buf, 1, frames); + fpp_t new_length = getFadeInLength(MAX_FADE_IN_LENGTH, frames, new_zc); + + for (fpp_t f = 0; f < frames; ++f) + { + for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch) + { + float currentLength = n->m_fadeInLength * (1.0f - (float) f / frames) + new_length * ((float) f / frames); + buf[f][ch] *= 0.5 - 0.5 * cosf(F_PI * (float) (total + f) / currentLength); + if (total + f >= currentLength) + { + n->m_fadeInLength = currentLength; + return; + } + } + } + n->m_fadeInLength = new_length; + } +} void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n ) { @@ -109,6 +201,3 @@ QString Instrument::fullDisplayName() const { return instrumentTrack()->displayName(); } - - - From 8a190e4e1350b18a046820cfe523b806482772d2 Mon Sep 17 00:00:00 2001 From: Cyp <48363+Cyp@users.noreply.github.com> Date: Tue, 19 May 2020 05:28:03 +0200 Subject: [PATCH 010/180] Fix wrong lengths of exported tracks, when tracks have different lengths. (#5348) * Fix wrong lengths of exported tracks, when tracks have different lengths. Song::updateLength() was called in ProjectRenderer::run() after Song::startExport(), updating m_length too late, resulting in it being used as the length of the wrong track. * Fix "Export as loop" resetting after rendering the first track Co-authored-by: Hyunjin Song --- src/core/ProjectRenderer.cpp | 1 - src/core/RenderManager.cpp | 4 ++-- src/core/Song.cpp | 15 +++++++-------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 19bdf4be806..0616a925278 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -181,7 +181,6 @@ void ProjectRenderer::run() PerfLogTimer perfLog("Project Render"); Engine::getSong()->startExport(); - Engine::getSong()->updateLength(); // Skip first empty buffer. Engine::mixer()->nextBuffer(); diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 785aa9fb16d..69255442cd4 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -80,9 +80,9 @@ void RenderManager::renderNextTrack() m_tracksToRender.pop_back(); // mute everything but the track we are about to render - for( auto it = m_unmuted.begin(); it != m_unmuted.end(); ++it ) + for (auto track : m_unmuted) { - (*it)->setMuted( (*it) != renderTrack ); + track->setMuted(track != renderTrack); } // for multi-render, prefix each output file with a different number diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 60ab3bfbce7..b12b98573f0 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -612,16 +612,14 @@ void Song::updateLength() { m_length = 0; m_tracksMutex.lockForRead(); - for( TrackList::const_iterator it = tracks().begin(); - it != tracks().end(); ++it ) + for (auto track : tracks()) { - if( Engine::getSong()->isExporting() && - ( *it )->isMuted() ) + if (m_exporting && track->isMuted()) { continue; } - const bar_t cur = ( *it )->length(); + const bar_t cur = track->length(); if( cur > m_length ) { m_length = cur; @@ -744,6 +742,10 @@ void Song::stop() void Song::startExport() { stop(); + + m_exporting = true; + updateLength(); + if (m_renderBetweenMarkers) { m_exportSongBegin = m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin(); @@ -781,8 +783,6 @@ void Song::startExport() playSong(); - m_exporting = true; - m_vstSyncController.setPlaybackState( true ); } @@ -793,7 +793,6 @@ void Song::stopExport() { stop(); m_exporting = false; - m_exportLoop = false; m_vstSyncController.setPlaybackState( m_playing ); } From b6b75a5f21a3cd6505b6517de7e7214b8bd66f19 Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+DouglasDGI@users.noreply.github.com> Date: Tue, 19 May 2020 10:15:03 -0600 Subject: [PATCH 011/180] Divide knob step size by 1000 for LADSPA effects (#4574) --- src/core/LadspaControl.cpp | 2 +- src/gui/widgets/Knob.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/LadspaControl.cpp b/src/core/LadspaControl.cpp index 731241b8541..68f2ee643b7 100644 --- a/src/core/LadspaControl.cpp +++ b/src/core/LadspaControl.cpp @@ -80,7 +80,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, ( m_port->max - m_port->min ) / ( m_port->name.toUpper() == "GAIN" && m_port->max == 10.0f ? 4000.0f : - ( m_port->suggests_logscale ? 8000.0f : 800.0f ) ) ); + ( m_port->suggests_logscale ? 8000000.0f : 800000.0f ) ) ); m_knobModel.setInitValue( m_port->def ); connect( &m_knobModel, SIGNAL( dataChanged() ), this, SLOT( knobChanged() ) ); diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 167c3ecf883..2546582f663 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -695,7 +695,8 @@ void Knob::paintEvent( QPaintEvent * _me ) void Knob::wheelEvent( QWheelEvent * _we ) { _we->accept(); - const int inc = ( _we->delta() > 0 ) ? 1 : -1; + const float stepMult = model()->range() / 2000 / model()->step(); + const int inc = ( ( _we->delta() > 0 ) ? 1 : -1 ) * ( ( stepMult < 1 ) ? 1 : stepMult ); model()->incValue( inc ); From c3e056a21df4601f1d537b0719c42eff523c9935 Mon Sep 17 00:00:00 2001 From: bahaokten Date: Tue, 19 May 2020 13:02:01 -0400 Subject: [PATCH 012/180] Implement compress action for commandline use (#5341) --- doc/bash-completion/lmms | 2 +- src/core/main.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/bash-completion/lmms b/doc/bash-completion/lmms index ccff8f249d9..b582612bddc 100644 --- a/doc/bash-completion/lmms +++ b/doc/bash-completion/lmms @@ -89,7 +89,7 @@ _lmms() pars_render=(--float --bitrate --format --interpolation) pars_render+=(--loop --mode --output --profile) pars_render+=(--samplerate --oversampling) - actions=(dump render rendertracks upgrade) + actions=(dump compress render rendertracks upgrade) actions_old=(-d --dump -r --render --rendertracks -u --upgrade) shortargs+=(-a -b -c -f -h -i -l -m -o -p -s -v -x) diff --git a/src/core/main.cpp b/src/core/main.cpp index 26d12713c8a..9ff89478515 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -161,6 +161,7 @@ void printHelp() "Actions:\n" " [options...] [] Start LMMS in normal GUI mode\n" " dump Dump XML of compressed file \n" + " compress Compress file \n" " render [options...] Render given project file\n" " rendertracks [options...] Render each track to a different file\n" " upgrade [out] Upgrade file and save as \n" @@ -427,6 +428,22 @@ int main( int argc, char * * argv ) return EXIT_SUCCESS; } + else if( arg == "compress" || arg == "--compress" ) + { + ++i; + + if( i == argc ) + { + return noInputFileError(); + } + + QFile f( QString::fromLocal8Bit( argv[i] ) ); + f.open( QIODevice::ReadOnly ); + QByteArray d = qCompress( f.readAll() ) ; + fwrite( d.constData(), sizeof(char), d.size(), stdout ); + + return EXIT_SUCCESS; + } else if( arg == "render" || arg == "--render" || arg == "-r" || arg == "rendertracks" || arg == "--rendertracks" ) { From 109cdf6cf3c3ee3c40d228cb694da2521ac5b689 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Thu, 21 May 2020 22:45:01 +0200 Subject: [PATCH 013/180] Fix #5483: sf2_player: No crash when file is no soundfont (#5487) --- plugins/sf2_player/sf2_player.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index f7a09f01e7c..99add350574 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -358,18 +358,24 @@ void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName ) // Add to map, if doesn't exist. else { - m_fontId = fluid_synth_sfload( m_synth, sf2Ascii, true ); - - if( fluid_synth_sfcount( m_synth ) > 0 ) + bool loaded = false; + if( fluid_is_soundfont( sf2Ascii ) ) { - // Grab this sf from the top of the stack and add to list - m_font = new sf2Font( fluid_synth_get_sfont( m_synth, 0 ) ); - s_fonts.insert( relativePath, m_font ); + m_fontId = fluid_synth_sfload( m_synth, sf2Ascii, true ); + + if( fluid_synth_sfcount( m_synth ) > 0 ) + { + // Grab this sf from the top of the stack and add to list + m_font = new sf2Font( fluid_synth_get_sfont( m_synth, 0 ) ); + s_fonts.insert( relativePath, m_font ); + loaded = true; + } } - else + + if(!loaded) { - collectErrorForUI( sf2Instrument::tr( "A soundfont %1 could not be loaded." ).arg( QFileInfo( _sf2File ).baseName() ) ); - // TODO: Why is the filename missing when the file does not exist? + collectErrorForUI( sf2Instrument::tr( "A soundfont %1 could not be loaded." ). + arg( QFileInfo( _sf2File ).baseName() ) ); } } From 7aef23d209760d019716e10c65c7fcbe5f634e52 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 23 May 2020 08:59:35 +0200 Subject: [PATCH 014/180] LinkedModelGroupView: Un-focus filter edit... ... whenever the user clicks other widgets --- src/gui/widgets/LinkedModelGroupViews.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 1b4bd8bbd75..b9e007ff9d1 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -42,6 +42,11 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, m_colNum(colNum), m_layout(new ControlLayout(this)) { + // This is required to remove the focus of the line edit + // when e.g. another spin box is being clicked. + // Removing the focus is wanted because in many cases, the user wants to + // quickly play notes on the virtual keyboard. + setFocusPolicy( Qt::StrongFocus ); } From d5a2366fc8755e6600e3f44f3b7532173425ba14 Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Wed, 20 May 2020 21:37:03 +0200 Subject: [PATCH 015/180] Fix #5504: invalid warning --- src/core/Mixer.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 38ee5c46d6c..3ab180840fc 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -1203,8 +1203,18 @@ MidiClient * Mixer::tryMidiClients() printf( "midi apple didn't work: client_name=%s\n", client_name.toUtf8().constData()); #endif - printf( "Couldn't create MIDI-client, neither with ALSA nor with " - "OSS. Will use dummy-MIDI-client.\n" ); + if(client_name != MidiDummy::name()) + { + if (client_name.isEmpty()) + { + printf("Unknown MIDI-client. "); + } + else + { + printf("Couldn't create %s MIDI-client. ", client_name.toUtf8().constData()); + } + printf("Will use dummy-MIDI-client.\n"); + } m_midiClientName = MidiDummy::name(); From 2a66e83f536ed71984054392ed469991de67874e Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 24 May 2020 12:50:50 +0200 Subject: [PATCH 016/180] Lv2 core implementation Implementation of the Lv2 core, except for CV ports. No features or extensions are supported yet. You can now generate sound using Lv2 instruments (restricted to non-piano) or effects. For an explenation about the new classes, see Lv2Manager.h --- .travis/linux..install.sh | 5 +- CMakeLists.txt | 37 ++ cmake/modules/PluginList.cmake | 2 + include/Engine.h | 11 + include/Lv2Basics.h | 67 +++ include/Lv2ControlBase.h | 146 ++++++ include/Lv2Manager.h | 126 ++++++ include/Lv2Ports.h | 237 ++++++++++ include/Lv2Proc.h | 183 ++++++++ include/Lv2SubPluginFeatures.h | 61 +++ include/Lv2ViewBase.h | 97 ++++ plugins/LadspaEffect/LadspaControls.h | 1 + plugins/Lv2Effect/CMakeLists.txt | 9 + plugins/Lv2Effect/Lv2Effect.cpp | 111 +++++ plugins/Lv2Effect/Lv2Effect.h | 54 +++ plugins/Lv2Effect/Lv2FxControlDialog.cpp | 72 +++ plugins/Lv2Effect/Lv2FxControlDialog.h | 47 ++ plugins/Lv2Effect/Lv2FxControls.cpp | 104 +++++ plugins/Lv2Effect/Lv2FxControls.h | 61 +++ plugins/Lv2Effect/logo.png | Bin 0 -> 967 bytes plugins/Lv2Instrument/CMakeLists.txt | 7 + plugins/Lv2Instrument/Lv2Instrument.cpp | 303 +++++++++++++ plugins/Lv2Instrument/Lv2Instrument.h | 123 +++++ plugins/Lv2Instrument/logo.png | Bin 0 -> 967 bytes src/CMakeLists.txt | 14 + src/core/CMakeLists.txt | 7 + src/core/DataFile.cpp | 3 + src/core/Engine.cpp | 11 + src/core/lv2/Lv2Basics.cpp | 49 ++ src/core/lv2/Lv2ControlBase.cpp | 191 ++++++++ src/core/lv2/Lv2Manager.cpp | 163 +++++++ src/core/lv2/Lv2Ports.cpp | 256 +++++++++++ src/core/lv2/Lv2Proc.cpp | 544 +++++++++++++++++++++++ src/core/lv2/Lv2SubPluginFeatures.cpp | 184 ++++++++ src/gui/CMakeLists.txt | 1 + src/gui/FileBrowser.cpp | 11 +- src/gui/Lv2ViewBase.cpp | 237 ++++++++++ src/gui/MainWindow.cpp | 2 +- src/lmmsconfig.h.in | 2 + 39 files changed, 3536 insertions(+), 3 deletions(-) create mode 100644 include/Lv2Basics.h create mode 100644 include/Lv2ControlBase.h create mode 100644 include/Lv2Manager.h create mode 100644 include/Lv2Ports.h create mode 100644 include/Lv2Proc.h create mode 100644 include/Lv2SubPluginFeatures.h create mode 100644 include/Lv2ViewBase.h create mode 100644 plugins/Lv2Effect/CMakeLists.txt create mode 100644 plugins/Lv2Effect/Lv2Effect.cpp create mode 100644 plugins/Lv2Effect/Lv2Effect.h create mode 100644 plugins/Lv2Effect/Lv2FxControlDialog.cpp create mode 100644 plugins/Lv2Effect/Lv2FxControlDialog.h create mode 100644 plugins/Lv2Effect/Lv2FxControls.cpp create mode 100644 plugins/Lv2Effect/Lv2FxControls.h create mode 100644 plugins/Lv2Effect/logo.png create mode 100644 plugins/Lv2Instrument/CMakeLists.txt create mode 100644 plugins/Lv2Instrument/Lv2Instrument.cpp create mode 100644 plugins/Lv2Instrument/Lv2Instrument.h create mode 100644 plugins/Lv2Instrument/logo.png create mode 100644 src/core/lv2/Lv2Basics.cpp create mode 100644 src/core/lv2/Lv2ControlBase.cpp create mode 100644 src/core/lv2/Lv2Manager.cpp create mode 100644 src/core/lv2/Lv2Ports.cpp create mode 100644 src/core/lv2/Lv2Proc.cpp create mode 100644 src/core/lv2/Lv2SubPluginFeatures.cpp create mode 100644 src/gui/Lv2ViewBase.cpp diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index fd2b79d948e..d56645603a1 100755 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -13,8 +13,11 @@ SWH_PACKAGES="perl libxml2-utils libxml-perl liblist-moreutils-perl" # VST dependencies VST_PACKAGES="wine-dev qt59x11extras qtbase5-private-dev libxcb-util0-dev libxcb-keysyms1-dev" +# LV2 dependencies; libsuil-dev is not required +LV2_PACKAGES="lv2-dev liblilv-dev" + # Help with unmet dependencies -PACKAGES="$PACKAGES $SWH_PACKAGES $VST_PACKAGES libjack-jackd2-0" +PACKAGES="$PACKAGES $SWH_PACKAGES $VST_PACKAGES $LV2_PACKAGES libjack-jackd2-0" # shellcheck disable=SC2086 sudo apt-get install -y $PACKAGES diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d039e53acd..36e3a699b2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,8 @@ OPTION(WANT_CARLA "Include Carla plugin" ON) OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON) OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON) OPTION(WANT_WEAKJACK "Loosely link JACK libraries" ON) +OPTION(WANT_LV2 "Include Lv2 plugins" ON) +OPTION(WANT_SUIL "Include SUIL for LV2 plugin UIs" ON) OPTION(WANT_MP3LAME "Include MP3/Lame support" ON) OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON) OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON) @@ -182,6 +184,39 @@ IF(NOT SNDFILE_VERSION VERSION_LESS 1.0.26) SET(LMMS_HAVE_SF_COMPLEVEL TRUE) ENDIF() +IF(WANT_LV2) + IF(PKG_CONFIG_FOUND) + PKG_CHECK_MODULES(LV2 lv2) + PKG_CHECK_MODULES(LILV lilv-0) + IF(LV2_FOUND AND LILV_FOUND) + SET(LMMS_HAVE_LV2 TRUE) + SET(STATUS_LV2 "OK") + ELSE() + SET(STATUS_LV2 "not found, install it or set PKG_CONFIG_PATH appropriately") + ENDIF() + ELSE() + SET(STATUS_LV2 "not found, requires pkg-config") + ENDIF() +ELSE(WANT_LV2) + SET(STATUS_LV2 "not built as requested") +ENDIF(WANT_LV2) + +IF(WANT_SUIL) + IF(PKG_CONFIG_FOUND) + PKG_CHECK_MODULES(SUIL suil-0) + IF(SUIL_FOUND) + SET(LMMS_HAVE_SUIL TRUE) + SET(STATUS_SUIL "OK") + ELSE() + SET(STATUS_SUIL "not found, install it or set PKG_CONFIG_PATH appropriately") + ENDIF() + ELSE() + SET(STATUS_SUIL "not found, requires pkg-config") + ENDIF() +ELSE(WANT_SUIL) + SET(STATUS_SUIL "not built as requested") +ENDIF(WANT_SUIL) + IF(WANT_CALF) SET(LMMS_HAVE_CALF TRUE) SET(STATUS_CALF "OK") @@ -686,6 +721,8 @@ MESSAGE( MESSAGE( "Optional plugins\n" "----------------\n" +"* Lv2 plugins : ${STATUS_LV2}\n" +"* SUIL for plugin UIs : ${STATUS_SUIL}\n" "* ZynAddSubFX instrument : ${STATUS_ZYN}\n" "* Carla Patchbay & Rack : ${STATUS_CARLA}\n" "* SoundFont2 player : ${STATUS_FLUIDSYNTH}\n" diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index 2d853038873..a2871bf99ec 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -39,6 +39,8 @@ SET(LMMS_PLUGIN_LIST HydrogenImport ladspa_browser LadspaEffect + Lv2Effect + Lv2Instrument lb302 MidiImport MidiExport diff --git a/include/Engine.h b/include/Engine.h index f4ff72fb2fb..8f988a1250e 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -30,6 +30,7 @@ #include +#include "lmmsconfig.h" #include "lmms_export.h" #include "lmms_basics.h" @@ -87,6 +88,13 @@ class LMMS_EXPORT LmmsCore : public QObject return s_projectJournal; } +#ifdef LMMS_HAVE_LV2 + static class Lv2Manager * getLv2Manager() + { + return s_lv2Manager; + } +#endif + static Ladspa2LMMS * getLADSPAManager() { return s_ladspaManager; @@ -143,6 +151,9 @@ class LMMS_EXPORT LmmsCore : public QObject static ProjectJournal * s_projectJournal; static DummyTrackContainer * s_dummyTC; +#ifdef LMMS_HAVE_LV2 + static class Lv2Manager* s_lv2Manager; +#endif static Ladspa2LMMS * s_ladspaManager; static void* s_dndPluginKey; diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h new file mode 100644 index 00000000000..0003f83e814 --- /dev/null +++ b/include/Lv2Basics.h @@ -0,0 +1,67 @@ +/* + * Lv2Basics.h - basic Lv2 utils + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef LV2BASICS_H +#define LV2BASICS_H + + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include + +struct LilvNodeDeleter +{ + void operator()(LilvNode* n) { lilv_node_free(n); } +}; + +struct LilvNodesDeleter +{ + void operator()(LilvNodes* n) { lilv_nodes_free(n); } +}; + +using AutoLilvNode = std::unique_ptr; +using AutoLilvNodes = std::unique_ptr; + +/** + Return QString from a plugin's node, everything will be freed automatically + @param plug The plugin where the node is + @param getFunc The function to return the node from the plugin +*/ +QString qStringFromPluginNode(const LilvPlugin* plug, + LilvNode * (*getFunc)(const LilvPlugin*)); + +//! Return port name as QString, everything will be freed automatically +QString qStringFromPortName(const LilvPlugin* plug, const LilvPort* port); + +//! Return port name as std::string, everything will be freed automatically +std::string stdStringFromPortName(const LilvPlugin* plug, const LilvPort* port); + +#endif // LMMS_HAVE_LV2 +#endif // LV2BASICS_H diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h new file mode 100644 index 00000000000..9f1b54992cd --- /dev/null +++ b/include/Lv2ControlBase.h @@ -0,0 +1,146 @@ +/* + * Lv2ControlBase.h - Lv2 control base class + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2_CONTROL_BASE_H +#define LV2_CONTROL_BASE_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "DataFile.h" +#include "LinkedModelGroups.h" +#include "Plugin.h" + +class Lv2Proc; +class PluginIssue; + +/** + Common base class for Lv2 plugins + + This class contains a vector of Lv2Proc, usually 1 (for stereo plugins) or + 2 (for mono plugins). Most of the logic is done there, this class primarily + forwards work to the Lv2Proc and collects the results. + + This class provides everything Lv2 plugins have in common. It's not + named Lv2Plugin, because + * it does not inherit Instrument + * the Plugin subclass Effect does not inherit this class + + This class would usually be a Model subclass. However, Qt doesn't allow + this: + * inhertiting only from Model will cause diamond inheritance for QObject, + which will cause errors with Q_OBJECT + * making this a direct subclass of Instrument resp. EffectControls would + require CRTP, which would make this class a template class, which would + conflict with Q_OBJECT + + The consequence is that this class can neither inherit QObject or Model, nor + Instrument or EffectControls, which means in fact: + * this class contains no signals or slots, but it offers stubs for slots + that shall be called by child classes + * this class can not override virtuals of Instrument or EffectControls, so + it will offer functions that must be called by virtuals in its child class +*/ +class Lv2ControlBase : public LinkedModelGroups +{ +public: + static Plugin::PluginTypes check(const LilvPlugin* m_plugin, + std::vector &issues, bool printIssues = false); + + const LilvPlugin* getPlugin() const { return m_plugin; } + + Lv2Proc *control(std::size_t idx) { return m_procs[idx].get(); } + const Lv2Proc *control(std::size_t idx) const { return m_procs[idx].get(); } + + bool hasGui() const { return m_hasGUI; } + void setHasGui(bool val) { m_hasGUI = val; } + +protected: + /* + ctor/dtor + */ + //! @param that the class inheriting this class and inheriting Model; + //! this is the same pointer as this, but a different type + //! @param uri the Lv2 URI telling this class what plugin to construct + Lv2ControlBase(class Model *that, const QString& uri); + ~Lv2ControlBase() override; + //! Must be checked after ctor or reload + bool isValid() const { return m_valid; } + + /* + overrides + */ + LinkedModelGroup* getGroup(std::size_t idx) override; + const LinkedModelGroup* getGroup(std::size_t idx) const override; + + /* + utils for the run thread + */ + //! Copy values from all connected models into the respective ports + void copyModelsFromLmms(); + //! Copy buffer passed by LMMS into our ports + void copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames); + //! Copy our ports into buffers passed by LMMS + void copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const; + //! Run the Lv2 plugin instance for @param frames frames + void run(fpp_t frames); + + /* + load/save, must be called from virtuals + */ + void saveSettings(QDomDocument &doc, QDomElement &that); + void loadSettings(const QDomElement &that); + void loadFile(const QString &file); + //! TODO: not implemented + void reloadPlugin(); + + /* + more functions that must be called from virtuals + */ + std::size_t controlCount() const; + QString nodeName() const { return "lv2controls"; } + +private: + //! Return the DataFile settings type + virtual DataFile::Types settingsType() = 0; + //! Inform the plugin about a file name change + virtual void setNameFromFile(const QString &fname) = 0; + + //! Independent processors + //! If this is a mono effect, the vector will have size 2 in order to + //! fulfill LMMS' requirement of having stereo input and output + std::vector> m_procs; + + bool m_valid = true; + bool m_hasGUI = false; + unsigned m_channelsPerProc; + + const LilvPlugin* m_plugin; +}; + +#endif // LMMS_HAVE_LV2 +#endif // LV2_CONTROL_BASE_H diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h new file mode 100644 index 00000000000..8437715619e --- /dev/null +++ b/include/Lv2Manager.h @@ -0,0 +1,126 @@ +/* + * Lv2Manager.h - Implementation of Lv2Manager class + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2MANAGER_H +#define LV2MANAGER_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include + +#include "Lv2Basics.h" +#include "Plugin.h" + + +/* + all Lv2 classes in relation (use our "4 spaces per tab rule" to view): + + explanation: + "x = {y z}" means class "x" consists of classes "y" and "z" + (and probably other classes not mentioned) + "x = {y*}" means class "x" references/uses class "y" + + core: + Lv2Proc = {LilvInstance} + Lv2ControlBase = {Lv2Proc, Lv2Proc... (2 for mono, 1 for stereo)} + Lv2Manager = {LilvPlugin*, LilvPlugin* ...} + (creates Lv2ControlBase, Lv2ControlBase...) + + Lv2FxControls = {Lv2ControlBase} + Lv2Effect = {Effect + Lv2FxControls} + (takes Lv2SubPluginFeatures in ctor) + Lv2Instrument = {Instrument + Lv2ControlBase} + (takes Lv2SubPluginFeatures in ctor) + + gui: + Lv2ViewProc = {Lv2Proc*} + Lv2ViewBase = {Lv2ViewProc, Lv2ViewProc... + (2 for mono, 1 for stereo)} + Lv2FxControlDialog = {EffectControlDialog + Lv2ViewBase} + Lv2InsView = {InstrumentView + Lv2ViewBase} + + Lv2SubPluginFeatures: + Lv2SubPluginFeatures = {Lv2Manager*} + Lv2Effect::Descriptor = {Lv2SubPluginFeatures} + Lv2Instrument::Descriptor = {Lv2SubPluginFeatures} +*/ + + +//! Class to keep track of all LV2 plugins +class Lv2Manager +{ +public: + void initPlugins(); + + Lv2Manager(); + ~Lv2Manager(); + + + AutoLilvNode uri(const char* uriStr); + + //! Class representing info for one plugin + struct Lv2Info + { + public: + //! use only for std::map internals + Lv2Info() : m_plugin(nullptr) {} + //! ctor used inside Lv2Manager + Lv2Info(const LilvPlugin* plug, Plugin::PluginTypes type, bool valid) : + m_plugin(plug), m_type(type), m_valid(valid) {} + Lv2Info(Lv2Info&& other) = default; + Lv2Info& operator=(Lv2Info&& other) = default; + + const LilvPlugin* plugin() const { return m_plugin; } + Plugin::PluginTypes type() const { return m_type; } + bool isValid() const { return m_valid; } + + private: + const LilvPlugin* m_plugin; + Plugin::PluginTypes m_type; + bool m_valid = false; + }; + + //! Return descriptor with URI @p uri or nullptr if none exists + const LilvPlugin *getPlugin(const std::string &uri); + //! Return descriptor with URI @p uri or nullptr if none exists + const LilvPlugin *getPlugin(const QString& uri); + + using Lv2InfoMap = std::map; + using Iterator = Lv2InfoMap::iterator; + Iterator begin() { return m_lv2InfoMap.begin(); } + Iterator end() { return m_lv2InfoMap.end(); } + +private: + bool m_debug; //!< if set, debug output will be printed + LilvWorld* m_world; + Lv2InfoMap m_lv2InfoMap; + bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); +}; + +#endif // LMMS_HAVE_LV2 + +#endif // LV2MANAGER_H diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h new file mode 100644 index 00000000000..c9e6c16c855 --- /dev/null +++ b/include/Lv2Ports.h @@ -0,0 +1,237 @@ +/* + * Lv2Ports.h - Lv2 port classes definitions + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2PORTS_H +#define LV2PORTS_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include + +#include "lmms_basics.h" +#include "PluginIssue.h" + +struct ConnectPortVisitor; + +namespace Lv2Ports { + +/* + port structs +*/ +enum class Flow { + Unknown, + Input, + Output +}; + +enum class Type { + Unknown, + Control, + Audio, + Event, //!< TODO: unused, describe + Cv //!< TODO: unused, describe +}; + +//! Port visualization +//! @note All Lv2 audio ports are float, this is only the visualisation +enum class Vis { + None, + Integer, + Enumeration, + Toggled +}; + +const char* toStr(Lv2Ports::Flow pf); +const char* toStr(Lv2Ports::Type pt); +const char* toStr(Lv2Ports::Vis pv); + +struct ControlPortBase; +struct Control; +struct Audio; +struct Cv; +struct Unknown; + +struct ConstVisitor +{ + virtual void visit(const Lv2Ports::ControlPortBase& ) {} + virtual void visit(const Lv2Ports::Control& ) {} + virtual void visit(const Lv2Ports::Audio& ) {} + virtual void visit(const Lv2Ports::Cv& ) {} + virtual void visit(const Lv2Ports::Unknown& ) {} + + virtual ~ConstVisitor(); +}; + +struct Visitor +{ + virtual void visit(Lv2Ports::ControlPortBase& ) {} + virtual void visit(Lv2Ports::Control& ) {} + virtual void visit(Lv2Ports::Audio& ) {} + virtual void visit(Lv2Ports::Cv& ) {} + virtual void visit(Lv2Ports::Unknown& ) {} + + virtual ~Visitor(); +}; + +struct Meta +{ + Type m_type = Type::Unknown; + Flow m_flow = Flow::Unknown; + Vis m_vis = Vis::None; + + float m_def = .0f, m_min = .0f, m_max = .0f; + bool m_optional = false; + bool m_used = true; + + std::vector get(const LilvPlugin* plugin, std::size_t portNum); +}; + +struct PortBase : public Meta +{ + const LilvPort* m_port = nullptr; + const LilvPlugin* m_plugin = nullptr; + + virtual void accept(Visitor& v) = 0; + virtual void accept(ConstVisitor& v) const = 0; + + QString name() const; + QString uri() const; + + virtual ~PortBase(); +}; + +template +struct VisitablePort : public Base +{ + void accept(Visitor& v) override { v.visit(static_cast(*this)); } + void accept(ConstVisitor& v) const override { v.visit(static_cast(*this)); } +}; + +struct ControlPortBase : public VisitablePort +{ + //! LMMS models + //! Always up-to-date, except during runs + std::unique_ptr m_connectedModel; + + //! Enumerate float values + //! lv2 defines scale points as + //! "single float Points (for control inputs)" + std::vector m_scalePointMap; +}; + +struct Control : public VisitablePort +{ + //! Data location which Lv2 plugins see + //! Model values are being copied here every run + //! Between runs, this data is not up-to-date + float m_val; +}; + +struct Cv : public VisitablePort +{ + //! Data location which Lv2 plugins see + //! Model values are being copied here every run + //! Between runs, this data is not up-to-date + std::vector m_buffer; +}; + +struct Audio : public VisitablePort +{ + Audio(std::size_t bufferSize, bool isSidechain, bool isOptional); + + //! Copy buffer passed by LMMS into our ports + //! @param channel channel index into each sample frame + void copyBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames); + //! Add buffer passed by LMMS into our ports, and halve the result + //! @param channel channel index into each sample frame + void averageWithBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames); + //! Copy our ports into buffers passed by LMMS + //! @param channel channel index into each sample frame + void copyBuffersToCore(sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) const; + + bool isSideChain() const { return m_sidechain; } + bool isOptional() const { return m_optional; } + bool mustBeUsed() const { return !isSideChain() && !isOptional(); } + std::size_t bufferSize() const { return m_buffer.size(); } + +private: + //! the buffer where Lv2 reads/writes the data from/to + std::vector m_buffer; + bool m_sidechain; + bool m_optional; + + // the only case when data of m_buffer may be referenced: + friend struct ::ConnectPortVisitor; +}; + +struct Unknown : public VisitablePort +{ +}; + +/* + port casts +*/ +template +struct DCastVisitor : public Visitor +{ + Target* m_result = nullptr; + void visit(Target& tar) { m_result = &tar; } +}; + +template +struct ConstDCastVisitor : public ConstVisitor +{ + const Target* m_result = nullptr; + void visit(const Target& tar) { m_result = &tar; } +}; + +//! If you don't want to use a whole visitor, you can use dcast +template +Target* dcast(PortBase* base) +{ + DCastVisitor vis; + base->accept(vis); + return vis.m_result; +} + +//! const overload +template +const Target* dcast(const PortBase* base) +{ + ConstDCastVisitor vis; + base->accept(vis); + return vis.m_result; +} + +} // namespace Lv2Ports + +#endif // LMMS_HAVE_LV2 +#endif // LV2PORTS_H diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h new file mode 100644 index 00000000000..fb1f3466634 --- /dev/null +++ b/include/Lv2Proc.h @@ -0,0 +1,183 @@ +/* + * Lv2Proc.h - Lv2 processor class + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2PROC_H +#define LV2PROC_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include + +#include "Lv2Basics.h" +#include "LinkedModelGroups.h" +#include "Plugin.h" +#include "PluginIssue.h" + + +// forward declare port structs/enums +namespace Lv2Ports +{ + struct Audio; + struct PortBase; + + enum class Type; + enum class Flow; + enum class Vis; +} + + +//! Class representing one Lv2 processor, i.e. one Lv2 handle +//! For Mono effects, 1 Lv2ControlBase references 2 Lv2Proc +class Lv2Proc : public LinkedModelGroup +{ +public: + static Plugin::PluginTypes check(const LilvPlugin* plugin, + std::vector &issues, bool printIssues = false); + + /* + ctor/dtor + */ + Lv2Proc(const LilvPlugin* plugin, Model *parent); + ~Lv2Proc() override; + //! Must be checked after ctor or reload + bool isValid() const { return m_valid; } + + /* + port access + */ + struct StereoPortRef + { + //! mono port or left port in case of stereo + Lv2Ports::Audio* m_left = nullptr; + //! unused, or right port in case of stereo + Lv2Ports::Audio* m_right = nullptr; + }; + + StereoPortRef& inPorts() { return m_inPorts; } + const StereoPortRef& inPorts() const { return m_inPorts; } + StereoPortRef& outPorts() { return m_outPorts; } + const StereoPortRef& outPorts() const { return m_outPorts; } + template + void foreach_port(const Functor& ftor) + { + for (std::unique_ptr& port : m_ports) + { + ftor(port.get()); + } + } + template + void foreach_port(const Functor& ftor) const + { + for (const std::unique_ptr& port : m_ports) + { + ftor(port.get()); + } + } + + //! Debug function to print ports to stdout + void dumpPorts(); + + /* + utils for the run thread + */ + //! Copy values from all connected models into the respective ports + void copyModelsFromCore(); + /** + * Copy buffer passed by the core into our ports + * @param buf buffer of sample frames, each sample frame is something like + * a `float[ * ]` array. + * @param firstChan The offset for @p buf where we have to read our + * first channel. + * This marks the first sample in each sample frame where we read from. + * If we are the 2nd of 2 mono procs, this can be greater than 0. + * @param num Number of channels we must read from @param buf (starting at + * @p offset) + */ + void copyBuffersFromCore(const sampleFrame *buf, + unsigned firstChan, unsigned num, fpp_t frames); + /** + * Copy our ports into buffers passed by the core + * @param buf buffer of sample frames, each sample frame is something like + * a `float[ * ]` array. + * @param firstChan The offset for @p buf where we have to write our + * first channel. + * This marks the first sample in each sample frame where we write to. + * If we are the 2nd of 2 mono procs, this can be greater than 0. + * @param num Number of channels we must write to @param buf (starting at + * @p offset) + */ + void copyBuffersToCore(sampleFrame *buf, unsigned firstChan, unsigned num, + fpp_t frames) const; + //! Run the Lv2 plugin instance for @param frames frames + void run(fpp_t frames); + + /* + misc + */ + class AutomatableModel *modelAtPort(const QString &uri); // unused currently + std::size_t controlCount() const { return LinkedModelGroup::modelNum(); } + +protected: + /* + load and save + */ + //! Create ports and instance, connect ports, activate plugin + void initPlugin(); + //! Deactivate instance + void shutdownPlugin(); + +private: + bool m_valid = true; + + const LilvPlugin* m_plugin; + LilvInstance* m_instance; + + std::vector> m_ports; + StereoPortRef m_inPorts, m_outPorts; + + //! models for the controls, sorted by port symbols + std::map m_connectedModels; + + //! load a file in the plugin, but don't do anything in LMMS + void loadFileInternal(const QString &file); + //! allocate m_ports, fill all with metadata, and assign meaning of ports + void createPorts(); + //! fill m_ports[portNum] with metadata + void createPort(std::size_t portNum); + //! connect m_ports[portNum] with Lv2 + void connectPort(std::size_t num); + + void dumpPort(std::size_t num); + + static bool portIsSideChain(const LilvPlugin* plugin, const LilvPort *port); + static bool portIsOptional(const LilvPlugin* plugin, const LilvPort *port); + static AutoLilvNode uri(const char* uriStr); +}; + +#endif // LMMS_HAVE_LV2 +#endif // LV2PROC_H diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h new file mode 100644 index 00000000000..33c29c3ef26 --- /dev/null +++ b/include/Lv2SubPluginFeatures.h @@ -0,0 +1,61 @@ +/* + * Lv2SubPluginFeatures.h - derivation from + * Plugin::Descriptor::SubPluginFeatures for + * hosting LV2 plugins + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2_SUBPLUGIN_FEATURES_H +#define LV2_SUBPLUGIN_FEATURES_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "Plugin.h" + +class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures +{ +private: + static const LilvPlugin *getPlugin(const Key &k); + static QString pluginName(const LilvPlugin *plug); + +public: + Lv2SubPluginFeatures(Plugin::PluginTypes type); + + void fillDescriptionWidget( + QWidget *parent, const Key *k) const override; + + QString additionalFileExtensions(const Key &k) const override; + QString displayName(const Key &k) const override; + QString description(const Key &k) const override; + const PixmapLoader *logo(const Key &k) const override; + + void listSubPluginKeys( + const Plugin::Descriptor *desc, KeyList &kl) const override; +}; + +#endif // LMMS_HAVE_LV2 + +#endif diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h new file mode 100644 index 00000000000..980e1f7efcf --- /dev/null +++ b/include/Lv2ViewBase.h @@ -0,0 +1,97 @@ +/* + * Lv2ViewBase.h - base class for Lv2 plugin views + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2VIEWBASE_H +#define LV2VIEWBASE_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + + +#include +#include + +#include "LinkedModelGroupViews.h" +#include "Lv2Basics.h" + +class Lv2Proc; +class Lv2ControlBase; + + +//! View for one processor, Lv2ViewBase contains 2 of those for mono plugins +class Lv2ViewProc : public LinkedModelGroupView +{ +public: + //! @param colNum numbers of columns for the controls + Lv2ViewProc(QWidget *parent, Lv2Proc *ctrlBase, int colNum); + ~Lv2ViewProc(); + +private: + static AutoLilvNode uri(const char *uriStr); +}; + + +//! Base class for view for one Lv2 plugin +class Lv2ViewBase : public LinkedModelGroupsView +{ +protected: + //! @param pluginWidget A child class which inherits QWidget + Lv2ViewBase(class QWidget *pluginWidget, Lv2ControlBase *ctrlBase); + ~Lv2ViewBase(); + + // these widgets must be connected by child widgets + class QPushButton *m_reloadPluginButton = nullptr; + class QPushButton *m_toggleUIButton = nullptr; + class QPushButton *m_helpButton = nullptr; + + void toggleUI(); + void toggleHelp(bool visible); + + // to be called by child virtuals + //! Reconnect models if model changed + void modelChanged(Lv2ControlBase* ctrlBase); + +private: + enum Rows + { + ButtonRow, + ProcRow, + LinkChannelsRow + }; + + static AutoLilvNode uri(const char *uriStr); + LinkedModelGroupView* getGroupView() override { return m_procView; } + + Lv2ViewProc* m_procView; + + //! Numbers of controls per row; must be multiple of 2 for mono effects + const int m_colNum = 6; + class QMdiSubWindow* m_helpWindow = nullptr; + class LedCheckBox *m_multiChannelLink; +}; + + +#endif // LMMS_HAVE_LV2 +#endif // LV2VIEWBASE_H diff --git a/plugins/LadspaEffect/LadspaControls.h b/plugins/LadspaEffect/LadspaControls.h index ccb96dd7484..013b914d45d 100644 --- a/plugins/LadspaEffect/LadspaControls.h +++ b/plugins/LadspaEffect/LadspaControls.h @@ -71,6 +71,7 @@ protected slots: ch_cnt_t m_controlCount; bool m_noLink; BoolModel m_stereoLinkModel; + //! control vector for each processor QVector m_controls; diff --git a/plugins/Lv2Effect/CMakeLists.txt b/plugins/Lv2Effect/CMakeLists.txt new file mode 100644 index 00000000000..915751797d1 --- /dev/null +++ b/plugins/Lv2Effect/CMakeLists.txt @@ -0,0 +1,9 @@ +IF(LMMS_HAVE_LV2) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + INCLUDE(BuildPlugin) + BUILD_PLUGIN(lv2effect Lv2Effect.cpp Lv2FxControls.cpp Lv2FxControlDialog.cpp Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h + MOCFILES Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h + EMBEDDED_RESOURCES logo.png) +ENDIF(LMMS_HAVE_LV2) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp new file mode 100644 index 00000000000..2ee5ceabbe5 --- /dev/null +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -0,0 +1,111 @@ +/* + * Lv2Effect.cpp - implementation of LV2 effect + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Effect.h" + +#include +#include + +#include "Lv2SubPluginFeatures.h" + +#include "embed.h" +#include "plugin_export.h" + + + + +Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = +{ + STRINGIFY(PLUGIN_NAME), + "LV2", + QT_TRANSLATE_NOOP("pluginBrowser", + "plugin for using arbitrary LV2-effects inside LMMS."), + "Johannes Lorenz ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader("logo"), + nullptr, + new Lv2SubPluginFeatures(Plugin::Effect) +}; + + + + +Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *key) : + Effect(&lv2effect_plugin_descriptor, parent, key), + m_controls(this, key->attributes["uri"]), + m_tmpOutputSmps(Engine::mixer()->framesPerPeriod()) +{ +} + + + + +bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +{ + if (!isEnabled() || !isRunning()) { return false; } + Q_ASSERT(frames <= static_cast(m_tmpOutputSmps.size())); + + m_controls.copyBuffersFromLmms(buf, frames); + m_controls.copyModelsFromLmms(); + +// m_pluginMutex.lock(); + m_controls.run(frames); +// m_pluginMutex.unlock(); + + m_controls.copyBuffersToLmms(m_tmpOutputSmps.data(), frames); + + double outSum = .0; + bool corrupt = wetLevel() < 0; // #3261 - if w < 0, bash w := 0, d := 1 + const float d = corrupt ? 1 : dryLevel(); + const float w = corrupt ? 0 : wetLevel(); + for(fpp_t f = 0; f < frames; ++f) + { + buf[f][0] = d * buf[f][0] + w * m_tmpOutputSmps[f][0]; + buf[f][1] = d * buf[f][1] + w * m_tmpOutputSmps[f][1]; + double l = static_cast(buf[f][0]); + double r = static_cast(buf[f][1]); + outSum += l*l + r*r; + } + checkGate(outSum / frames); + + return isRunning(); +} + + + + +extern "C" +{ + +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) +{ + using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; + Lv2Effect* eff = new Lv2Effect(_parent, static_cast(_data)); + if (!eff->isValid()) { delete eff; eff = nullptr; } + return eff; +} + +} diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h new file mode 100644 index 00000000000..77792d428a1 --- /dev/null +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -0,0 +1,54 @@ +/* + * Lv2Effect.h - implementation of LV2 effect + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2_EFFECT_H +#define LV2_EFFECT_H + +#include "Effect.h" +#include "Lv2FxControls.h" + +class Lv2Effect : public Effect +{ + Q_OBJECT + +public: + /* + initialization + */ + Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key* _key); + //! Must be checked after ctor or reload + bool isValid() const { return m_controls.isValid(); } + + bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + EffectControls* controls() override { return &m_controls; } + + Lv2FxControls* lv2Controls() { return &m_controls; } + const Lv2FxControls* lv2Controls() const { return &m_controls; } + +private: + Lv2FxControls m_controls; + std::vector m_tmpOutputSmps; +}; + +#endif // LMMS_HAVE_LV2 diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp new file mode 100644 index 00000000000..9a77171d6df --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -0,0 +1,72 @@ +/* + * Lv2FxControlDialog.cpp - Lv2FxControlDialog implementation + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2FxControlDialog.h" + +#include +#include +#include + +#include "Lv2Effect.h" +#include "Lv2FxControls.h" + + +Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : + EffectControlDialog(controls), + Lv2ViewBase(this, controls) +{ + if (m_reloadPluginButton) { + connect(m_reloadPluginButton, &QPushButton::clicked, + this, [this](){ lv2Controls()->reloadPlugin(); }); + } + if (m_toggleUIButton) { + connect(m_toggleUIButton, &QPushButton::toggled, + this, [this](){ toggleUI(); }); + } + if (m_helpButton) { + connect(m_helpButton, &QPushButton::toggled, + this, [this](bool visible){ toggleHelp(visible); }); + } + // for Effects, modelChanged only goes to the top EffectView + // we need to call it manually + modelChanged(); +} + + + + +Lv2FxControls *Lv2FxControlDialog::lv2Controls() +{ + return static_cast(m_effectControls); +} + + + + +void Lv2FxControlDialog::modelChanged() +{ + Lv2ViewBase::modelChanged(lv2Controls()); +} + + diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.h b/plugins/Lv2Effect/Lv2FxControlDialog.h new file mode 100644 index 00000000000..3abdb300d4d --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControlDialog.h @@ -0,0 +1,47 @@ +/* + * Lv2FxControlDialog.h - Lv2FxControlDialog implementation + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2_FX_CONTROL_DIALOG_H +#define LV2_FX_CONTROL_DIALOG_H + +#include "EffectControlDialog.h" +#include "Lv2ViewBase.h" + +class Lv2FxControls; + + +class Lv2FxControlDialog : public EffectControlDialog, public Lv2ViewBase +{ + Q_OBJECT + +public: + Lv2FxControlDialog(Lv2FxControls *controls); + +private: + Lv2FxControls *lv2Controls(); + void modelChanged() override; +}; + + +#endif diff --git a/plugins/Lv2Effect/Lv2FxControls.cpp b/plugins/Lv2Effect/Lv2FxControls.cpp new file mode 100644 index 00000000000..a5733fec670 --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControls.cpp @@ -0,0 +1,104 @@ +/* + * Lv2FxControls.cpp - Lv2FxControls implementation + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2FxControls.h" + +#include + +#include "Engine.h" +#include "Lv2Effect.h" +#include "Lv2FxControlDialog.h" +#include "Lv2Proc.h" + + + + +Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) : + EffectControls(effect), + Lv2ControlBase(this, uri) +{ + if (isValid()) + { + connect(Engine::mixer(), &Mixer::sampleRateChanged, + this, [this](){Lv2ControlBase::reloadPlugin();}); + } +} + + + + +void Lv2FxControls::saveSettings(QDomDocument &doc, QDomElement &that) +{ + Lv2ControlBase::saveSettings(doc, that); +} + + + + +void Lv2FxControls::loadSettings(const QDomElement &that) +{ + Lv2ControlBase::loadSettings(that); +} + + + + +int Lv2FxControls::controlCount() +{ + return static_cast(Lv2ControlBase::controlCount()); +} + + + + +EffectControlDialog *Lv2FxControls::createView() +{ + return new Lv2FxControlDialog(this); +} + + + + +void Lv2FxControls::changeControl() // TODO: what is that? +{ + // engine::getSong()->setModified(); +} + + + + +DataFile::Types Lv2FxControls::settingsType() +{ + return DataFile::EffectSettings; +} + + + + +void Lv2FxControls::setNameFromFile(const QString &name) +{ + effect()->setDisplayName(name); +} + + diff --git a/plugins/Lv2Effect/Lv2FxControls.h b/plugins/Lv2Effect/Lv2FxControls.h new file mode 100644 index 00000000000..e5449a4f7fe --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControls.h @@ -0,0 +1,61 @@ +/* + * Lv2FxControls.h - Lv2FxControls implementation + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2_FX_CONTROLS_H +#define LV2_FX_CONTROLS_H + +#include "EffectControls.h" +#include "Lv2ControlBase.h" + +class Lv2Effect; + + +class Lv2FxControls : public EffectControls, public Lv2ControlBase +{ + Q_OBJECT +public: + Lv2FxControls(Lv2Effect *effect, const QString &uri); + + void saveSettings(QDomDocument &_doc, QDomElement &_parent) override; + void loadSettings(const QDomElement &that) override; + inline QString nodeName() const override + { + return Lv2ControlBase::nodeName(); + } + + int controlCount() override; + EffectControlDialog *createView() override; + +private slots: + void changeControl(); + +private: + DataFile::Types settingsType() override; + void setNameFromFile(const QString &name) override; + + friend class Lv2FxControlDialog; + friend class Lv2Effect; +}; + +#endif diff --git a/plugins/Lv2Effect/logo.png b/plugins/Lv2Effect/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c423ccea45839a6296fa79d972484b2fad7c0f51 GIT binary patch literal 967 zcmV;&133JNP)aE)q(*?>IU8e zs(@dBpMl9`4&Wi631|fV0QQ*KXGH$|MoANrew5TwYE+Tb>b^}#YK&F*T+$6mb*0L8 zN$Vutlyo6ftd=w->2Rq}T1iLT*VO>!E^qe9`d!4J-gV~h-^}c0Lcbn(G+~lAvynMC zQV&!lM1Pvu6-kxJ_9OV#{b&Y;rW@?N>Jy`KtCy|CdK($N%sP~K1nrh zKP>58NsmWt|L5ysERh32xg6M$VzUFtMcR4M?Hho-z$Hmf&eqU<2ViESz*m8IXNuWN zL3zk&HwqjCzI6Fxz_BF_pui|>lGIdyK21PdAnx~{aoWrdn^_0&q3b$}5x~blevYFM z%JXg?;s<}{mOKD6y8#Ra;$BH*!QU?fdV_jr%xpZUR|V|x;@x75K#WG~fM)_(r^{#1 zC*k}Pzp8iZuc;8a)E^`1kX{j-8~>0pKnFp74xwubKUo*5RTCU}i&} zyT@n~(r3iXzLvBNcstM?k~HSem12$n!23bDOHwPay+FO!1NjPIZSWLC`2rkllhji{ z_mex;mpCltfR7@54S3FU8~AjV{-)-VqRuCon+<2(?u(Rq7w;pTUi4%xiS3eVBa=C8 zvM(5m9YD@A%~QUb#PD~4)L}s=Pr0QKaBH4zcAMEzNuL1S5&dAwfn{DdDboR5_jg^e zEanp%%Y2vAT}yPToK8iqB`I{-hqmJZxXaq=^t>F-?tVWkx0mpNevA8fGN!EZo$rpM zS0z=KkmJ>o_PK9ko^D5P=IVhHz|$`JJ#tVfHY$Kx|Go&k?8n&Xs)M8)@GP(yc#6`v p8yyC|0nVA(6d5vP$dDm}<6kPu9i$iGsP6y(002ovPDHLkV1fcwztR8z literal 0 HcmV?d00001 diff --git a/plugins/Lv2Instrument/CMakeLists.txt b/plugins/Lv2Instrument/CMakeLists.txt new file mode 100644 index 00000000000..290bd84e82e --- /dev/null +++ b/plugins/Lv2Instrument/CMakeLists.txt @@ -0,0 +1,7 @@ +IF(LMMS_HAVE_LV2) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + INCLUDE(BuildPlugin) + BUILD_PLUGIN(lv2instrument Lv2Instrument.cpp Lv2Instrument.h MOCFILES Lv2Instrument.h EMBEDDED_RESOURCES logo.png) +ENDIF(LMMS_HAVE_LV2) diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp new file mode 100644 index 00000000000..974aaf416b4 --- /dev/null +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -0,0 +1,303 @@ +/* + * Lv2Instrument.cpp - implementation of LV2 instrument + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Instrument.h" + +#include +#include + +#include "Engine.h" +#include "InstrumentPlayHandle.h" +#include "InstrumentTrack.h" +#include "Lv2SubPluginFeatures.h" +#include "Mixer.h" +#include "StringPairDrag.h" + +#include "embed.h" +#include "plugin_export.h" + + + + +Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = +{ + STRINGIFY(PLUGIN_NAME), + "LV2", + QT_TRANSLATE_NOOP("pluginBrowser", + "plugin for using arbitrary LV2 instruments inside LMMS."), + "Johannes Lorenz ", + 0x0100, + Plugin::Instrument, + new PluginPixmapLoader("logo"), + nullptr, + new Lv2SubPluginFeatures(Plugin::Instrument) +}; + + + + +/* + Lv2Instrument +*/ + + +Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, + Descriptor::SubPluginFeatures::Key *key) : + Instrument(instrumentTrackArg, &lv2instrument_plugin_descriptor, key), + Lv2ControlBase(this, key->attributes["uri"]) +{ + if (Lv2ControlBase::isValid()) + { +#ifdef LV2_INSTRUMENT_USE_MIDI + for (int i = 0; i < NumKeys; ++i) { m_runningNotes[i] = 0; } +#endif + connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()), + this, SLOT(updatePitchRange()), Qt::DirectConnection); + connect(Engine::mixer(), &Mixer::sampleRateChanged, + this, [this](){Lv2ControlBase::reloadPlugin();}); + + // now we need a play-handle which cares for calling play() + InstrumentPlayHandle *iph = + new InstrumentPlayHandle(this, instrumentTrackArg); + Engine::mixer()->addPlayHandle(iph); + } +} + + + + +Lv2Instrument::~Lv2Instrument() +{ + Engine::mixer()->removePlayHandlesOfTypes(instrumentTrack(), + PlayHandle::TypeNotePlayHandle | + PlayHandle::TypeInstrumentPlayHandle); +} + + + + +bool Lv2Instrument::isValid() const { return Lv2ControlBase::isValid(); } + + + + +void Lv2Instrument::saveSettings(QDomDocument &doc, QDomElement &that) +{ + Lv2ControlBase::saveSettings(doc, that); +} + + + + +void Lv2Instrument::loadSettings(const QDomElement &that) +{ + Lv2ControlBase::loadSettings(that); +} + + + + +void Lv2Instrument::loadFile(const QString &file) +{ + Lv2ControlBase::loadFile(file); +} + + + + +#ifdef LV2_INSTRUMENT_USE_MIDI +bool Lv2Instrument::handleMidiEvent( + const MidiEvent &event, const MidiTime &time, f_cnt_t offset) +{ + // this function can be called from GUI threads while the plugin is running, + // so this requires caching, e.g. in ringbuffers + (void)time; + (void)offset; + (void)event; + return true; +} +#endif + + + + +// not yet working +#ifndef LV2_INSTRUMENT_USE_MIDI +void Lv2Instrument::playNote(NotePlayHandle *nph, sampleFrame *) +{ +} +#endif + + + + +void Lv2Instrument::play(sampleFrame *buf) +{ + copyModelsFromLmms(); + + fpp_t fpp = Engine::mixer()->framesPerPeriod(); + + run(fpp); + + copyBuffersToLmms(buf, fpp); + + instrumentTrack()->processAudioBuffer(buf, fpp, nullptr); +} + + + + +PluginView *Lv2Instrument::instantiateView(QWidget *parent) +{ + return new Lv2InsView(this, parent); +} + + + + +void Lv2Instrument::updatePitchRange() +{ + qDebug() << "Lmms: Cannot update pitch range for lv2 plugin:" + "not implemented yet"; +} + + + + +QString Lv2Instrument::nodeName() const +{ + return Lv2ControlBase::nodeName(); +} + + + + +DataFile::Types Lv2Instrument::settingsType() +{ + return DataFile::InstrumentTrackSettings; +} + + + + +void Lv2Instrument::setNameFromFile(const QString &name) +{ + instrumentTrack()->setName(name); +} + + + + +/* + Lv2InsView +*/ + + +Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : + InstrumentView(_instrument, _parent), + Lv2ViewBase(this, _instrument) +{ + setAutoFillBackground(true); + if (m_reloadPluginButton) { + connect(m_reloadPluginButton, &QPushButton::clicked, + this, [this](){ castModel()->reloadPlugin();} ); + } + if (m_toggleUIButton) { + connect(m_toggleUIButton, &QPushButton::toggled, + this, [this](){ toggleUI(); }); + } + if (m_helpButton) { + connect(m_helpButton, &QPushButton::toggled, + this, [this](bool visible){ toggleHelp(visible); }); + } +} + + + + +void Lv2InsView::dragEnterEvent(QDragEnterEvent *_dee) +{ + void (QDragEnterEvent::*reaction)(void) = &QDragEnterEvent::ignore; + + if (_dee->mimeData()->hasFormat(StringPairDrag::mimeType())) + { + const QString txt = + _dee->mimeData()->data(StringPairDrag::mimeType()); + if (txt.section(':', 0, 0) == "pluginpresetfile") { + reaction = &QDragEnterEvent::acceptProposedAction; + } + } + + (_dee->*reaction)(); +} + + + + +void Lv2InsView::dropEvent(QDropEvent *_de) +{ + const QString type = StringPairDrag::decodeKey(_de); + const QString value = StringPairDrag::decodeValue(_de); + if (type == "pluginpresetfile") + { + castModel()->loadFile(value); + _de->accept(); + return; + } + _de->ignore(); +} + + + + +void Lv2InsView::toggleUI() +{ +} + + + + +void Lv2InsView::modelChanged() +{ + Lv2ViewBase::modelChanged(castModel()); +} + + + + +extern "C" +{ + +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) +{ + using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; + Lv2Instrument* ins = new Lv2Instrument( + static_cast(_parent), + static_cast(_data )); + if (!ins->isValid()) { delete ins; ins = nullptr; } + return ins; +} + +} diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h new file mode 100644 index 00000000000..6451d49cdcf --- /dev/null +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -0,0 +1,123 @@ +/* + * Lv2Instrument.h - implementation of LV2 instrument + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2_INSTRUMENT_H +#define LV2_INSTRUMENT_H + +#include + +#include "Instrument.h" +#include "InstrumentView.h" +#include "Note.h" +#include "Lv2ControlBase.h" +#include "Lv2ViewBase.h" + +// whether to use MIDI vs playHandle +// currently only MIDI works +#define LV2_INSTRUMENT_USE_MIDI + +class QPushButton; + + +class Lv2Instrument : public Instrument, public Lv2ControlBase +{ + Q_OBJECT +public: + /* + initialization + */ + Lv2Instrument(InstrumentTrack *instrumentTrackArg, + Descriptor::SubPluginFeatures::Key* key); + ~Lv2Instrument() override; + //! Must be checked after ctor or reload + bool isValid() const; + + /* + load/save + */ + void saveSettings(QDomDocument &doc, QDomElement &that) override; + void loadSettings(const QDomElement &that) override; + void loadFile(const QString &file) override; + + /* + realtime funcs + */ + bool hasNoteInput() const override { return false; /* not supported yet */ } +#ifdef LV2_INSTRUMENT_USE_MIDI + bool handleMidiEvent(const MidiEvent &event, + const MidiTime &time = MidiTime(), f_cnt_t offset = 0) override; +#else + void playNote(NotePlayHandle *nph, sampleFrame *) override; +#endif + void play(sampleFrame *buf) override; + + /* + misc + */ + Flags flags() const override + { +#ifdef LV2_INSTRUMENT_USE_MIDI + return IsSingleStreamed | IsMidiBased; +#else + return IsSingleStreamed; +#endif + } + PluginView *instantiateView(QWidget *parent) override; + +private slots: + void updatePitchRange(); + +private: + QString nodeName() const override; + DataFile::Types settingsType() override; + void setNameFromFile(const QString &name) override; + +#ifdef LV2_INSTRUMENT_USE_MIDI + int m_runningNotes[NumKeys]; +#endif + + friend class Lv2InsView; +}; + + +class Lv2InsView : public InstrumentView, public Lv2ViewBase +{ + Q_OBJECT +public: + Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent); + +protected: + void dragEnterEvent(QDragEnterEvent *_dee) override; + void dropEvent(QDropEvent *_de) override; + +private slots: + void reloadPlugin(); + void toggleUI(); + +private: + void modelChanged() override; +}; + + +#endif // LV2_INSTRUMENT_H diff --git a/plugins/Lv2Instrument/logo.png b/plugins/Lv2Instrument/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c423ccea45839a6296fa79d972484b2fad7c0f51 GIT binary patch literal 967 zcmV;&133JNP)aE)q(*?>IU8e zs(@dBpMl9`4&Wi631|fV0QQ*KXGH$|MoANrew5TwYE+Tb>b^}#YK&F*T+$6mb*0L8 zN$Vutlyo6ftd=w->2Rq}T1iLT*VO>!E^qe9`d!4J-gV~h-^}c0Lcbn(G+~lAvynMC zQV&!lM1Pvu6-kxJ_9OV#{b&Y;rW@?N>Jy`KtCy|CdK($N%sP~K1nrh zKP>58NsmWt|L5ysERh32xg6M$VzUFtMcR4M?Hho-z$Hmf&eqU<2ViESz*m8IXNuWN zL3zk&HwqjCzI6Fxz_BF_pui|>lGIdyK21PdAnx~{aoWrdn^_0&q3b$}5x~blevYFM z%JXg?;s<}{mOKD6y8#Ra;$BH*!QU?fdV_jr%xpZUR|V|x;@x75K#WG~fM)_(r^{#1 zC*k}Pzp8iZuc;8a)E^`1kX{j-8~>0pKnFp74xwubKUo*5RTCU}i&} zyT@n~(r3iXzLvBNcstM?k~HSem12$n!23bDOHwPay+FO!1NjPIZSWLC`2rkllhji{ z_mex;mpCltfR7@54S3FU8~AjV{-)-VqRuCon+<2(?u(Rq7w;pTUi4%xiS3eVBa=C8 zvM(5m9YD@A%~QUb#PD~4)L}s=Pr0QKaBH4zcAMEzNuL1S5&dAwfn{DdDboR5_jg^e zEanp%%Y2vAT}yPToK8iqB`I{-hqmJZxXaq=^t>F-?tVWkx0mpNevA8fGN!EZo$rpM zS0z=KkmJ>o_PK9ko^D5P=IVhHz|$`JJ#tVfHY$Kx|Go&k?8n&Xs)M8)@GP(yc#6`v p8yyC|0nVA(6d5vP$dDm}<6kPu9i$iGsP6y(002ovPDHLkV1fcwztR8z literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59710926d86..3ebec349e60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,6 +90,17 @@ IF(NOT ("${LAME_INCLUDE_DIRS}" STREQUAL "")) INCLUDE_DIRECTORIES("${LAME_INCLUDE_DIRS}") ENDIF() +IF(NOT ("${LV2_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) +ENDIF() + +IF(NOT ("${LILV_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) +ENDIF() + +IF(NOT ("${SUIL_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) +ENDIF() LIST(APPEND LMMS_SRCS "${RINGBUFFER_DIR}/src/lib/ringbuffer.cpp") # Use libraries in non-standard directories (e.g., another version of Qt) @@ -167,6 +178,9 @@ SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} ${JACK_LIBRARIES} ${OGGVORBIS_LIBRARIES} ${LAME_LIBRARIES} + ${LV2_LIBRARIES} + ${SUIL_LIBRARIES} + ${LILV_LIBRARIES} ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${EXTRA_LIBRARIES} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f1c183e3f55..42d0f6784ae 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -90,6 +90,13 @@ set(LMMS_SRCS core/audio/AudioSampleRecorder.cpp core/audio/AudioSdl.cpp + core/lv2/Lv2Basics.cpp + core/lv2/Lv2ControlBase.cpp + core/lv2/Lv2Ports.cpp + core/lv2/Lv2Proc.cpp + core/lv2/Lv2Manager.cpp + core/lv2/Lv2SubPluginFeatures.cpp + core/midi/MidiAlsaRaw.cpp core/midi/MidiAlsaSeq.cpp core/midi/MidiClient.cpp diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 129c9738b3e..44cb920d844 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -166,6 +166,9 @@ bool DataFile::validate( QString extension ) ( extension == "xiz" && ! pluginFactory->pluginSupportingExtension(extension).isNull()) || extension == "sf2" || extension == "sf3" || extension == "pat" || extension == "mid" || extension == "dll" +#ifdef LMMS_HAVE_LV2 + || extension == "lv2" +#endif ) ) { return true; diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index ce82310fa4c..81cd0acdb17 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -28,6 +28,7 @@ #include "ConfigManager.h" #include "FxMixer.h" #include "Ladspa2LMMS.h" +#include "Lv2Manager.h" #include "Mixer.h" #include "Plugin.h" #include "PresetPreviewPlayHandle.h" @@ -41,6 +42,9 @@ FxMixer * LmmsCore::s_fxMixer = NULL; BBTrackContainer * LmmsCore::s_bbTrackContainer = NULL; Song * LmmsCore::s_song = NULL; ProjectJournal * LmmsCore::s_projectJournal = NULL; +#ifdef LMMS_HAVE_LV2 +Lv2Manager * LmmsCore::s_lv2Manager = nullptr; +#endif Ladspa2LMMS * LmmsCore::s_ladspaManager = NULL; void* LmmsCore::s_dndPluginKey = nullptr; DummyTrackContainer * LmmsCore::s_dummyTC = NULL; @@ -63,6 +67,10 @@ void LmmsCore::init( bool renderOnly ) s_fxMixer = new FxMixer; s_bbTrackContainer = new BBTrackContainer; +#ifdef LMMS_HAVE_LV2 + s_lv2Manager = new Lv2Manager; + s_lv2Manager->initPlugins(); +#endif s_ladspaManager = new Ladspa2LMMS; s_projectJournal->setJournalling( true ); @@ -95,6 +103,9 @@ void LmmsCore::destroy() deleteHelper( &s_fxMixer ); deleteHelper( &s_mixer ); +#ifdef LMMS_HAVE_LV2 + deleteHelper( &s_lv2Manager ); +#endif deleteHelper( &s_ladspaManager ); //delete ConfigManager::inst(); diff --git a/src/core/lv2/Lv2Basics.cpp b/src/core/lv2/Lv2Basics.cpp new file mode 100644 index 00000000000..b6be53a2cf6 --- /dev/null +++ b/src/core/lv2/Lv2Basics.cpp @@ -0,0 +1,49 @@ +/* + * Lv2Basics.cpp - basic Lv2 functions + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Basics.h" + +#ifdef LMMS_HAVE_LV2 + +QString qStringFromPluginNode(const LilvPlugin* plug, + LilvNode* (*getFunc)(const LilvPlugin*)) +{ + return QString::fromUtf8( + lilv_node_as_string(AutoLilvNode((*getFunc)(plug)).get())); +} + +QString qStringFromPortName(const LilvPlugin* plug, const LilvPort* port) +{ + return QString::fromUtf8( + lilv_node_as_string(AutoLilvNode(lilv_port_get_name(plug, port)).get())); +} + +std::string stdStringFromPortName(const LilvPlugin* plug, const LilvPort* port) +{ + return std::string( + lilv_node_as_string(AutoLilvNode(lilv_port_get_name(plug, port)).get())); +} + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp new file mode 100644 index 00000000000..3f50325e7d2 --- /dev/null +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -0,0 +1,191 @@ +/* + * Lv2ControlBase.cpp - Lv2 control base class + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2ControlBase.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "Engine.h" +#include "Lv2Manager.h" +#include "Lv2Proc.h" +#include "stdshims.h" + + + + +Plugin::PluginTypes Lv2ControlBase::check(const LilvPlugin *plugin, + std::vector &issues, bool printIssues) +{ + // for some reason, all checks can be done by one processor... + return Lv2Proc::check(plugin, issues, printIssues); +} + + + + +Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : + m_plugin(Engine::getLv2Manager()->getPlugin(uri)) +{ + if (m_plugin) + { + int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo + while (channelsLeft > 0) + { + std::unique_ptr newOne = make_unique(m_plugin, that); + if (newOne->isValid()) + { + channelsLeft -= std::max( + 1 + static_cast(newOne->inPorts().m_right), + 1 + static_cast(newOne->outPorts().m_right)); + Q_ASSERT(channelsLeft >= 0); + m_procs.push_back(std::move(newOne)); + } + else + { + qCritical() << "Failed instantiating LV2 processor"; + m_valid = false; + channelsLeft = 0; + } + } + if (m_valid) + { + m_channelsPerProc = DEFAULT_CHANNELS / m_procs.size(); + linkAllModels(); + } + } + else + { + qCritical() << "No Lv2 plugin found for URI" << uri; + m_valid = false; + } +} + + + + +Lv2ControlBase::~Lv2ControlBase() {} + + + + +LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) +{ + return (m_procs.size() > idx) ? m_procs[idx].get() : nullptr; +} + + + + +const LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) const +{ + return (m_procs.size() > idx) ? m_procs[idx].get() : nullptr; +} + + + + +void Lv2ControlBase::copyModelsFromLmms() { + for (auto& c : m_procs) { c->copyModelsFromCore(); } +} + + + + +void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { + unsigned firstChan = 0; // tell the procs which channels they shall read from + for (auto& c : m_procs) { + c->copyBuffersFromCore(buf, firstChan, m_channelsPerProc, frames); + firstChan += m_channelsPerProc; + } +} + + + + +void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { + unsigned firstChan = 0; // tell the procs which channels they shall write to + for (const auto& c : m_procs) { + c->copyBuffersToCore(buf, firstChan, m_channelsPerProc, frames); + firstChan += m_channelsPerProc; + } +} + + + + +void Lv2ControlBase::run(fpp_t frames) { + for (auto& c : m_procs) { c->run(frames); } +} + + + + +void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) +{ + LinkedModelGroups::saveSettings(doc, that); + + // TODO: save state if supported by plugin +} + + + + +void Lv2ControlBase::loadSettings(const QDomElement &that) +{ + LinkedModelGroups::loadSettings(that); + + // TODO: load state if supported by plugin +} + + + + +void Lv2ControlBase::loadFile(const QString &file) +{ + (void)file; +} + + + + +void Lv2ControlBase::reloadPlugin() +{ + // TODO +} + + + + +std::size_t Lv2ControlBase::controlCount() const { + std::size_t res = 0; + for (const auto& c : m_procs) { res += c->controlCount(); } + return res; +} + + + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp new file mode 100644 index 00000000000..bce3bf372ca --- /dev/null +++ b/src/core/lv2/Lv2Manager.cpp @@ -0,0 +1,163 @@ +/* + * Lv2Manager.cpp - Implementation of Lv2Manager class + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Manager.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include +#include +#include +#include + +#include "ConfigManager.h" +#include "Plugin.h" +#include "PluginFactory.h" +#include "Lv2ControlBase.h" +#include "PluginIssue.h" + + + + +Lv2Manager::Lv2Manager() +{ + const char* dbgStr = getenv("LMMS_LV2_DEBUG"); + m_debug = (dbgStr && *dbgStr); + + m_world = lilv_world_new(); + lilv_world_load_all(m_world); +} + + + + +Lv2Manager::~Lv2Manager() +{ + lilv_world_free(m_world); +} + + + + +AutoLilvNode Lv2Manager::uri(const char *uriStr) +{ + return AutoLilvNode(lilv_new_uri(m_world, uriStr)); +} + + + + +const LilvPlugin *Lv2Manager::getPlugin(const std::string &uri) +{ + auto itr = m_lv2InfoMap.find(uri); + return itr == m_lv2InfoMap.end() ? nullptr : itr->second.plugin(); +} + + + + +const LilvPlugin *Lv2Manager::getPlugin(const QString &uri) +{ + return getPlugin(uri.toStdString()); +} + + + + +void Lv2Manager::initPlugins() +{ + const LilvPlugins* plugins = lilv_world_get_all_plugins(m_world); + std::size_t pluginCount = 0, pluginsLoaded = 0; + QElapsedTimer timer; + timer.start(); + + LILV_FOREACH(plugins, itr, plugins) + { + const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); + + std::vector issues; + Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues, m_debug); + Lv2Info info(curPlug, type, issues.empty()); + + m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] + = std::move(info); + if(issues.empty()) { ++pluginsLoaded; } + ++pluginCount; + } + + qDebug() << "Lv2 plugin SUMMARY:" + << pluginsLoaded << "of" << pluginCount << " loaded in" + << timer.elapsed() << "msecs."; + if(pluginsLoaded != pluginCount) + { + if (m_debug) + { + qDebug() << + "If you don't want to see all this debug output, please set\n" + " environment variable \"LMMS_LV2_DEBUG\" to empty or\n" + " do not set it."; + } + else + { + qDebug() << + "For details about not loaded plugins, please set\n" + " environment variable \"LMMS_LV2_DEBUG\" to nonempty."; + } + } +} + + + + +// unused + untested yet +bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) +{ + const LilvPluginClasses* allClasses = lilv_world_get_plugin_classes(m_world); + const LilvPluginClass* root = lilv_world_get_plugin_class(m_world); + const LilvPluginClass* search = lilv_plugin_classes_get_by_uri(allClasses, + uri(uriStr).get()); + + auto clssEq = [](const LilvPluginClass* pc1, + const LilvPluginClass* pc2) -> bool + { + return lilv_node_equals( + lilv_plugin_class_get_uri(pc1), + lilv_plugin_class_get_uri(pc2)); + }; + bool isFound = false; + while (!(isFound = clssEq(clvss, search)) && !clssEq(clvss, root)) + { + clvss = lilv_plugin_classes_get_by_uri(allClasses, + lilv_plugin_class_get_parent_uri(clvss)); + } + return isFound; +} + + + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp new file mode 100644 index 00000000000..ae2d26d4971 --- /dev/null +++ b/src/core/lv2/Lv2Ports.cpp @@ -0,0 +1,256 @@ +/* + * Lv2Ports.cpp - Lv2 port classes implementation + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "Lv2Ports.h" + +#ifdef LMMS_HAVE_LV2 + +#include "Engine.h" +#include "Lv2Basics.h" +#include "Lv2Manager.h" + +namespace Lv2Ports { + + + + +const char *toStr(Flow pf) +{ + switch(pf) + { + case Flow::Unknown: return "unknown"; + case Flow::Input: return "input"; + case Flow::Output: return "output"; + } + return ""; +} + + + + +const char *toStr(Type pt) +{ + switch(pt) + { + case Type::Unknown: return "unknown"; + case Type::Control: return "control"; + case Type::Audio: return "audio"; + case Type::Event: return "event"; + case Type::Cv: return "cv"; + } + return ""; +} + + + + +const char *toStr(Vis pv) +{ + switch(pv) + { + case Vis::Toggled: return "toggled"; + case Vis::Enumeration: return "enumeration"; + case Vis::Integer: return "integer"; + case Vis::None: return "none"; + } + return ""; +} + + + + +std::vector Meta::get(const LilvPlugin *plugin, + std::size_t portNum) +{ + std::vector portIssues; + auto issue = [&portIssues](PluginIssueType i, std::string msg = "") { + portIssues.emplace_back(i, std::move(msg)); }; + + Lv2Manager* man = Engine::getLv2Manager(); + + const LilvPort* lilvPort = lilv_plugin_get_port_by_index( + plugin, static_cast(portNum)); + + auto portFunc = [&plugin, &lilvPort, &man]( + bool (*fptr)(const LilvPlugin*, const LilvPort*, const LilvNode*), + const char* str) { + return fptr(plugin, lilvPort, man->uri(str).get()); + }; + + auto hasProperty = [&portFunc](const char* str) { + return portFunc(lilv_port_has_property, str); }; + auto isA = [&portFunc](const char* str) { + return portFunc(lilv_port_is_a, str); }; + + const std::string portName = stdStringFromPortName(plugin, lilvPort); + + m_optional = hasProperty(LV2_CORE__connectionOptional); + + m_vis = hasProperty(LV2_CORE__integer) + ? Vis::Integer // WARNING: this may still be changed below + : hasProperty(LV2_CORE__enumeration) + ? Vis::Enumeration + : hasProperty(LV2_CORE__toggled) + ? Vis::Toggled + : Vis::None; + + if (isA(LV2_CORE__InputPort)) { m_flow = Flow::Input; } + else if (isA(LV2_CORE__OutputPort)) { m_flow = Flow::Output; } + else { + m_flow = Flow::Unknown; + issue(unknownPortFlow, portName); + } + + m_def = .0f; m_min = .0f; m_max = .0f; + + if (isA(LV2_CORE__ControlPort)) + { + m_type = Type::Control; + + if (m_flow == Flow::Input) + { + bool isToggle = m_vis == Vis::Toggled; + + LilvNode * defN, * minN = nullptr, * maxN = nullptr; + lilv_port_get_range(plugin, lilvPort, &defN, + isToggle ? nullptr : &minN, + isToggle ? nullptr : &maxN); + AutoLilvNode def(defN), min(minN), max(maxN); + + auto takeRangeValue = [&](LilvNode* node, + float& storeHere, PluginIssueType it) + { + if (node) { storeHere = lilv_node_as_float(node); } + else { issue(it, portName); } + }; + + takeRangeValue(def.get(), m_def, portHasNoDef); + if (!isToggle) + { + takeRangeValue(min.get(), m_min, portHasNoMin); + takeRangeValue(max.get(), m_max, portHasNoMax); + + if (m_max - m_min > 15.0f) + { + // range too large for spinbox visualisation, use knobs + // e.g. 0...15 would be OK + m_vis = Vis::None; + } + } + } + } + else if (isA(LV2_CORE__AudioPort)) { m_type = Type::Audio; } + else if (isA(LV2_CORE__CVPort)) { + issue(badPortType, "cvPort"); + m_type = Type::Cv; + } else { + if (m_optional) { m_used = false; } + else { + issue(PluginIssueType::unknownPortType, portName); + m_type = Type::Unknown; + } + } + + return portIssues; +} + + + + +QString PortBase::name() const +{ + AutoLilvNode node(lilv_port_get_name(m_plugin, m_port)); + QString res = lilv_node_as_string(node.get()); + return res; +} + + + + +QString PortBase::uri() const +{ + return lilv_node_as_string(lilv_port_get_symbol(m_plugin, m_port)); +} + + + + +Audio::Audio(std::size_t bufferSize, bool isSidechain, bool isOptional) + : m_buffer(bufferSize), m_sidechain(isSidechain), m_optional(isOptional) +{ +} + + + + +void Audio::copyBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) +{ + for (std::size_t f = 0; f < static_cast(frames); ++f) + { + m_buffer[f] = lmmsBuf[f][channel]; + } +} + + + + +void Audio::averageWithBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) +{ + for (std::size_t f = 0; f < static_cast(frames); ++f) + { + m_buffer[f] = (m_buffer[f] + lmmsBuf[f][channel]) / 2.0f; + } +} + + + + +void Audio::copyBuffersToCore(sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) const +{ + for (std::size_t f = 0; f < static_cast(frames); ++f) + { + lmmsBuf[f][channel] = m_buffer[f]; + } +} + + + + +// make the compiler happy, give each class with virtuals +// a function (the destructor here) which is in a cpp file +PortBase::~PortBase() {} +ConstVisitor::~ConstVisitor() {} +Visitor::~Visitor() {} + + + + +} // namespace Lv2Ports + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp new file mode 100644 index 00000000000..2d77f3f9835 --- /dev/null +++ b/src/core/lv2/Lv2Proc.cpp @@ -0,0 +1,544 @@ +/* + * Lv2Proc.cpp - Lv2 processor class + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Proc.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "AutomatableModel.h" +#include "ComboBoxModel.h" +#include "Engine.h" +#include "Lv2Manager.h" +#include "Lv2Ports.h" +#include "Mixer.h" + + + + +Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, + std::vector& issues, bool printIssues) +{ + unsigned maxPorts = lilv_plugin_get_num_ports(plugin); + enum { inCount, outCount, maxCount }; + unsigned audioChannels[maxCount] = { 0, 0 }; // input and output count + + for (unsigned portNum = 0; portNum < maxPorts; ++portNum) + { + Lv2Ports::Meta meta; + // does all port checks: + std::vector tmp = meta.get(plugin, portNum); + std::move(tmp.begin(), tmp.end(), std::back_inserter(issues)); + + bool portMustBeUsed = + !portIsSideChain(plugin, + lilv_plugin_get_port_by_index(plugin, portNum)) && + !portIsOptional(plugin, + lilv_plugin_get_port_by_index(plugin, portNum)); + if (meta.m_type == Lv2Ports::Type::Audio && portMustBeUsed) + ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output + ? outCount : inCount]; + } + + if (audioChannels[inCount] > 2) + issues.emplace_back(tooManyInputChannels, + std::to_string(audioChannels[inCount])); + if (audioChannels[outCount] == 0) + issues.emplace_back(noOutputChannel); + else if (audioChannels[outCount] > 2) + issues.emplace_back(tooManyOutputChannels, + std::to_string(audioChannels[outCount])); + + AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); + LILV_FOREACH (nodes, itr, reqFeats.get()) + { + issues.emplace_back(featureNotSupported, + lilv_node_as_string(lilv_nodes_get(reqFeats.get(), itr))); + } + + if (printIssues && issues.size()) + { + qDebug() << "Lv2 plugin" + << qStringFromPluginNode(plugin, lilv_plugin_get_name) + << "(URI:" + << lilv_node_as_uri(lilv_plugin_get_uri(plugin)) + << ") can not be loaded:"; + for (const PluginIssue& iss : issues) { qDebug() << " - " << iss; } + } + + return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2) + ? Plugin::Undefined + : (audioChannels[inCount] > 0) + ? Plugin::Effect + : Plugin::Instrument; +} + + + + +Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : + LinkedModelGroup(parent), + m_plugin(plugin) +{ + initPlugin(); +} + + + + +Lv2Proc::~Lv2Proc() { shutdownPlugin(); } + + + + +void Lv2Proc::dumpPorts() +{ + std::size_t num = 0; + for (const std::unique_ptr& port: m_ports) + { + (void)port; + dumpPort(num++); + } +} + + + + +void Lv2Proc::copyModelsFromCore() +{ + struct FloatFromModelVisitor : public ConstModelVisitor + { + const std::vector* m_scalePointMap; // in + float m_res; // out + void visit(const FloatModel& m) override { m_res = m.value(); } + void visit(const IntModel& m) override { + m_res = static_cast(m.value()); } + void visit(const BoolModel& m) override { + m_res = static_cast(m.value()); } + void visit(const ComboBoxModel& m) override { + m_res = (*m_scalePointMap)[static_cast(m.value())]; } + }; + + struct Copy : public Lv2Ports::Visitor + { + void visit(Lv2Ports::Control& ctrl) override + { + if (ctrl.m_flow == Lv2Ports::Flow::Input) + { + FloatFromModelVisitor ffm; + ffm.m_scalePointMap = &ctrl.m_scalePointMap; + ctrl.m_connectedModel->accept(ffm); + ctrl.m_val = ffm.m_res; + } + } + void visit(Lv2Ports::Cv& cv) override + { + if (cv.m_flow == Lv2Ports::Flow::Input) + { + FloatFromModelVisitor ffm; + ffm.m_scalePointMap = &cv.m_scalePointMap; + cv.m_connectedModel->accept(ffm); + // dirty fix, needs better interpolation + std::fill(cv.m_buffer.begin(), cv.m_buffer.end(), ffm.m_res); + } + } + } copy; + + for (const std::unique_ptr& port : m_ports) { + port->accept(copy); } +} + + + + +void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, + unsigned firstChan, unsigned num, + fpp_t frames) +{ + inPorts().m_left->copyBuffersFromCore(buf, firstChan, frames); + if (num > 1) + { + // if the caller requests to take input from two channels, but we only + // have one input channel... take medium of left and right for + // mono input + // (this happens if we have two outputs and only one input) + if (inPorts().m_right) + { + inPorts().m_right->copyBuffersFromCore(buf, firstChan + 1, frames); + } + else + { + inPorts().m_left->averageWithBuffersFromCore(buf, firstChan + 1, frames); + } + } +} + + + + +void Lv2Proc::copyBuffersToCore(sampleFrame* buf, + unsigned firstChan, unsigned num, + fpp_t frames) const +{ + outPorts().m_left->copyBuffersToCore(buf, firstChan + 0, frames); + if (num > 1) + { + // if the caller requests to copy into two channels, but we only have + // one output channel, duplicate our output + // (this happens if we have two inputs and only one output) + Lv2Ports::Audio* ap = outPorts().m_right + ? outPorts().m_right : outPorts().m_left; + ap->copyBuffersToCore(buf, firstChan + 1, frames); + } +} + + + + +void Lv2Proc::run(fpp_t frames) +{ + lilv_instance_run(m_instance, static_cast(frames)); +} + + + + +AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) +{ + // unused currently + AutomatableModel *mod; + auto itr = m_connectedModels.find(uri.toUtf8().data()); + if (itr != m_connectedModels.end()) { mod = itr->second; } + else { mod = nullptr; } + return mod; +} + + + + +void Lv2Proc::initPlugin() +{ + createPorts(); + + m_instance = lilv_plugin_instantiate(m_plugin, + Engine::mixer()->processingSampleRate(), + nullptr); + + if (m_instance) + { + for (std::size_t portNum = 0; portNum < m_ports.size(); ++portNum) + connectPort(portNum); + lilv_instance_activate(m_instance); + } + else + { + qCritical() << "Failed to create an instance of" + << qStringFromPluginNode(m_plugin, lilv_plugin_get_name) + << "(URI:" + << lilv_node_as_uri(lilv_plugin_get_uri(m_plugin)) + << ")"; + m_valid = false; + } +} + + + + +void Lv2Proc::shutdownPlugin() +{ + lilv_instance_deactivate(m_instance); + lilv_instance_free(m_instance); + m_instance = nullptr; +} + + + + +void Lv2Proc::loadFileInternal(const QString &file) +{ + (void)file; +} + + + + +void Lv2Proc::createPort(std::size_t portNum) +{ + Lv2Ports::Meta meta; + meta.get(m_plugin, portNum); + + const LilvPort* lilvPort = lilv_plugin_get_port_by_index(m_plugin, + static_cast(portNum)); + Lv2Ports::PortBase* port; + if (meta.m_type == Lv2Ports::Type::Control) + { + Lv2Ports::Control* ctrl = new Lv2Ports::Control; + if (meta.m_flow == Lv2Ports::Flow::Input) + { + AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); + QString dispName = lilv_node_as_string(node.get()); + switch (meta.m_vis) + { + case Lv2Ports::Vis::None: + { + // allow ~1000 steps + float stepSize = (meta.m_max - meta.m_min) / 1000.0f; + + // make multiples of 0.01 (or 0.1 for larger values) + float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; + stepSize -= fmodf(stepSize, minStep); + stepSize = std::max(stepSize, minStep); + + ctrl->m_connectedModel.reset( + new FloatModel(meta.m_def, meta.m_min, meta.m_max, + stepSize, nullptr, dispName)); + break; + } + case Lv2Ports::Vis::Integer: + ctrl->m_connectedModel.reset( + new IntModel(static_cast(meta.m_def), + static_cast(meta.m_min), + static_cast(meta.m_max), + nullptr, dispName)); + break; + case Lv2Ports::Vis::Enumeration: + { + ComboBoxModel* comboModel + = new ComboBoxModel( + nullptr, dispName); + LilvScalePoints* sps = + lilv_port_get_scale_points(m_plugin, lilvPort); + LILV_FOREACH(scale_points, i, sps) + { + const LilvScalePoint* sp = lilv_scale_points_get(sps, i); + ctrl->m_scalePointMap.push_back(lilv_node_as_float( + lilv_scale_point_get_value(sp))); + comboModel->addItem( + lilv_node_as_string( + lilv_scale_point_get_label(sp))); + } + lilv_scale_points_free(sps); + ctrl->m_connectedModel.reset(comboModel); + break; + } + case Lv2Ports::Vis::Toggled: + ctrl->m_connectedModel.reset( + new BoolModel(static_cast(meta.m_def), + nullptr, dispName)); + break; + } + } + port = ctrl; + } + else if (meta.m_type == Lv2Ports::Type::Audio) + { + Lv2Ports::Audio* audio = + new Lv2Ports::Audio( + static_cast( + Engine::mixer()->framesPerPeriod()), + portIsSideChain(m_plugin, lilvPort), + portIsOptional(m_plugin, lilvPort) + ); + port = audio; + } else { + port = new Lv2Ports::Unknown; + } + + // `meta` is of class `Lv2Ports::Meta` and `port` is of a child class + // we can now assign the `Lv2Ports::Meta` part of meta to ports, leaving + // the additional members of `port` unchanged + *static_cast(port) = meta; + port->m_port = lilvPort; + port->m_plugin = m_plugin; + + m_ports[portNum].reset(port); +} + + + + +void Lv2Proc::createPorts() +{ + // register ports at the processor after creation, + // i.e. link their data or count them + struct RegisterPort : public Lv2Ports::Visitor + { + Lv2Proc* m_proc; + + void visit(Lv2Ports::Control& ctrl) override + { + if (ctrl.m_flow == Lv2Ports::Flow::Input) + { + AutomatableModel* amo = ctrl.m_connectedModel.get(); + m_proc->m_connectedModels.emplace( + lilv_node_as_string(lilv_port_get_symbol( + m_proc->m_plugin, ctrl.m_port)), + amo); + m_proc->addModel(amo, ctrl.uri()); + } + } + + void visit(Lv2Ports::Audio& audio) override + { + if (audio.mustBeUsed()) + { + StereoPortRef dummy; + StereoPortRef* portRef = &dummy; + switch (audio.m_flow) + { + case Lv2Ports::Flow::Input: + portRef = &m_proc->m_inPorts; + break; + case Lv2Ports::Flow::Output: + portRef = &m_proc->m_outPorts; + break; + case Lv2Ports::Flow::Unknown: + break; + } + // in Lv2, leftPort is defined to be the first port + if (!portRef->m_left) { portRef->m_left = &audio; } + else if (!portRef->m_right) { portRef->m_right = &audio; } + } + } + }; + + std::size_t maxPorts = lilv_plugin_get_num_ports(m_plugin); + m_ports.resize(maxPorts); + + for (std::size_t portNum = 0; portNum < maxPorts; ++portNum) + { + createPort(portNum); + RegisterPort registerPort; + registerPort.m_proc = this; + m_ports[portNum]->accept(registerPort); + } + + // initially assign model values to port values + copyModelsFromCore(); + + // debugging: + //dumpPorts(); +} + + + + +struct ConnectPortVisitor : public Lv2Ports::Visitor +{ + std::size_t m_num; + LilvInstance* m_instance; + void connectPort(void* location) { + lilv_instance_connect_port(m_instance, + static_cast(m_num), location); + } + void visit(Lv2Ports::Control& ctrl) override { connectPort(&ctrl.m_val); } + void visit(Lv2Ports::Audio& audio) override + { + connectPort((audio.mustBeUsed()) ? audio.m_buffer.data() : nullptr); + } + void visit(Lv2Ports::Unknown&) override { connectPort(nullptr); } + ~ConnectPortVisitor() override; +}; + +ConnectPortVisitor::~ConnectPortVisitor() {} + +void Lv2Proc::connectPort(std::size_t num) +{ + ConnectPortVisitor connect; + connect.m_num = num; + connect.m_instance = m_instance; + m_ports[num]->accept(connect); +} + + + + +void Lv2Proc::dumpPort(std::size_t num) +{ + struct DumpPortDetail : public Lv2Ports::ConstVisitor + { + void visit(const Lv2Ports::Control& ctrl) override { + qDebug() << " control port"; + // output ports may be uninitialized yet, only print inputs + if (ctrl.m_flow == Lv2Ports::Flow::Input) + { + qDebug() << " value:" << ctrl.m_val; + } + } + void visit(const Lv2Ports::Audio& audio) override { + qDebug() << (audio.isSideChain() ? " audio port (sidechain)" + : " audio port"); + qDebug() << " buffer size:" << audio.bufferSize(); + } + }; + + const Lv2Ports::PortBase& port = *m_ports[num]; + qDebug().nospace() << "port " << num << ":"; + qDebug() << " name:" + << qStringFromPortName(m_plugin, port.m_port); + qDebug() << " flow: " << Lv2Ports::toStr(port.m_flow); + qDebug() << " type: " << Lv2Ports::toStr(port.m_type); + qDebug() << " visualization: " << Lv2Ports::toStr(port.m_vis); + if (port.m_type == Lv2Ports::Type::Control || port.m_type == Lv2Ports::Type::Cv) + { + qDebug() << " default:" << port.m_def; + qDebug() << " min:" << port.m_min; + qDebug() << " max:" << port.m_max; + } + qDebug() << " optional: " << port.m_optional; + qDebug() << " => USED: " << port.m_used; + + DumpPortDetail dumper; + port.accept(dumper); +} + + + + +bool Lv2Proc::portIsSideChain(const LilvPlugin *plugin, const LilvPort *port) +{ + return lilv_port_has_property(plugin, port, + uri(LV2_CORE_PREFIX "isSidechain").get()); +} + + + + +bool Lv2Proc::portIsOptional(const LilvPlugin *plugin, const LilvPort *port) +{ + return lilv_port_has_property(plugin, port, + uri(LV2_CORE__connectionOptional).get()); +} + + + + +AutoLilvNode Lv2Proc::uri(const char *uriStr) +{ + return Engine::getLv2Manager()->uri(uriStr); +} + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp new file mode 100644 index 00000000000..3f86c5324e2 --- /dev/null +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -0,0 +1,184 @@ +/* + * Lv2SubPluginFeatures.cpp - derivation from + * Plugin::Descriptor::SubPluginFeatures for + * hosting LV2 plugins + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2SubPluginFeatures.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include + +#include "Engine.h" +#include "Lv2Basics.h" +#include "Lv2Manager.h" + + +const LilvPlugin *Lv2SubPluginFeatures::getPlugin( + const Plugin::Descriptor::SubPluginFeatures::Key &k) +{ + const LilvPlugin* result = Engine::getLv2Manager()-> + getPlugin(k.attributes["uri"]); + Q_ASSERT(result); + return result; +} + + + + +QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug) +{ + return qStringFromPluginNode(plug, lilv_plugin_get_name); +} + + + + +Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes type) : + SubPluginFeatures(type) +{ +} + + + + +void Lv2SubPluginFeatures::fillDescriptionWidget(QWidget *parent, + const Key *k) const +{ + const LilvPlugin *plug = getPlugin(*k); + + QLabel *label = new QLabel(parent); + label->setText(QWidget::tr("Name: ") + pluginName(plug)); + + QLabel *label2 = new QLabel(parent); + label2->setText(QWidget::tr("URI: ") + + lilv_node_as_uri(lilv_plugin_get_uri(plug))); + + QWidget *maker = new QWidget(parent); + QHBoxLayout *l = new QHBoxLayout(maker); + l->setMargin(0); + l->setSpacing(0); + + QLabel *maker_label = new QLabel(maker); + maker_label->setText(QWidget::tr("Maker: ")); + maker_label->setAlignment(Qt::AlignTop); + + QLabel *maker_content = new QLabel(maker); + maker_content->setText( + qStringFromPluginNode(plug, lilv_plugin_get_author_name)); + maker_content->setWordWrap(true); + + l->addWidget(maker_label); + l->addWidget(maker_content, 1); + + QWidget *copyright = new QWidget(parent); + l = new QHBoxLayout(copyright); + l->setMargin(0); + l->setSpacing(0); + copyright->setMinimumWidth(parent->minimumWidth()); + + QLabel *copyright_label = new QLabel(copyright); + copyright_label->setText(QWidget::tr("Copyright: ")); + copyright_label->setAlignment(Qt::AlignTop); + + QLabel *copyright_content = new QLabel(copyright); + copyright_content->setText(""); + copyright_content->setWordWrap(true); + l->addWidget(copyright_label); + l->addWidget(copyright_content, 1); + + AutoLilvNodes extensions(lilv_plugin_get_extension_data(plug)); + (void)extensions; + // possibly TODO: version, project, plugin type, number of channels +} + + + + +QString Lv2SubPluginFeatures::additionalFileExtensions( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + (void)k; + // lv2 only loads .lv2 files + // maybe add conversions later, e.g. for loading xmz + return QString(); +} + + + + +QString Lv2SubPluginFeatures::displayName( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + return pluginName(getPlugin(k)); +} + + + + +QString Lv2SubPluginFeatures::description( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + (void)k; + return QString::fromUtf8("description not implemented yet"); // TODO +} + + + + +const PixmapLoader *Lv2SubPluginFeatures::logo( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + (void)k; // TODO + return nullptr; +} + + + + +void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor *desc, + KeyList &kl) const +{ + Lv2Manager *lv2Mgr = Engine::getLv2Manager(); + for (const auto &uriInfoPair : *lv2Mgr) + { + if (uriInfoPair.second.type() == m_type && uriInfoPair.second.isValid()) + { + using KeyType = + Plugin::Descriptor::SubPluginFeatures::Key; + KeyType::AttributeMap atm; + atm["uri"] = QString::fromUtf8(uriInfoPair.first.c_str()); + const LilvPlugin* plug = uriInfoPair.second.plugin(); + + kl.push_back(KeyType(desc, pluginName(plug), atm)); + //qDebug() << "Found LV2 sub plugin key of type" << + // m_type << ":" << pr.first.c_str(); + } + } +} + +#endif // LMMS_HAVE_LV2 + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 759be387290..81e588c6659 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -19,6 +19,7 @@ SET(LMMS_SRCS gui/LfoControllerDialog.cpp gui/LmmsPalette.cpp gui/LmmsStyle.cpp + gui/Lv2ViewBase.cpp gui/MainApplication.cpp gui/MainWindow.cpp gui/MidiSetupWidget.cpp diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index c976c39f689..b37bf03f207 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -457,7 +457,11 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) m_previewPlayHandle = s; delete tf; } - else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" ) && + else if ( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" +#ifdef LMMS_HAVE_LV2 + || f->extension() == "lv2" +#endif + ) && ! pluginFactory->pluginSupportingExtension(f->extension()).info.isNull() ) { m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin ); @@ -1069,6 +1073,11 @@ void FileItem::determineFileType( void ) m_type = VstPluginFile; m_handling = LoadByPlugin; } + else if ( ext == "lv2" ) + { + m_type = PresetFile; + m_handling = LoadByPlugin; + } else { m_type = UnknownFile; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp new file mode 100644 index 00000000000..9b9217e48b2 --- /dev/null +++ b/src/gui/Lv2ViewBase.cpp @@ -0,0 +1,237 @@ +/* + * Lv2ViewBase.cpp - base class for Lv2 plugin views + * + * Copyright (c) 2018-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2ViewBase.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include +#include +#include + +#include "Controls.h" +#include "Engine.h" +#include "GuiApplication.h" +#include "embed.h" +#include "gui_templates.h" +#include "LedCheckbox.h" +#include "Lv2ControlBase.h" +#include "Lv2Manager.h" +#include "Lv2Proc.h" +#include "Lv2Ports.h" +#include "MainWindow.h" +#include "SubWindow.h" + + + + +Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : + LinkedModelGroupView (parent, ctrlBase, colNum) +{ + class SetupWidget : public Lv2Ports::ConstVisitor + { + public: + QWidget* m_par; // input + const LilvNode* m_commentUri; // input + Control* m_control = nullptr; // output + void visit(const Lv2Ports::Control& port) override + { + if (port.m_flow == Lv2Ports::Flow::Input) + { + using PortVis = Lv2Ports::Vis; + + switch (port.m_vis) + { + case PortVis::None: + m_control = new KnobControl(m_par); + break; + case PortVis::Integer: + m_control = new LcdControl((port.m_max <= 9.0f) ? 1 : 2, + m_par); + break; + case PortVis::Enumeration: + m_control = new ComboControl(m_par); + break; + case PortVis::Toggled: + m_control = new CheckControl(m_par); + break; + } + m_control->setText(port.name()); + + AutoLilvNodes props(lilv_port_get_value( + port.m_plugin, port.m_port, m_commentUri)); + LILV_FOREACH(nodes, itr, props.get()) + { + const LilvNode* nod = lilv_nodes_get(props.get(), itr); + m_control->topWidget()->setToolTip(lilv_node_as_string(nod)); + break; + } + } + } + }; + + AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); + ctrlBase->foreach_port( + [this, &commentUri](const Lv2Ports::PortBase* port) + { + SetupWidget setup; + setup.m_par = this; + setup.m_commentUri = commentUri.get(); + port->accept(setup); + + if (setup.m_control) + { + addControl(setup.m_control, + lilv_node_as_string(lilv_port_get_symbol( + port->m_plugin, port->m_port)), + port->name().toUtf8().data(), + false); + } + }); +} + + + + +Lv2ViewProc::~Lv2ViewProc() {} + + + + +AutoLilvNode Lv2ViewProc::uri(const char *uriStr) +{ + return Engine::getLv2Manager()->uri(uriStr); +} + + + + +Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) +{ + QGridLayout* grid = new QGridLayout(meAsWidget); + + QHBoxLayout* btnBox = new QHBoxLayout(); + if (/* DISABLES CODE */ (false)) + { + m_reloadPluginButton = new QPushButton(QObject::tr("Reload Plugin"), + meAsWidget); + btnBox->addWidget(m_reloadPluginButton, 0); + } + + if (/* DISABLES CODE */ (false)) // TODO: check if the plugin has the UI extension + { + m_toggleUIButton = new QPushButton(QObject::tr("Show GUI"), + meAsWidget); + m_toggleUIButton->setCheckable(true); + m_toggleUIButton->setChecked(false); + m_toggleUIButton->setIcon(embed::getIconPixmap("zoom")); + m_toggleUIButton->setFont( + pointSize<8>(m_toggleUIButton->font())); + btnBox->addWidget(m_toggleUIButton, 0); + } + btnBox->addStretch(1); + + meAsWidget->setAcceptDrops(true); + + // note: the lifetime of C++ objects ends after the top expression in the + // expression syntax tree, so the AutoLilvNode gets freed after the function + // has been called + AutoLilvNodes props(lilv_plugin_get_value(ctrlBase->getPlugin(), + uri(LILV_NS_RDFS "comment").get())); + LILV_FOREACH(nodes, itr, props.get()) + { + const LilvNode* node = lilv_nodes_get(props.get(), itr); + QLabel* infoLabel = new QLabel(lilv_node_as_string(node)); + infoLabel->setWordWrap(true); + infoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + m_helpButton = new QPushButton(QObject::tr("Help")); + m_helpButton->setCheckable(true); + btnBox->addWidget(m_helpButton); + + m_helpWindow = gui->mainWindow()->addWindowedWidget(infoLabel); + m_helpWindow->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Expanding); + m_helpWindow->setAttribute(Qt::WA_DeleteOnClose, false); + m_helpWindow->hide(); + + break; + } + + if (m_reloadPluginButton || m_toggleUIButton || m_helpButton) + { + grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); + } + else { delete btnBox; } + + m_procView = new Lv2ViewProc(meAsWidget, ctrlBase->control(0), m_colNum); + grid->addWidget(m_procView, Rows::ProcRow, 0); +} + + + + +Lv2ViewBase::~Lv2ViewBase() { + // TODO: hide UI if required +} + + + + +void Lv2ViewBase::toggleHelp(bool visible) +{ + if (m_helpWindow) + { + if (visible) { m_helpWindow->show(); m_helpWindow->raise(); } + else { m_helpWindow->hide(); } + } +} + + + + +void Lv2ViewBase::modelChanged(Lv2ControlBase *ctrlBase) +{ + // reconnect models + if (m_toggleUIButton) + { + m_toggleUIButton->setChecked(ctrlBase->hasGui()); + } + + LinkedModelGroupsView::modelChanged(ctrlBase); +} + + + + +AutoLilvNode Lv2ViewBase::uri(const char *uriStr) +{ + return Engine::getLv2Manager()->uri(uriStr); +} + + +#endif // LMMS_HAVE_LV2 diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index bf8863ec616..2be265e047b 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -138,7 +138,7 @@ MainWindow::MainWindow() : sideBar->appendTab( new FileBrowser( confMgr->userPresetsDir() + "*" + confMgr->factoryPresetsDir(), - "*.xpf *.cs.xml *.xiz", + "*.xpf *.cs.xml *.xiz *.lv2", tr( "My Presets" ), embed::getIconPixmap( "preset_file" ).transformed( QTransform().rotate( 90 ) ), splitter , false, true ) ); diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 3ea9d749c0c..86882d22e25 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -13,6 +13,8 @@ #cmakedefine LMMS_HAVE_FLUIDSYNTH #cmakedefine LMMS_HAVE_JACK #cmakedefine LMMS_HAVE_WEAKJACK +#cmakedefine LMMS_HAVE_LV2 +#cmakedefine LMMS_HAVE_SUIL #cmakedefine LMMS_HAVE_MP3LAME #cmakedefine LMMS_HAVE_OGGVORBIS #cmakedefine LMMS_HAVE_OSS From 3a985ff1fc825051679bf694892d77d2fdb3d16a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 24 May 2020 13:20:05 +0200 Subject: [PATCH 017/180] Fix #4201: BB editor: adjust cursor position (#5489) This fixes an offset for cursors whose pointer position varies between different themes. --- data/themes/classic/style.css | 2 ++ data/themes/default/style.css | 2 ++ include/Track.h | 9 ++++++++- src/core/Track.cpp | 17 +++++++++++++++-- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 112d8f05cab..50a0155c947 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -618,6 +618,8 @@ TrackContentObjectView { qproperty-textColor: rgb( 255, 255, 255 ); qproperty-textShadowColor: rgb( 0, 0, 0 ); qproperty-gradient: true; /* boolean property, set true to have a gradient */ + /* finger tip offset of cursor */ + qproperty-mouseHotspotHand: 3px 3px; } /* instrument pattern */ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index a9af1139fef..3ba61f48c09 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -653,6 +653,8 @@ TrackContentObjectView { qproperty-textColor: #fff; qproperty-textShadowColor: rgba(0,0,0,200); qproperty-gradient: false; /* boolean property, set true to have a gradient */ + /* finger tip offset of cursor */ + qproperty-mouseHotspotHand: 7px 2px; } /* instrument pattern */ diff --git a/include/Track.h b/include/Track.h index bc9f161a9b3..bd1a9f36d71 100644 --- a/include/Track.h +++ b/include/Track.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -201,6 +202,9 @@ class TrackContentObjectView : public selectableObject, public ModelView Q_PROPERTY( QColor textShadowColor READ textShadowColor WRITE setTextShadowColor ) Q_PROPERTY( QColor BBPatternBackground READ BBPatternBackground WRITE setBBPatternBackground ) Q_PROPERTY( bool gradient READ gradient WRITE setGradient ) + // We have to use a QSize here because using QPoint isn't supported. + // width -> x, height -> y + Q_PROPERTY( QSize mouseHotspotHand WRITE setMouseHotspotHand ) public: TrackContentObjectView( TrackContentObject * tco, TrackView * tv ); @@ -233,6 +237,7 @@ class TrackContentObjectView : public selectableObject, public ModelView void setTextShadowColor( const QColor & c ); void setBBPatternBackground( const QColor & c ); void setGradient( const bool & b ); + void setMouseHotspotHand(const QSize & s); // access needsUpdate member variable bool needsUpdate(); @@ -302,8 +307,10 @@ protected slots: QColor m_textShadowColor; QColor m_BBPatternBackground; bool m_gradient; + QSize m_mouseHotspotHand; // QSize must be used because QPoint isn't supported by property system + bool m_cursorSetYet; - bool m_needsUpdate; + bool m_needsUpdate; inline void setInitialMousePos( QPoint pos ) { m_initialMousePos = pos; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 6a6b0deb133..3f9e04fe641 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -257,6 +257,8 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, m_textShadowColor( 0, 0, 0 ), m_BBPatternBackground( 0, 0, 0 ), m_gradient( true ), + m_mouseHotspotHand( 0, 0 ), + m_cursorSetYet( false ), m_needsUpdate( true ) { if( s_textFloat == NULL ) @@ -268,7 +270,7 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, setAttribute( Qt::WA_OpaquePaintEvent, true ); setAttribute( Qt::WA_DeleteOnClose, true ); setFocusPolicy( Qt::StrongFocus ); - setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); + setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); move( 0, 0 ); show(); @@ -317,6 +319,12 @@ TrackContentObjectView::~TrackContentObjectView() */ void TrackContentObjectView::update() { + if( !m_cursorSetYet ) + { + setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + m_cursorSetYet = true; + } + if( fixedTCOs() ) { updateLength(); @@ -387,6 +395,11 @@ void TrackContentObjectView::setBBPatternBackground( const QColor & c ) void TrackContentObjectView::setGradient( const bool & b ) { m_gradient = b; } +void TrackContentObjectView::setMouseHotspotHand(const QSize & s) +{ + m_mouseHotspotHand = s; +} + // access needsUpdate member variable bool TrackContentObjectView::needsUpdate() { return m_needsUpdate; } @@ -572,7 +585,7 @@ void TrackContentObjectView::leaveEvent( QEvent * e ) { if( cursor().shape() != Qt::BitmapCursor ) { - setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); + setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); } if( e != NULL ) { From c6a1abe15011dadb1a1e0357632bdec8b7a8c7cb Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 24 May 2020 13:35:16 +0200 Subject: [PATCH 018/180] Fix #5194: Fix knobs moving too fast (#5360) This PR fixes issues on systems where `QCursor::setPos()` has no effect or is not reliable. These issues included knobs moving to fast on some operating systems. Affected widgets are `Knob` and `LcdSpinBox`. With this PR, on all operating systems, the `setPos` calls are removed and the cursor is not hidden anymore, so the mouse keeps moving normally when changing values of one of the widgets. As now the previous pointer position keeps moving (instead of being reset to the original position using `QCursor::setPos`), the mathematics that translate pointer pixel distance to `Knob`/`LcdSpinBox` value increase have to be changed: * The `Knob` transition function is now linear and uses a new factor. * `LcdSpinBox` now uses float values and saves the current float remainder (this is actually a separate issue revealed by this fix), leading to a fluent, non hanging movement. --- include/Knob.h | 3 +-- include/LcdSpinBox.h | 3 ++- src/gui/widgets/Knob.cpp | 17 ++++++++--------- src/gui/widgets/LcdSpinBox.cpp | 34 ++++++++++++++++++---------------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/include/Knob.h b/include/Knob.h index d4214334ce8..e54ed103f05 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -174,8 +174,7 @@ private slots: BoolModel m_volumeKnob; FloatModel m_volumeRatio; - QPoint m_mouseOffset; - QPoint m_origMousePos; + QPoint m_lastMousePos; //!< mouse position in last mouseMoveEvent float m_leftOver; bool m_buttonPressed; diff --git a/include/LcdSpinBox.h b/include/LcdSpinBox.h index 0bac3ddc070..38c524c5ad9 100644 --- a/include/LcdSpinBox.h +++ b/include/LcdSpinBox.h @@ -73,8 +73,9 @@ public slots: virtual void mouseDoubleClickEvent( QMouseEvent * _me ); private: + float m_remainder; //!< floating offset of spinbox in [-0.5, 0.5] bool m_mouseMoving; - QPoint m_origMousePos; + QPoint m_lastMousePos; //!< mouse position in last mouseMoveEvent int m_displayOffset; void enterValue(); diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index e559d120c99..250eb0ad38d 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -497,8 +497,8 @@ float Knob::getValue( const QPoint & _p ) { float value; - // arcane mathemagicks for calculating knob movement - value = ( ( _p.y() + _p.y() * qMin( qAbs( _p.y() / 2.5f ), 6.0f ) ) ) / 12.0f; + // knob value increase is linear to mouse movement + value = .4f * _p.y(); // if shift pressed we want slower movement if( gui->mainWindow()->isShiftPressed() ) @@ -587,13 +587,11 @@ void Knob::mousePressEvent( QMouseEvent * _me ) } const QPoint & p = _me->pos(); - m_origMousePos = p; - m_mouseOffset = QPoint(0, 0); + m_lastMousePos = p; m_leftOver = 0.0f; emit sliderPressed(); - QApplication::setOverrideCursor( Qt::BlankCursor ); s_textFloat->setText( displayValue() ); s_textFloat->moveGlobal( this, QPoint( width() + 2, 0 ) ); @@ -618,12 +616,13 @@ void Knob::mousePressEvent( QMouseEvent * _me ) void Knob::mouseMoveEvent( QMouseEvent * _me ) { - if( m_buttonPressed && _me->pos() != m_origMousePos ) + if( m_buttonPressed && _me->pos() != m_lastMousePos ) { - m_mouseOffset = _me->pos() - m_origMousePos; - setPosition( m_mouseOffset ); + // knob position is changed depending on last mouse position + setPosition( _me->pos() - m_lastMousePos ); emit sliderMoved( model()->value() ); - QCursor::setPos( mapToGlobal( m_origMousePos ) ); + // original position for next time is current position + m_lastMousePos = _me->pos(); } s_textFloat->setText( displayValue() ); } diff --git a/src/gui/widgets/LcdSpinBox.cpp b/src/gui/widgets/LcdSpinBox.cpp index 7102f5f6baa..07080edc755 100644 --- a/src/gui/widgets/LcdSpinBox.cpp +++ b/src/gui/widgets/LcdSpinBox.cpp @@ -23,6 +23,7 @@ * */ +#include #include #include #include @@ -40,8 +41,9 @@ LcdSpinBox::LcdSpinBox( int numDigits, QWidget* parent, const QString& name ) : LcdWidget( numDigits, parent, name ), IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ), + m_remainder( 0.f ), m_mouseMoving( false ), - m_origMousePos(), + m_lastMousePos(), m_displayOffset( 0 ) { } @@ -52,8 +54,9 @@ LcdSpinBox::LcdSpinBox( int numDigits, QWidget* parent, const QString& name ) : LcdSpinBox::LcdSpinBox( int numDigits, const QString& style, QWidget* parent, const QString& name ) : LcdWidget( numDigits, parent, name ), IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ), + m_remainder( 0.f ), m_mouseMoving( false ), - m_origMousePos(), + m_lastMousePos(), m_displayOffset( 0 ) { } @@ -98,8 +101,7 @@ void LcdSpinBox::mousePressEvent( QMouseEvent* event ) event->y() < cellHeight() + 2 ) { m_mouseMoving = true; - m_origMousePos = event->globalPos(); - QApplication::setOverrideCursor( Qt::BlankCursor ); + m_lastMousePos = event->globalPos(); AutomatableModel *thisModel = model(); if( thisModel ) @@ -121,15 +123,20 @@ void LcdSpinBox::mouseMoveEvent( QMouseEvent* event ) { if( m_mouseMoving ) { - int dy = event->globalY() - m_origMousePos.y(); - if( event->modifiers() & Qt::ShiftModifier ) - dy = qBound( -4, dy/4, 4 ); - if( dy > 1 || dy < -1 ) + int dy = event->globalY() - m_lastMousePos.y(); + if( dy ) { - model()->setInitValue( model()->value() - - dy / 2 * model()->step() ); + float fdy = static_cast(dy); + if( event->modifiers() & Qt::ShiftModifier ) { + fdy = qBound( -4.f, fdy/4.f, 4.f ); + } + float floatValNotRounded = + model()->value() + m_remainder - fdy / 2.f * model()->step(); + float floatValRounded = roundf( floatValNotRounded ); + m_remainder = floatValNotRounded - floatValRounded; + model()->setInitValue( floatValRounded ); emit manualChange(); - QCursor::setPos( m_origMousePos ); + m_lastMousePos = event->globalPos(); } } } @@ -142,10 +149,7 @@ void LcdSpinBox::mouseReleaseEvent( QMouseEvent* ) if( m_mouseMoving ) { model()->restoreJournallingState(); - - QCursor::setPos( m_origMousePos ); QApplication::restoreOverrideCursor(); - m_mouseMoving = false; } } @@ -187,5 +191,3 @@ void LcdSpinBox::enterValue() } } - - From 0528a00ccac94e5fccafd5e175371b21451dcf9d Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Sun, 24 May 2020 20:59:38 +0200 Subject: [PATCH 019/180] Compensate beat note length when stretching (#5515) * Compensate beat note length when stretching We allow stretching beat notes to normal notes but the length starts from -192 so there is a lag for one whole note before any change is seen. Compensate by setting the oldNote value to 1 when stretching if the note is 0 or below in length. Co-authored-by: Spekular --- src/gui/editors/PianoRoll.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 807696775a0..3c88b4c43d9 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1606,6 +1606,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // then resize the note m_action = ActionResizeNote; + for (Note *note : getSelectedNotes()) + { + if (note->oldLength() <= 0) { note->setOldLength(4); } + } + // set resize-cursor QCursor c( Qt::SizeHorCursor ); QApplication::setOverrideCursor( c ); From f3032575af20b1868540ec8cf6beddce38e8510b Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 24 May 2020 21:02:24 +0200 Subject: [PATCH 020/180] Remove stopPlaying connection Missed this in #5477 --- src/tracks/SampleTrack.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index f377e35cfa3..9232cc8b0d4 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -843,8 +843,6 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_activityIndicator->setGeometry(settingsWidgetWidth - 2 * 24 - 11, 2, 8, 28); m_activityIndicator->show(); connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); - connect(Engine::getSong(), SIGNAL(stopped()), - this, SLOT(stopPlaying())); setModel( _t ); From 82f9239cd131dad94d6dc27c0337ac5d649fc53d Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Wed, 27 May 2020 18:33:54 +0200 Subject: [PATCH 021/180] 24 bit FLAC export. Clip negative side of wave (#5501) 24 bit FLAC export. Clip negative side of wave to counteract a bug in libsndfile < 1.0.29 Co-authored-by: Spekular Co-authored-by: Hyunjin Song --- src/core/audio/AudioFileFlac.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/audio/AudioFileFlac.cpp b/src/core/audio/AudioFileFlac.cpp index d9d91f54bea..cb159e46d5e 100644 --- a/src/core/audio/AudioFileFlac.cpp +++ b/src/core/audio/AudioFileFlac.cpp @@ -22,6 +22,9 @@ * */ +#include + +#include #include #include "AudioFileFlac.h" @@ -86,6 +89,7 @@ bool AudioFileFlac::startEncoding() void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames, float master_gain) { OutputSettings::BitDepth depth = getOutputSettings().getBitDepth(); + float clipvalue = std::nextafterf( -1.0f, 0.0f ); if (depth == OutputSettings::Depth_24Bit || depth == OutputSettings::Depth_32Bit) // Float encoding { @@ -94,7 +98,10 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram { for(ch_cnt_t channel=0; channel(buf.get()),frames); From 97680e081efe93059930b43dd9a901b8c72b10d7 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 1 Jun 2020 22:48:34 -0300 Subject: [PATCH 022/180] Allows instruments to keep the MIDI channel information when forwarding (#5470) Now it's possible to forward received MIDI events with their original channel, either to another track or to the instrument plugin itself. To do that, the user must select the channel "--" on the MIDI output widget. In that case, all MIDI events will be forwarded with their original channel, and the MIDI events produced by the track itself will be sent with the default channel. --- include/MidiPort.h | 5 ++++- src/core/midi/MidiPort.cpp | 7 ++++--- src/gui/widgets/InstrumentMidiIOView.cpp | 4 +--- src/tracks/InstrumentTrack.cpp | 12 +++++++++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/MidiPort.h b/include/MidiPort.h index e9cba39ed2f..acf95992fad 100644 --- a/include/MidiPort.h +++ b/include/MidiPort.h @@ -96,7 +96,10 @@ class MidiPort : public Model, public SerializingObject int realOutputChannel() const { - return outputChannel() - 1; + // There's a possibility of outputChannel being 0 ("--"), which is used to keep all + // midi channels when forwarding. In that case, realOutputChannel will return the + // default channel 1 (whose value is 0). + return outputChannel() ? outputChannel() - 1 : 0; } void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime() ); diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index 52e0a522362..4e97a6713cb 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -47,7 +47,7 @@ MidiPort::MidiPort( const QString& name, m_midiEventProcessor( eventProcessor ), m_mode( mode ), m_inputChannelModel( 0, 0, MidiChannelCount, this, tr( "Input channel" ) ), - m_outputChannelModel( 1, 1, MidiChannelCount, this, tr( "Output channel" ) ), + m_outputChannelModel( 1, 0, MidiChannelCount, this, tr( "Output channel" ) ), m_inputControllerModel( 0, 0, MidiControllerCount, this, tr( "Input controller" ) ), m_outputControllerModel( 0, 0, MidiControllerCount, this, tr( "Output controller" ) ), m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed input velocity" ) ), @@ -151,8 +151,9 @@ void MidiPort::processInEvent( const MidiEvent& event, const MidiTime& time ) void MidiPort::processOutEvent( const MidiEvent& event, const MidiTime& time ) { - // mask event - if( isOutputEnabled() && realOutputChannel() == event.channel() ) + // When output is enabled, route midi events if the selected channel matches + // the event channel or if there's no selected channel (value 0, represented by "--") + if( isOutputEnabled() && ( outputChannel() == 0 || realOutputChannel() == event.channel() ) ) { MidiEvent outEvent = event; diff --git a/src/gui/widgets/InstrumentMidiIOView.cpp b/src/gui/widgets/InstrumentMidiIOView.cpp index 56047d5e677..62d1dbb1828 100644 --- a/src/gui/widgets/InstrumentMidiIOView.cpp +++ b/src/gui/widgets/InstrumentMidiIOView.cpp @@ -84,8 +84,8 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : midiOutputLayout->setSpacing( 6 ); m_outputChannelSpinBox = new LcdSpinBox( 2, m_midiOutputGroupBox ); + m_outputChannelSpinBox->addTextForValue( 0, "--" ); m_outputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); - m_outputChannelSpinBox->setEnabled( false ); midiOutputLayout->addWidget( m_outputChannelSpinBox ); m_fixedOutputVelocitySpinBox = new LcdSpinBox( 3, m_midiOutputGroupBox ); @@ -108,8 +108,6 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : midiOutputLayout->addWidget( m_fixedOutputNoteSpinBox ); midiOutputLayout->addStretch(); - connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), - m_outputChannelSpinBox, SLOT( setEnabled( bool ) ) ); connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), m_fixedOutputVelocitySpinBox, SLOT( setEnabled( bool ) ) ); connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 18dcdf81a99..8ed7d2b85fc 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -393,6 +393,12 @@ void InstrumentTrack::processOutEvent( const MidiEvent& event, const MidiTime& t const MidiEvent transposedEvent = applyMasterKey( event ); const int key = transposedEvent.key(); + // If we have a selected output midi channel between 1-16, we will use that channel to handle the midi event. + // But if our selected midi output channel is 0 ("--"), we will use the event channel instead. + const auto handleEventOutputChannel = midiPort()->outputChannel() == 0 + ? event.channel() + : midiPort()->realOutputChannel(); + switch( event.type() ) { case MidiNoteOn: @@ -403,10 +409,10 @@ void InstrumentTrack::processOutEvent( const MidiEvent& event, const MidiTime& t { if( m_runningMidiNotes[key] > 0 ) { - m_instrument->handleMidiEvent( MidiEvent( MidiNoteOff, midiPort()->realOutputChannel(), key, 0 ), time, offset ); + m_instrument->handleMidiEvent( MidiEvent( MidiNoteOff, handleEventOutputChannel, key, 0 ), time, offset ); } ++m_runningMidiNotes[key]; - m_instrument->handleMidiEvent( MidiEvent( MidiNoteOn, midiPort()->realOutputChannel(), key, event.velocity() ), time, offset ); + m_instrument->handleMidiEvent( MidiEvent( MidiNoteOn, handleEventOutputChannel, key, event.velocity() ), time, offset ); } m_midiNotesMutex.unlock(); @@ -419,7 +425,7 @@ void InstrumentTrack::processOutEvent( const MidiEvent& event, const MidiTime& t if( key >= 0 && key < NumKeys && --m_runningMidiNotes[key] <= 0 ) { - m_instrument->handleMidiEvent( MidiEvent( MidiNoteOff, midiPort()->realOutputChannel(), key, 0 ), time, offset ); + m_instrument->handleMidiEvent( MidiEvent( MidiNoteOff, handleEventOutputChannel, key, 0 ), time, offset ); } m_midiNotesMutex.unlock(); emit endNote(); From a05306131b29ae29af2eac89794f5461f0521567 Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Thu, 4 Jun 2020 08:21:30 +0300 Subject: [PATCH 023/180] Consolidate error messages while loading project (#5269) --- include/Song.h | 2 +- src/core/Song.cpp | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/Song.h b/include/Song.h index d398a168cdc..47367828194 100644 --- a/include/Song.h +++ b/include/Song.h @@ -441,7 +441,7 @@ private slots: SaveOptions m_saveOptions; - QStringList m_errors; + QHash m_errors; PlayModes m_playMode; PlayPos m_playPos[Mode_Count]; diff --git a/src/core/Song.cpp b/src/core/Song.cpp index b12b98573f0..3a7d34c43d6 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1421,21 +1421,34 @@ void Song::clearErrors() void Song::collectError( const QString error ) { - m_errors.append( error ); + if (!m_errors.contains(error)) { m_errors[error] = 1; } + else { m_errors[ error ]++; } } bool Song::hasErrors() { - return ( m_errors.length() > 0 ); + return !(m_errors.isEmpty()); } QString Song::errorSummary() { - QString errors = m_errors.join("\n") + '\n'; + QString errors; + + auto i = m_errors.constBegin(); + while (i != m_errors.constEnd()) + { + errors.append( i.key() ); + if( i.value() > 1 ) + { + errors.append( tr(" (repeated %1 times)").arg( i.value() ) ); + } + errors.append("\n"); + ++i; + } errors.prepend( "\n\n" ); errors.prepend( tr( "The following errors occurred while loading: " ) ); From 8c7e63b35b1f929dff7176406a7a1c69e6262d92 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Tue, 9 Jun 2020 20:45:45 -0400 Subject: [PATCH 024/180] Remove macros likely() and unlikely() (#5530) * Remove macros likely() and unlikely(): https://github.com/LMMS/lmms/pull/5530/#issue-431515330 * Remove only use of macro Q_UNLIKELY(): https://github.com/LMMS/lmms/pull/5530/#issuecomment-641634577 --- include/AutomatableModel.h | 2 +- include/lmms_basics.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 25039eb0dc2..c332858b7b6 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -148,7 +148,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject template inline T value( int frameOffset = 0 ) const { - if( unlikely( hasLinkedModels() || m_controllerConnection != NULL ) ) + if( hasLinkedModels() || m_controllerConnection != NULL ) { return castValue( controllerValue( frameOffset ) ); } diff --git a/include/lmms_basics.h b/include/lmms_basics.h index 9618108563e..77a24649820 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -55,9 +55,6 @@ typedef uint16_t fx_ch_t; // FX-channel (0 to MAX_EFFECT_CHANNEL) typedef uint32_t jo_id_t; // (unique) ID of a journalling object -// use for improved branch prediction -#define likely(x) Q_LIKELY(x) -#define unlikely(x) Q_UNLIKELY(x) // windows headers define "min" and "max" macros, breaking the methods bwloe #undef min From 503006057c22c2f91d3f3fa44522d541bc263b3b Mon Sep 17 00:00:00 2001 From: IanCaio Date: Thu, 11 Jun 2020 01:12:42 -0300 Subject: [PATCH 025/180] Adds a button to clone the BB track pattern Adds a button on the BBEditor that clones the current BB track pattern, but without also cloning the song editor TCOs. That can be useful when an user is editing drumlines and wants to make a section with a slight variation for example. --- .../themes/classic/clone_bb_track_pattern.png | Bin 0 -> 614 bytes .../themes/default/clone_bb_track_pattern.png | Bin 0 -> 208 bytes include/BBEditor.h | 1 + src/gui/editors/BBEditor.cpp | 21 ++++++++++++++++++ 4 files changed, 22 insertions(+) create mode 100644 data/themes/classic/clone_bb_track_pattern.png create mode 100644 data/themes/default/clone_bb_track_pattern.png diff --git a/data/themes/classic/clone_bb_track_pattern.png b/data/themes/classic/clone_bb_track_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4b3669d816be1f834204a21be141ef63f9d3b2 GIT binary patch literal 614 zcmV-s0-61ZP)@uC87km@NO2K2hKTs-v{R$oO5`dcaw}UmjIqn2(P%o7;~U} zaaGRe^UwSJo_4z(F~;7MfJEf-5$vB~$=1&UjWN(#!x-bhRUOp9OF*r)nCD{{7|K z)>_|~*4b79?F=oj(P)%@!#IxJ_gX`1U@dJyah})f6|2<>&1Tct-ZiwBVnYalG)(oLI?;UlG$wb97Rz`L|;lkI}dv)*7AHlW4qmAFc_fKYC#BrBuP%w z>Gb({JcdN{wX)^qa`|qtSiA>-AP8P8&=^yM{hmxFAEcD8MD#7sV0Ev4v)K@Ul5HHv zKgZ+oN2Sz;h`tx={NhHHZ9bwX5&%LX`cYN)7wC@35d~K%rT_o{07*qoM6N<$g8q#O A(EtDd literal 0 HcmV?d00001 diff --git a/data/themes/default/clone_bb_track_pattern.png b/data/themes/default/clone_bb_track_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..ed7d40fa191d288ad3fb13ca97be9166e90373ce GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VOR^eP*AeOHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IXpdw#S7sn8b z-sCA0f)f|0B_t*!BvkzS@U6bc#QW50qeb0@fv=QG4mh1~3u4TTBD)z4*}Q$iB}F@!?5 literal 0 HcmV?d00001 diff --git a/include/BBEditor.h b/include/BBEditor.h index ed54beaf16d..8743bfe9860 100644 --- a/include/BBEditor.h +++ b/include/BBEditor.h @@ -86,6 +86,7 @@ public slots: void removeSteps(); void addSampleTrack(); void addAutomationTrack(); + void cloneBBTrackPattern(); protected slots: void dropEvent(QDropEvent * de ) override; diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index 56fe298561f..1e5c2f9005b 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -29,6 +29,7 @@ #include #include "ComboBox.h" +#include "BBTrack.h" #include "BBTrackContainer.h" #include "embed.h" #include "MainWindow.h" @@ -86,6 +87,8 @@ BBEditor::BBEditor( BBTrackContainer* tc ) : trackAndStepActionsToolBar->addAction(embed::getIconPixmap("add_bb_track"), tr("Add beat/bassline"), Engine::getSong(), SLOT(addBBTrack())); + trackAndStepActionsToolBar->addAction(embed::getIconPixmap("clone_bb_track_pattern"), tr("Clone beat/bassline pattern"), + m_trackContainerView, SLOT(cloneBBTrackPattern())); trackAndStepActionsToolBar->addAction( embed::getIconPixmap("add_sample_track"), tr("Add sample-track"), m_trackContainerView, @@ -311,3 +314,21 @@ void BBTrackContainerView::makeSteps( bool clone ) } } } + +// Creates a clone of the current BB track with the same pattern, but with no TCOs on the song editor +void BBTrackContainerView::cloneBBTrackPattern() +{ + // Get the current BBTrack id + BBTrackContainer *bbtc = static_cast(model()); + const int cur_bb = bbtc->currentBB(); + + BBTrack *bbt = BBTrack::findBBTrack(cur_bb); + + // Clone the track + Track *newTrack = bbt->clone(); + + // Track still have the TCOs which is undesirable in this case, clear the track + newTrack->lock(); + newTrack->deleteTCOs(); + newTrack->unlock(); +} From b1c1d146013ac2b47d3cec0c15ab99236bc97682 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sat, 13 Jun 2020 12:14:47 -0300 Subject: [PATCH 026/180] Changes the clone pattern method name - Changes method name from cloneBBTrackPattern to clonePattern - Small fix on the comments - Adds a TODO comment regarding reusing the code from TrackOperationsWidget as a reference, so we can later figure out a way to not repeat the code --- include/BBEditor.h | 2 +- src/gui/editors/BBEditor.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/BBEditor.h b/include/BBEditor.h index 8743bfe9860..311ed570436 100644 --- a/include/BBEditor.h +++ b/include/BBEditor.h @@ -86,7 +86,7 @@ public slots: void removeSteps(); void addSampleTrack(); void addAutomationTrack(); - void cloneBBTrackPattern(); + void clonePattern(); protected slots: void dropEvent(QDropEvent * de ) override; diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index 1e5c2f9005b..bfc16df5be5 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -88,7 +88,7 @@ BBEditor::BBEditor( BBTrackContainer* tc ) : trackAndStepActionsToolBar->addAction(embed::getIconPixmap("add_bb_track"), tr("Add beat/bassline"), Engine::getSong(), SLOT(addBBTrack())); trackAndStepActionsToolBar->addAction(embed::getIconPixmap("clone_bb_track_pattern"), tr("Clone beat/bassline pattern"), - m_trackContainerView, SLOT(cloneBBTrackPattern())); + m_trackContainerView, SLOT(clonePattern())); trackAndStepActionsToolBar->addAction( embed::getIconPixmap("add_sample_track"), tr("Add sample-track"), m_trackContainerView, @@ -315,8 +315,9 @@ void BBTrackContainerView::makeSteps( bool clone ) } } -// Creates a clone of the current BB track with the same pattern, but with no TCOs on the song editor -void BBTrackContainerView::cloneBBTrackPattern() +// Creates a clone of the current BB track with the same pattern, but no TCOs in the song editor +// TODO: Avoid repeated code from cloneTrack and clearTrack in TrackOperationsWidget somehow +void BBTrackContainerView::clonePattern() { // Get the current BBTrack id BBTrackContainer *bbtc = static_cast(model()); From 82f413568d3e9d5c259757164b57e3376ba3d891 Mon Sep 17 00:00:00 2001 From: Spekular Date: Mon, 15 Jun 2020 09:33:51 +0200 Subject: [PATCH 027/180] Make better use of getSelectedNotes() in PianoRoll.cpp (#5526) * Make better use of getSelectedNotes() in PianoRoll.cpp * Save and reuse selected note vector more often * Apply review suggestions Thanks to @Veratil * Comment, style, consistency --- include/PianoRoll.h | 6 +- src/gui/editors/PianoRoll.cpp | 347 +++++++++++++--------------------- 2 files changed, 132 insertions(+), 221 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 5859594f6dc..ef32f19eccf 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -185,7 +185,7 @@ class PianoRoll : public QWidget const QColor & selCol, const int noteOpc, const bool borderless, bool drawNoteName ); void removeSelection(); void selectAll(); - NoteVector getSelectedNotes(); + NoteVector getSelectedNotes() const; void selectNotesOnKey(); int xCoordOfTick( int tick ); @@ -212,7 +212,7 @@ protected slots: void copySelectedNotes(); void cutSelectedNotes(); void pasteNotes(); - void deleteSelectedNotes(); + bool deleteSelectedNotes(); void updatePosition(const MidiTime & t ); void updatePositionAccompany(const MidiTime & t ); @@ -294,7 +294,9 @@ protected slots: MidiTime newNoteLen() const; void shiftPos(int amount); + void shiftPos(NoteVector notes, int amount); void shiftSemiTone(int amount); + void shiftSemiTone(NoteVector notes, int amount); bool isSelection() const; int selectionCount() const; void testPlayNote( Note * n ); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index d4e29ad4166..5e9ec8af067 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1010,8 +1010,8 @@ void PianoRoll::drawNoteRect( QPainter & p, int x, int y, int const distanceToBorder = 2; int const xOffset = borderWidth + distanceToBorder; - // noteTextHeight, textSize are not suitable for determining vertical spacing, - // capHeight() can be used for this, but requires Qt 5.8. + // noteTextHeight, textSize are not suitable for determining vertical spacing, + // capHeight() can be used for this, but requires Qt 5.8. // We use boundingRect() with QChar (the QString version returns wrong value). QRect const boundingRect = fontMetrics.boundingRect(QChar::fromLatin1('H')); int const yOffset = (noteHeight - boundingRect.top() - boundingRect.bottom()) / 2; @@ -1112,27 +1112,24 @@ void PianoRoll::clearSelectedNotes() - -void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones +void PianoRoll::shiftSemiTone(int amount) //Shift notes by amount semitones { - if (!hasValidPattern()) {return;} + if (!hasValidPattern()) { return; } + + auto selectedNotes = getSelectedNotes(); + //If no notes are selected, shift all of them, otherwise shift selection + if (selectedNotes.empty()) { return shiftSemiTone(m_pattern->notes(), amount); } + else { return shiftSemiTone(selectedNotes, amount); } +} +void PianoRoll::shiftSemiTone(NoteVector notes, int amount) +{ m_pattern->addJournalCheckPoint(); - bool useAllNotes = ! isSelection(); - for( Note *note : m_pattern->notes() ) - { - // if none are selected, move all notes, otherwise - // only move selected notes - if( useAllNotes || note->selected() ) - { - note->setKey( note->key() + amount ); - } - } + for (Note *note : notes) { note->setKey( note->key() + amount ); } m_pattern->rearrangeAllNotes(); m_pattern->dataChanged(); - - // we modified the song + //We modified the song update(); gui->songEditor()->update(); } @@ -1140,38 +1137,29 @@ void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones -void PianoRoll::shiftPos( int amount ) //shift notes pos by amount +void PianoRoll::shiftPos(int amount) //Shift notes pos by amount { - if (!hasValidPattern()) {return;} + if (!hasValidPattern()) { return; } + auto selectedNotes = getSelectedNotes(); + //If no notes are selected, shift all of them, otherwise shift selection + if (selectedNotes.empty()) { return shiftPos(m_pattern->notes(), amount); } + else { return shiftPos(selectedNotes, amount); } +} + +void PianoRoll::shiftPos(NoteVector notes, int amount) +{ m_pattern->addJournalCheckPoint(); - bool useAllNotes = ! isSelection(); + auto leftMostPos = notes.first()->pos(); + //Limit leftwards shifts to prevent moving left of pattern start + auto shiftAmount = (leftMostPos > -amount) ? amount : -leftMostPos; + if (shiftAmount == 0) { return; } - bool first = true; - for( Note *note : m_pattern->notes() ) - { - // if none are selected, move all notes, otherwise - // only move selected notes - if( note->selected() || (useAllNotes && note->length() > 0) ) - { - // don't let notes go to out of bounds - if( first ) - { - m_moveBoundaryLeft = note->pos(); - if( m_moveBoundaryLeft + amount < 0 ) - { - amount += 0 - (amount + m_moveBoundaryLeft); - } - first = false; - } - note->setPos( note->pos() + amount ); - } - } + for (Note *note : notes) { note->setPos( note->pos() + shiftAmount ); } m_pattern->rearrangeAllNotes(); m_pattern->updateLength(); m_pattern->dataChanged(); - // we modified the song update(); gui->songEditor()->update(); @@ -1197,17 +1185,7 @@ bool PianoRoll::isSelection() const // are any notes selected? int PianoRoll::selectionCount() const // how many notes are selected? { - int sum = 0; - - for( const Note *note : m_pattern->notes() ) - { - if( note->selected() ) - { - ++sum; - } - } - - return sum; + return getSelectedNotes().size(); } @@ -1712,57 +1690,34 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_mouseDownKey = m_startKey; m_mouseDownTick = m_currentPosition; - bool first = true; - for( it = notes.begin(); it != notes.end(); ++it ) + //If clicked on an unselected note, remove selection and select that new note + if (!m_currentNote->selected()) { - Note *note = *it; + clearSelectedNotes(); + m_currentNote->setSelected( true ); + } + + auto selectedNotes = getSelectedNotes(); + + m_moveBoundaryLeft = selectedNotes.first()->pos().getTicks(); + m_moveBoundaryRight = selectedNotes.first()->endPos(); + m_moveBoundaryBottom = selectedNotes.first()->key(); + m_moveBoundaryTop = m_moveBoundaryBottom; + //Figure out the bounding box of all the selected notes + for (Note *note: selectedNotes) + { // remember note starting positions note->setOldKey( note->key() ); note->setOldPos( note->pos() ); note->setOldLength( note->length() ); - if( note->selected() ) - { - - // figure out the bounding box of all the selected notes - if( first ) - { - m_moveBoundaryLeft = note->pos().getTicks(); - m_moveBoundaryRight = note->endPos(); - m_moveBoundaryBottom = note->key(); - m_moveBoundaryTop = note->key(); - - first = false; - } - else - { - m_moveBoundaryLeft = qMin( - note->pos().getTicks(), - (tick_t) m_moveBoundaryLeft ); - m_moveBoundaryRight = qMax( (int) note->endPos(), - m_moveBoundaryRight ); - m_moveBoundaryBottom = qMin( note->key(), - m_moveBoundaryBottom ); - m_moveBoundaryTop = qMax( note->key(), - m_moveBoundaryTop ); - } - } + m_moveBoundaryLeft = qMin(note->pos().getTicks(), (tick_t) m_moveBoundaryLeft); + m_moveBoundaryRight = qMax((int) note->endPos(), m_moveBoundaryRight); + m_moveBoundaryBottom = qMin(note->key(), m_moveBoundaryBottom); + m_moveBoundaryTop = qMax(note->key(), m_moveBoundaryTop); } - // if clicked on an unselected note, remove selection - // and select that new note - if( ! m_currentNote->selected() ) - { - clearSelectedNotes(); - m_currentNote->setSelected( true ); - m_moveBoundaryLeft = m_currentNote->pos().getTicks(); - m_moveBoundaryRight = m_currentNote->endPos(); - m_moveBoundaryBottom = m_currentNote->key(); - m_moveBoundaryTop = m_currentNote->key(); - } - - // clicked at the "tail" of the note? if( pos_ticks * m_ppb / MidiTime::ticksPerBar() > m_currentNote->endPos() * m_ppb / MidiTime::ticksPerBar() - RESIZE_AREA_WIDTH @@ -1772,7 +1727,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // then resize the note m_action = ActionResizeNote; - for (Note *note : getSelectedNotes()) + for (Note *note : selectedNotes) { if (note->oldLength() <= 0) { note->setOldLength(4); } } @@ -1796,28 +1751,14 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // if they're holding shift, copy all selected notes if( ! is_new_note && me->modifiers() & Qt::ShiftModifier ) { - // vector to hold new notes until we're through the loop - QVector newNotes; - for( Note* const& note : notes ) + for (Note *note: selectedNotes) { - if( note->selected() ) - { - // copy this note - Note noteCopy( *note ); - newNotes.push_back( noteCopy ); - } - ++it; + Note *newNote = m_pattern->addNote(*note, false); + newNote->setSelected(false); } - if( newNotes.size() != 0 ) + if (!selectedNotes.empty()) { - //put notes from vector into piano roll - for( int i = 0; i < newNotes.size(); ++i) - { - Note * newNote = m_pattern->addNote( newNotes[i], false ); - newNote->setSelected( false ); - } - // added new notes, so must update engine, song, etc Engine::getSong()->setModified(); update(); @@ -2686,36 +2627,33 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) if (m_action == ActionMoveNote) { - for (Note *note : notes) + for (Note *note : getSelectedNotes()) { - if( note->selected() ) + if (shift && ! m_startedWithShift) { - if( shift && ! m_startedWithShift ) + // quick resize, toggled by holding shift after starting a note move, but not before + int ticks_new = note->oldLength().getTicks() + off_ticks; + if( ticks_new <= 0 ) { - // quick resize, toggled by holding shift after starting a note move, but not before - int ticks_new = note->oldLength().getTicks() + off_ticks; - if( ticks_new <= 0 ) - { - ticks_new = 1; - } - note->setLength( MidiTime( ticks_new ) ); - m_lenOfNewNotes = note->length(); - } - else - { - // moving note - int pos_ticks = note->oldPos().getTicks() + off_ticks; - int key_num = note->oldKey() + off_key; - - // ticks can't be negative - pos_ticks = qMax(0, pos_ticks); - // upper/lower bound checks on key_num - key_num = qMax(0, key_num); - key_num = qMin(key_num, NumKeys); - - note->setPos( MidiTime( pos_ticks ) ); - note->setKey( key_num ); + ticks_new = 1; } + note->setLength( MidiTime( ticks_new ) ); + m_lenOfNewNotes = note->length(); + } + else + { + // moving note + int pos_ticks = note->oldPos().getTicks() + off_ticks; + int key_num = note->oldKey() + off_key; + + // ticks can't be negative + pos_ticks = qMax(0, pos_ticks); + // upper/lower bound checks on key_num + key_num = qMax(0, key_num); + key_num = qMin(key_num, NumKeys); + + note->setPos( MidiTime( pos_ticks ) ); + note->setKey( key_num ); } } } @@ -2726,6 +2664,8 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // If shift is pressed we resize and rearrange only the selected notes // If shift + ctrl then we also rearrange all posterior notes (sticky) // If shift is pressed but only one note is selected, apply sticky + + auto selectedNotes = getSelectedNotes(); if (shift) { @@ -2735,20 +2675,20 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // This factor is such that the endpoint of the note whose handle is being dragged should lie under the cursor. // first, determine the start-point of the left-most selected note: int stretchStartTick = -1; - for (const Note *note : notes) + for (const Note *note : selectedNotes) { - if (note->selected() && (stretchStartTick < 0 || note->oldPos().getTicks() < stretchStartTick)) + if (stretchStartTick < 0 || note->oldPos().getTicks() < stretchStartTick) { stretchStartTick = note->oldPos().getTicks(); } } // determine the ending tick of the right-most selected note const Note *posteriorNote = nullptr; - for (const Note *note : notes) + for (const Note *note : selectedNotes) { - if (note->selected() && (posteriorNote == nullptr || + if (posteriorNote == nullptr || note->oldPos().getTicks() + note->oldLength().getTicks() > - posteriorNote->oldPos().getTicks() + posteriorNote->oldLength().getTicks())) + posteriorNote->oldPos().getTicks() + posteriorNote->oldLength().getTicks()) { posteriorNote = note; } @@ -2762,40 +2702,37 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // process all selected notes & determine how much the endpoint of the right-most note was shifted int posteriorDeltaThisFrame = 0; - for (Note *note : notes) + for (Note *note : selectedNotes) { - if(note->selected()) + // scale relative start and end positions by scaleFactor + int newStart = stretchStartTick + scaleFactor * + (note->oldPos().getTicks() - stretchStartTick); + int newEnd = stretchStartTick + scaleFactor * + (note->oldPos().getTicks()+note->oldLength().getTicks() - stretchStartTick); + // if not holding alt, quantize the offsets + if (!alt) { - // scale relative start and end positions by scaleFactor - int newStart = stretchStartTick + scaleFactor * - (note->oldPos().getTicks() - stretchStartTick); - int newEnd = stretchStartTick + scaleFactor * - (note->oldPos().getTicks()+note->oldLength().getTicks() - stretchStartTick); - // if not holding alt, quantize the offsets - if(!alt) - { - // quantize start time - int oldStart = note->oldPos().getTicks(); - int startDiff = newStart - oldStart; - startDiff = floor(startDiff / quantization()) * quantization(); - newStart = oldStart + startDiff; - // quantize end time - int oldEnd = oldStart + note->oldLength().getTicks(); - int endDiff = newEnd - oldEnd; - endDiff = floor(endDiff / quantization()) * quantization(); - newEnd = oldEnd + endDiff; - } - int newLength = qMax(1, newEnd-newStart); - if (note == posteriorNote) - { - posteriorDeltaThisFrame = (newStart+newLength) - - (note->pos().getTicks() + note->length().getTicks()); - } - note->setLength( MidiTime(newLength) ); - note->setPos( MidiTime(newStart) ); - - m_lenOfNewNotes = note->length(); + // quantize start time + int oldStart = note->oldPos().getTicks(); + int startDiff = newStart - oldStart; + startDiff = floor(startDiff / quantization()) * quantization(); + newStart = oldStart + startDiff; + // quantize end time + int oldEnd = oldStart + note->oldLength().getTicks(); + int endDiff = newEnd - oldEnd; + endDiff = floor(endDiff / quantization()) * quantization(); + newEnd = oldEnd + endDiff; } + int newLength = qMax(1, newEnd-newStart); + if (note == posteriorNote) + { + posteriorDeltaThisFrame = (newStart+newLength) - + (note->pos().getTicks() + note->length().getTicks()); + } + note->setLength( MidiTime(newLength) ); + note->setPos( MidiTime(newStart) ); + + m_lenOfNewNotes = note->length(); } if (ctrl || selectionCount() == 1) { @@ -2813,16 +2750,13 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) else { // shift is not pressed; stretch length of selected notes but not their position - for (Note *note : notes) + for (Note *note : selectedNotes) { - if (note->selected()) - { - int newLength = note->oldLength() + off_ticks; - newLength = qMax(1, newLength); - note->setLength( MidiTime(newLength) ); + int newLength = note->oldLength() + off_ticks; + newLength = qMax(1, newLength); + note->setLength( MidiTime(newLength) ); - m_lenOfNewNotes = note->length(); - } + m_lenOfNewNotes = note->length(); } } } @@ -3974,7 +3908,7 @@ void PianoRoll::selectAll() // returns vector with pointers to all selected notes -NoteVector PianoRoll::getSelectedNotes() +NoteVector PianoRoll::getSelectedNotes() const { NoteVector selectedNotes; @@ -4188,47 +4122,22 @@ void PianoRoll::pasteNotes() -void PianoRoll::deleteSelectedNotes() +//Return false if no notes are deleted +bool PianoRoll::deleteSelectedNotes() { - if( ! hasValidPattern() ) - { - return; - } + if (!hasValidPattern()) { return false; } - bool update_after_delete = false; + auto selectedNotes = getSelectedNotes(); + if (selectedNotes.empty()) { return false; } m_pattern->addJournalCheckPoint(); - // get note-vector of current pattern - const NoteVector & notes = m_pattern->notes(); - - // will be our iterator in the following loop - NoteVector::ConstIterator it = notes.begin(); - while( it != notes.end() ) - { - Note *note = *it; - if( note->selected() ) - { - // delete this note - m_pattern->removeNote( note ); - update_after_delete = true; - - // start over, make sure we get all the notes - it = notes.begin(); - } - else - { - ++it; - } - } - - if( update_after_delete ) - { - Engine::getSong()->setModified(); - update(); - gui->songEditor()->update(); - } + for (Note* note: selectedNotes) { m_pattern->removeNote( note ); } + Engine::getSong()->setModified(); + update(); + gui->songEditor()->update(); + return true; } From f56b4963c141b91bc6e78947814f2f0cb92c26a1 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Wed, 17 Jun 2020 23:05:40 +0100 Subject: [PATCH 028/180] Fix some TCOs not saving properly (#5537) --- src/core/Track.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 3f9e04fe641..c377d7b42ee 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -717,7 +717,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) setInitialMousePos( me->pos() ); - if( me->x() < width() - RESIZE_GRIP_WIDTH ) + if( m_tco->getAutoResize() || me->x() < width() - RESIZE_GRIP_WIDTH ) { m_action = Move; setCursor( Qt::SizeAllCursor ); @@ -738,7 +738,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) MidiTime::ticksPerTact() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) ); } - else if( !m_tco->getAutoResize() ) + else { m_action = Resize; setCursor( Qt::SizeHorCursor ); From 5d7e6720e1ba0c154041fd663801f7a7a0e16cb5 Mon Sep 17 00:00:00 2001 From: Luna Nooteboom Date: Sun, 21 Jun 2020 04:17:05 +0200 Subject: [PATCH 029/180] Automatically assign a midi input device to the selected track (#5499) --- include/InstrumentTrack.h | 5 ++++ include/PianoRoll.h | 1 + include/PianoView.h | 1 + include/SetupDialog.h | 1 + src/gui/PianoView.cpp | 7 ++++++ src/gui/SetupDialog.cpp | 25 ++++++++++++++++++ src/gui/editors/PianoRoll.cpp | 8 ++++++ src/tracks/InstrumentTrack.cpp | 46 ++++++++++++++++++++++++++++++++++ 8 files changed, 94 insertions(+) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index b30d5cb21c9..29348b98eb6 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -219,6 +219,8 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor return m_previewMode; } + void autoAssignMidiDevice( bool ); + signals: void instrumentChanged(); void midiNoteOn( const Note& ); @@ -260,6 +262,9 @@ protected slots: bool m_previewMode; + bool m_hasAutoMidiDev; + static InstrumentTrack *s_autoAssignedTrack; + IntModel m_baseNoteModel; NotePlayHandleList m_processHandles; diff --git a/include/PianoRoll.h b/include/PianoRoll.h index ef32f19eccf..bf8aa983c06 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -178,6 +178,7 @@ class PianoRoll : public QWidget void resizeEvent( QResizeEvent * re ) override; void wheelEvent( QWheelEvent * we ) override; void focusOutEvent( QFocusEvent * ) override; + void focusInEvent( QFocusEvent * ) override; int getKey( int y ) const; void drawNoteRect( QPainter & p, int x, int y, diff --git a/include/PianoView.h b/include/PianoView.h index b793ee76813..2f2fea2c935 100644 --- a/include/PianoView.h +++ b/include/PianoView.h @@ -56,6 +56,7 @@ class PianoView : public QWidget, public ModelView void mouseReleaseEvent( QMouseEvent * me ) override; void mouseMoveEvent( QMouseEvent * me ) override; void focusOutEvent( QFocusEvent * _fe ) override; + void focusInEvent( QFocusEvent * fe ) override; void resizeEvent( QResizeEvent * _event ) override; diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 80a22d69c35..fa0ebde6af6 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -175,6 +175,7 @@ private slots: QComboBox * m_midiInterfaces; MswMap m_midiIfaceSetupWidgets; trMap m_midiIfaceNames; + QComboBox * m_assignableMidiDevices; // Paths settings widgets. QString m_workingDir; diff --git a/src/gui/PianoView.cpp b/src/gui/PianoView.cpp index c5f1b623fca..58eb59dafbc 100644 --- a/src/gui/PianoView.cpp +++ b/src/gui/PianoView.cpp @@ -673,10 +673,17 @@ void PianoView::focusOutEvent( QFocusEvent * ) m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, i, 0 ) ); m_piano->setKeyState( i, false ); } + + update(); } +void PianoView::focusInEvent( QFocusEvent * ) +{ + m_piano->instrumentTrack()->autoAssignMidiDevice(true); +} + /*! \brief update scrollbar range after resize diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 6de547b5ae2..2a7aeba7294 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -677,9 +677,32 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : this, SLOT(midiInterfaceChanged(const QString &))); + // MIDI autoassign tab. + TabWidget * midiAutoAssign_tw = new TabWidget( + tr("Automatically assign MIDI controller to selected track"), midi_w); + midiAutoAssign_tw->setFixedHeight(56); + + m_assignableMidiDevices = new QComboBox(midiAutoAssign_tw); + m_assignableMidiDevices->setGeometry(10, 20, 240, 28); + m_assignableMidiDevices->addItem("none"); + if ( !Engine::mixer()->midiClient()->isRaw() ) + { + m_assignableMidiDevices->addItems(Engine::mixer()->midiClient()->readablePorts()); + } + else + { + m_assignableMidiDevices->addItem("all"); + } + int current = m_assignableMidiDevices->findText(ConfigManager::inst()->value("midi", "midiautoassign")); + if (current >= 0) + { + m_assignableMidiDevices->setCurrentIndex(current); + } + // MIDI layout ordering. midi_layout->addWidget(midiiface_tw); midi_layout->addWidget(ms_w); + midi_layout->addWidget(midiAutoAssign_tw); midi_layout->addStretch(); @@ -917,6 +940,8 @@ void SetupDialog::accept() QString::number(m_bufferSize)); ConfigManager::inst()->setValue("mixer", "mididev", m_midiIfaceNames[m_midiInterfaces->currentText()]); + ConfigManager::inst()->setValue("midi", "midiautoassign", + m_assignableMidiDevices->currentText()); ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 5e9ec8af067..e0f4fdadff0 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3600,6 +3600,14 @@ void PianoRoll::focusOutEvent( QFocusEvent * ) update(); } +void PianoRoll::focusInEvent( QFocusEvent * ) +{ + if ( hasValidPattern() ) + { + // Assign midi device + m_pattern->instrumentTrack()->autoAssignMidiDevice(true); + } +} diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 8ed7d2b85fc..855902fe961 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -97,6 +97,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : m_sustainPedalPressed( false ), m_silentBuffersProcessed( false ), m_previewMode( false ), + m_hasAutoMidiDev( false ), m_baseNoteModel( 0, 0, KeysPerOctave * NumOctaves - 1, this, tr( "Base note" ) ), m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr( "Volume" ) ), @@ -149,6 +150,13 @@ int InstrumentTrack::baseNote() const InstrumentTrack::~InstrumentTrack() { + // De-assign midi device + if (m_hasAutoMidiDev) + { + autoAssignMidiDevice(false); + s_autoAssignedTrack = NULL; + } + // kill all running notes and the iph silenceAllNotes( true ); @@ -766,7 +774,13 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement if (Engine::getSong()->isSavingProject() && !Engine::getSong()->getSaveOptions().discardMIDIConnections.value()) { + // Don't save auto assigned midi device connection + bool hasAuto = m_hasAutoMidiDev; + autoAssignMidiDevice(false); + m_midiPort.saveState( doc, thisElement ); + + autoAssignMidiDevice(hasAuto); } m_audioPort.effects()->saveState( doc, thisElement ); @@ -914,6 +928,38 @@ Instrument * InstrumentTrack::loadInstrument(const QString & _plugin_name, +InstrumentTrack *InstrumentTrack::s_autoAssignedTrack = NULL; + +/*! \brief Automatically assign a midi controller to this track, based on the midiautoassign setting + * + * \param assign set to true to connect the midi device, set to false to disconnect + */ +void InstrumentTrack::autoAssignMidiDevice(bool assign) +{ + if (assign) + { + if (s_autoAssignedTrack) + { + s_autoAssignedTrack->autoAssignMidiDevice(false); + } + s_autoAssignedTrack = this; + } + + const QString &device = ConfigManager::inst()->value("midi", "midiautoassign"); + if ( Engine::mixer()->midiClient()->isRaw() && device != "none" ) + { + m_midiPort.setReadable( assign ); + return; + } + + // Check if the device exists + if ( Engine::mixer()->midiClient()->readablePorts().indexOf(device) >= 0 ) + { + m_midiPort.subscribeReadablePort(device, assign); + m_hasAutoMidiDev = assign; + } +} + // #### ITV: From c6f8f7b8e77964e396a5b3425ccebeea2fb4f52e Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 23 Jun 2020 10:41:24 +0900 Subject: [PATCH 030/180] Travis: stop auto-uploading binary releases --- .travis.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 396217af8f4..3d03abf201a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,20 +45,6 @@ script: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.script.sh after_script: - ccache -s -deploy: - provider: releases - api_key: - secure: d4a+x4Gugpss7JK2DcHjyBZDmEFFh4iVfKDfITSD50T6Mc6At4LMgojvEu+6qT6IyOY2vm3UVT6fhyeuWDTRDwW9tfFlaHVA0h8aTRD+eAXOA7pQ8rEMwQO3+WCKuKTfEqUkpL4wxhww8dpkv54tqeIs0S4TBqz9tk8UhzU7XbE= - file_glob: true - file: - - lmms-${TRAVIS_TAG:1}-$TARGET_OS.exe - - /var/cache/pbuilder/result/lmms_*.tar.xz - skip_cleanup: true - on: - tags: true - all_branches: true - condition: '"$TARGET_DEPLOY" = True' - repo: LMMS/lmms notifications: webhooks: urls: From c28dbd1835196f3c9b10560a5744e06cf4bc15c2 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 23 Jun 2020 10:45:39 +0900 Subject: [PATCH 031/180] Update CONTRIBUTORS --- doc/CONTRIBUTORS | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/doc/CONTRIBUTORS b/doc/CONTRIBUTORS index 4a791028fe3..eb030ad7106 100644 --- a/doc/CONTRIBUTORS +++ b/doc/CONTRIBUTORS @@ -9,24 +9,26 @@ Colin Wallace Oskar Wallgren Raine M. Ekman Umcaruje -Michael Gregorius +Hyunjin Song Javier Serrano Polo +Michael Gregorius grejppi Javier Serrano Polo -Hyunjin Song Wong Cho Ching Alexandre Almeida Daniel Winzen LMMS Service Account Steffen Baranowsky Danny McRae +Johannes Lorenz Garrett -Hyunin Song liushuyu +Dominic Clark +Hyunin Song Andrew Kelley +Spekular Andreas Brandmaier Fastigium -Spekular Amadeus Folego Jonas Trappenberg M374LX @@ -35,18 +37,17 @@ grindhold Mike Choi Karmo Rosental Christopher L. Simons -Dominic Clark NoiseByNorthwest -falkTX -Johannes Lorenz Rebecca DeField +falkTX Stian Jørgensrud +David Carlier Ryan Roden-Corrent +Shmuel H midi-pascal Augustin Cavalier BaraMGB Csaba Hruska -David Carlier DeRobyJ Hussam Eddin Alhomsi Rüdiger Ranft @@ -66,11 +67,15 @@ Jonathan Aquilina Mohammad Amin Sameti ra wongcc966422 +Cyp David CARLIER +Douglas <34612565+DouglasDGI@users.noreply.github.com> Gurjot Singh Janne Sinisalo Krzysztof Foltman Lou Herard +Noah Brecht +Olivier Humbert Paul Batchelor Paul Wayper Petter Reinholdtsen @@ -86,9 +91,8 @@ Cyrille Bollu Dan Williams Ian Sannar Jaroslav Petrnoušek -Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> -Johannes Lorenz Kenneth Perry (thothonegan) +Kevin Zander LYF610400210 Lukas W Mark-Agent003 @@ -101,29 +105,37 @@ Rebecca LaVie Roberto Giaconia SecondFlight Steffen Baranowsky +T0NIT0 RMX TheTravelingSpaceman Thomas Clark gnudles liushuyu +makepost miketurn psyomn quadro sarahkeefe +thmueller64 <64359888+thmueller64@users.noreply.github.com> Achim Settelmeier +Alexandra Dutton +Andreas Müller André Hentschel Armin Kazmi +Artur Twardowski <32247490+artur-twardowski@users.noreply.github.com> Attila Herman Bastian Kummer Christopher A. Oliver +Cyp <48363+Cyp@users.noreply.github.com> Devin Venable Diego Ramos Ruggeri -Douglas <34612565+DouglasDGI@users.noreply.github.com> DragonEagle Filip Hron Frank Mather Frederik +Gingka Akiyama <33764485+GingkathFox@users.noreply.github.com> Greg Simpson Hexasoft +Hubert Figuière IvanMaldonado Ivo Wetzel Jens Lang @@ -138,17 +150,17 @@ Lee Avital LocoMatt Léo Andrès Markus Elfring +Martin Pavelek Maurizio Lo Bosco Mehdi Mikobuntu Mingcong Bai Nikos Chantziaras -Noah Brecht Ododo -Olivier Humbert Paul Nasca Peter Nelson Ra +Ron U Ryan Schmidt Shane Ambler Simon Jackson (Netbook) @@ -170,13 +182,15 @@ follower fundamental gandalf3 groboclown +https://gitlab.com/users/CYBERDEViLNL <1148379+CYBERDEViLNL@users.noreply.github.com> irrenhaus3 jasp00 justnope kamnxt +knittl lmmsservice m-xbutterfly -noahb01 +necrashter projectpitchin rgwan xhe From 984fd3a935cb7b85ead204dc81ff32293aa556cf Mon Sep 17 00:00:00 2001 From: Kumar Date: Thu, 2 Jul 2020 19:56:41 +0530 Subject: [PATCH 032/180] Adding a trail (gradient) behind the position bar (#5543) * src/gui/editors/SongEditor.cpp * Gradient can be toggled and color can be changed. * Made playback line (and tail) transparent to mouse clicks * Gradient disappears when paused/stopped; tail length depends on zoom * Fixes bug where gradient appears when a pattern is played; style corrections * Cleaned up code * Rename m_zoomLevels to s_zoomLevels * Finalising code * Make positionLine class independent of parent zooming model * Edit a bug fix to make it more efficient * Rename m_x and finalise positionLine code * Rename m_x and finalise positionLine changes * Rename X to playHeadPos --- data/themes/classic/style.css | 5 ++ data/themes/default/style.css | 5 ++ include/SongEditor.h | 25 ++++++++-- src/gui/editors/SongEditor.cpp | 90 ++++++++++++++++++++++++++++++---- 4 files changed, 112 insertions(+), 13 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index cf609066a81..dceb426dfd8 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -118,6 +118,11 @@ QMenu::indicator:selected { background-color: #747474; } +positionLine { + qproperty-tailGradient: false; + qproperty-lineColor: rgb(255, 255, 255); +} + PianoRoll { background-color: rgb(0, 0, 0); qproperty-backgroundShade: rgba( 255, 255, 255, 10 ); diff --git a/data/themes/default/style.css b/data/themes/default/style.css index feb85a612b0..4c698a9e239 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -150,6 +150,11 @@ QMenu::indicator:selected { background-color: #101213; } +positionLine { + qproperty-tailGradient: true; + qproperty-lineColor: rgb(255, 255, 255); +} + PianoRoll { background-color: #141616; qproperty-backgroundShade: rgba(255, 255, 255, 10); diff --git a/include/SongEditor.h b/include/SongEditor.h index 9621bcc23d8..c4c25d7d0c9 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -28,6 +28,7 @@ #define SONG_EDITOR_H #include +#include #include "ActionGroup.h" #include "Editor.h" @@ -47,13 +48,28 @@ class TimeLineWidget; class positionLine : public QWidget { + Q_OBJECT + Q_PROPERTY ( bool tailGradient READ hasTailGradient WRITE setHasTailGradient ) + Q_PROPERTY ( QColor lineColor READ lineColor WRITE setLineColor ) public: - positionLine( QWidget * parent ); + positionLine ( QWidget* parent ); + + // qproperty access functions + bool hasTailGradient () const; + void setHasTailGradient ( const bool g ); + QColor lineColor () const; + void setLineColor ( const QColor & c ); + +public slots: + void zoomChange (double zoom); private: - void paintEvent( QPaintEvent * pe ) override; + void paintEvent( QPaintEvent* pe ) override; + + bool m_hasTailGradient; + QColor m_lineColor; -} ; +}; class SongEditor : public TrackContainerView @@ -164,6 +180,9 @@ private slots: bool m_selectRegion; friend class SongEditorWindow; + +signals: + void zoomingValueChanged( double ); } ; diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index a8602880565..a7f67c743f8 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -52,26 +52,89 @@ #include "PianoRoll.h" #include "Track.h" -positionLine::positionLine( QWidget * parent ) : - QWidget( parent ) +positionLine::positionLine( QWidget* parent ) : + QWidget( parent ), + m_hasTailGradient ( false ), + m_lineColor (0, 0, 0, 0) { - setFixedWidth( 1 ); + resize( 8, height() ); + setAttribute( Qt::WA_NoSystemBackground, true ); + setAttribute( Qt::WA_TransparentForMouseEvents ); } +void positionLine::paintEvent( QPaintEvent* pe ) +{ + QPainter p( this ); + + // If width is 1, we don't need a gradient + if (width() == 1) + { + p.fillRect( rect(), + QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); + } + + // If width > 1, we need the gradient + else + { + // Create the gradient trail behind the line + QLinearGradient gradient( rect().bottomLeft(), rect().bottomRight() ); + + // If gradient is enabled, we're in focus and we're playing, enable gradient + if (Engine::getSong()->isPlaying() && m_hasTailGradient && + Engine::getSong()->playMode() == Song::Mode_PlaySong) + { + gradient.setColorAt(( ( width() - 1.0 )/width() ), + QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 60) ); + } + else + { + gradient.setColorAt(( ( width() - 1.0 )/width() ), + QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); + } + + // Fill in the remaining parts + gradient.setColorAt(0, + QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); + gradient.setColorAt(1, + QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); + + // Fill line + p.fillRect( rect(), gradient ); + } +} + +// QProperty handles +bool positionLine::hasTailGradient() const +{ return m_hasTailGradient; } + +void positionLine::setHasTailGradient( const bool g ) +{ m_hasTailGradient = g; } +QColor positionLine::lineColor() const +{ return m_lineColor; } +void positionLine::setLineColor( const QColor & c ) +{ m_lineColor = c; } -void positionLine::paintEvent( QPaintEvent * pe ) +// NOTE: the move() implementation fixes a bug where the position line would appear +// in an unexpected location when positioned at the start of the track +void positionLine::zoomChange( double zoom ) { - QPainter p( this ); - p.fillRect( rect(), QColor( 255, 255, 255, 153 ) ); + int playHeadPos = x() + width() - 1; + + resize( 8.0 * zoom, height() ); + move( playHeadPos - width() + 1, y() ); + + update(); } + + + const QVector SongEditor::m_zoomLevels = { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f }; - SongEditor::SongEditor( Song * song ) : TrackContainerView( song ), m_song( song ), @@ -111,6 +174,11 @@ SongEditor::SongEditor( Song * song ) : m_positionLine = new positionLine( this ); static_cast( layout() )->insertWidget( 1, m_timeLine ); + + connect( m_song, SIGNAL( playbackStateChanged() ), + m_positionLine, SLOT( update() ) ); + connect( this, SIGNAL( zoomingValueChanged( double ) ), + m_positionLine, SLOT( zoomChange( double ) ) ); // add some essential widgets to global tool-bar @@ -250,6 +318,8 @@ SongEditor::SongEditor( Song * song ) : m_zoomingModel->findText( "100%" ) ); connect( m_zoomingModel, SIGNAL( dataChanged() ), this, SLOT( zoomingChanged() ) ); + connect( m_zoomingModel, SIGNAL( dataChanged() ), + m_positionLine, SLOT( update() ) ); //Set up snapping model, 2^i for ( int i = 3; i >= -4; i-- ) @@ -808,7 +878,7 @@ void SongEditor::updatePosition( const MidiTime & t ) if( x >= trackOpWidth + widgetWidth -1 ) { m_positionLine->show(); - m_positionLine->move( x, m_timeLine->height() ); + m_positionLine->move( x-( m_positionLine->width() - 1 ), m_timeLine->height() ); } else { @@ -837,11 +907,11 @@ void SongEditor::zoomingChanged() setPixelsPerBar( pixelsPerBar() ); realignTracks(); updateRubberband(); + + emit zoomingValueChanged( m_zoomLevels[m_zoomingModel->value()] ); } - - void SongEditor::selectAllTcos( bool select ) { QVector so = select ? rubberBand()->selectableObjects() : rubberBand()->selectedObjects(); From 24eb2304fdce1e52a551fcd31cb8d8915b29ffc0 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 4 Jul 2020 20:39:04 +0900 Subject: [PATCH 033/180] Add a CMake option for deploying Qt translations --- .travis.yml | 2 +- CMakeLists.txt | 11 +++++++++++ data/locale/CMakeLists.txt | 6 +++--- src/core/main.cpp | 23 +++++++++++------------ 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3d03abf201a..6ab079296eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ before_install: install: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.install.sh before_script: - - export CMAKE_FLAGS="-DWANT_QT5=$QT5 -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo" + - export CMAKE_FLAGS="-DWANT_QT5=$QT5 -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUNDLE_QT_TRANSLATIONS=ON" - if [ -z "$TRAVIS_TAG" ]; then export CMAKE_FLAGS="$CMAKE_FLAGS -DUSE_CCACHE=ON"; fi script: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.script.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index a84018eac6c..9a6765440d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ OPTION(WANT_VST "Include VST support" ON) OPTION(WANT_VST_NOWINE "Include partial VST support (without wine)" OFF) OPTION(WANT_WINMM "Include WinMM MIDI support" OFF) OPTION(WANT_QT5 "Build with Qt5" OFF) +OPTION(BUNDLE_QT_TRANSLATIONS "Install Qt translation files for LMMS" OFF) IF(LMMS_BUILD_APPLE) @@ -92,6 +93,7 @@ IF(LMMS_BUILD_WIN32) SET(WANT_SNDIO OFF) SET(WANT_SOUNDIO OFF) SET(WANT_WINMM ON) + SET(BUNDLE_QT_TRANSLATIONS ON) SET(LMMS_HAVE_WINMM TRUE) SET(STATUS_ALSA "") SET(STATUS_JACK "") @@ -165,6 +167,15 @@ IF(WANT_QT5) # Resolve Qt5::qmake to full path for use in packaging scripts GET_TARGET_PROPERTY(QT_QMAKE_EXECUTABLE "${Qt5Core_QMAKE_EXECUTABLE}" IMPORTED_LOCATION) + execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS + OUTPUT_VARIABLE QT_TRANSLATIONS_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + IF(EXISTS "${QT_TRANSLATIONS_DIR}") + MESSAGE("-- Found Qt translations in ${QT_TRANSLATIONS_DIR}") + ADD_DEFINITIONS(-D'QT_TRANSLATIONS_DIR="${QT_TRANSLATIONS_DIR}"') + ENDIF() FIND_PACKAGE(Qt5Test) SET(QT_QTTEST_LIBRARY Qt5::Test) diff --git a/data/locale/CMakeLists.txt b/data/locale/CMakeLists.txt index 4de6d5ff78c..0eb98f8e37f 100644 --- a/data/locale/CMakeLists.txt +++ b/data/locale/CMakeLists.txt @@ -48,9 +48,9 @@ FOREACH(_item ${qm_targets}) ADD_DEPENDENCIES(finalize-locales "${_item}") ENDFOREACH(_item ${qm_targets}) -IF(LMMS_BUILD_WIN32) - FILE(GLOB QT_QM_FILES "${QT_TRANSLATIONS_DIR}/qt*[^h].qm") +IF(BUNDLE_QT_TRANSLATIONS) + FILE(GLOB QT_QM_FILES "${QT_TRANSLATIONS_DIR}/qt*.qm") LIST(SORT QT_QM_FILES) -ENDIF(LMMS_BUILD_WIN32) +ENDIF() INSTALL(FILES ${QM_FILES} ${QT_QM_FILES} DESTINATION "${LMMS_DATA_DIR}/locale") diff --git a/src/core/main.cpp b/src/core/main.cpp index 1f400e91a0a..f04a02b0875 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -86,9 +86,10 @@ inline void loadTranslation( const QString & tname, QTranslator * t = new QTranslator( QCoreApplication::instance() ); QString name = tname + ".qm"; - t->load( name, dir ); - - QCoreApplication::instance()->installTranslator( t ); + if (t->load(name, dir)) + { + QCoreApplication::instance()->installTranslator(t); + } } @@ -651,18 +652,16 @@ int main( int argc, char * * argv ) pos = QLocale::system().name().left( 2 ); } -#ifdef LMMS_BUILD_WIN32 -#undef QT_TRANSLATIONS_DIR -#define QT_TRANSLATIONS_DIR ConfigManager::inst()->localeDir() -#endif + // load actual translation for LMMS + loadTranslation( pos ); -#ifdef QT_TRANSLATIONS_DIR // load translation for Qt-widgets/-dialogs - loadTranslation( QString( "qt_" ) + pos, - QString( QT_TRANSLATIONS_DIR ) ); +#ifdef QT_TRANSLATIONS_DIR + // load from the original path first + loadTranslation(QString("qt_") + pos, QT_TRANSLATIONS_DIR); #endif - // load actual translation for LMMS - loadTranslation( pos ); + // override it with bundled/custom one, if exists + loadTranslation(QString("qt_") + pos, ConfigManager::inst()->localeDir()); // try to set realtime priority From fde11df89b3fbe04409e0ec14f4f6c47f39d64fd Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 4 Jul 2020 19:18:29 +0900 Subject: [PATCH 034/180] Update PROJECT_YEAR --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a6765440d0..01b627b03d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ INCLUDE(FindPkgConfig) STRING(TOUPPER "${CMAKE_PROJECT_NAME}" PROJECT_NAME_UCASE) -SET(PROJECT_YEAR 2019) +SET(PROJECT_YEAR 2020) SET(PROJECT_AUTHOR "LMMS Developers") SET(PROJECT_URL "https://lmms.io") From 94363be152f526edba4e884264d891f1361cf54b Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 4 Jul 2020 20:58:46 +0900 Subject: [PATCH 035/180] Bump version to 1.2.2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01b627b03d7..b56a8c07b81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ SET(PROJECT_DESCRIPTION "${PROJECT_NAME_UCASE} - Free music production software" SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") SET(VERSION_MINOR "2") -SET(VERSION_RELEASE "1") +SET(VERSION_RELEASE "2") SET(VERSION_STAGE "") SET(VERSION_BUILD "0") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") From 84998a26fdda972587d4559071aca44d2a429418 Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+DouglasDGI@users.noreply.github.com> Date: Sat, 4 Jul 2020 10:48:16 -0600 Subject: [PATCH 036/180] Update LcdSpinBox.cpp (#5555) --- src/gui/widgets/LcdSpinBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/widgets/LcdSpinBox.cpp b/src/gui/widgets/LcdSpinBox.cpp index 6cbdc8103d3..446639090f6 100644 --- a/src/gui/widgets/LcdSpinBox.cpp +++ b/src/gui/widgets/LcdSpinBox.cpp @@ -52,7 +52,7 @@ LcdSpinBox::LcdSpinBox( int numDigits, QWidget* parent, const QString& name ) : LcdSpinBox::LcdSpinBox( int numDigits, const QString& style, QWidget* parent, const QString& name ) : - LcdWidget( numDigits, parent, name ), + LcdWidget( numDigits, style, parent, name ), IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ), m_remainder( 0.f ), m_mouseMoving( false ), From fd04efd394b89829b420fda204861992c0fe556a Mon Sep 17 00:00:00 2001 From: Kumar Date: Mon, 6 Jul 2020 21:07:27 +0530 Subject: [PATCH 037/180] Make Pause hotkey Shift+Space (#5554) --- include/Editor.h | 3 +++ src/gui/editors/Editor.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/include/Editor.h b/include/Editor.h index 1c80e9f2f85..454ec4d3e65 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -58,6 +58,9 @@ protected slots: private slots: /// Called by pressing the space key. Plays or stops. void togglePlayStop(); + + /// Called by pressing shift+space. Toggles pause state. + void togglePause(); signals: diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp index c27eda4c06c..00a659d0779 100644 --- a/src/gui/editors/Editor.cpp +++ b/src/gui/editors/Editor.cpp @@ -74,6 +74,11 @@ void Editor::togglePlayStop() play(); } +void Editor::togglePause() +{ + Engine::getSong()->togglePause(); +} + Editor::Editor(bool record, bool stepRecord) : m_toolBar(new DropToolBar(this)), m_playAction(nullptr), @@ -104,6 +109,7 @@ Editor::Editor(bool record, bool stepRecord) : connect(m_toggleStepRecordingAction, SIGNAL(triggered()), this, SLOT(toggleStepRecording())); connect(m_stopAction, SIGNAL(triggered()), this, SLOT(stop())); new QShortcut(Qt::Key_Space, this, SLOT(togglePlayStop())); + new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Space), this, SLOT(togglePause())); // Add actions to toolbar addButton(m_playAction, "playButton"); From 5aba3d2a73f91d5c6268b258f2c6f167df1a8e62 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Tue, 7 Jul 2020 04:59:34 -0300 Subject: [PATCH 038/180] Fix a bug on the "Remove unused channels" command (#5559) On the FX mixer, the "Remove unused channels" action only checked for InstrumentTracks on every channel but ignored SampleTracks that could be linked to the particular FX channel. Because of that, if there was a channel where only SampleTracks are forwarded to and we clicked on "Remove unused channels", it was going to be removed. This commit fixes it. --- src/gui/FxMixerView.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 54bbff70f9d..782aafcd6ed 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -455,6 +455,15 @@ void FxMixerView::deleteUnusedChannels() break; } } + else if( t->type() == Track::SampleTrack ) + { + SampleTrack *strack = dynamic_cast( t ); + if( i == strack->effectChannelModel()->value(0) ) + { + empty=false; + break; + } + } } FxChannel * ch = Engine::fxMixer()->effectChannel( i ); // delete channel if no references found From afbf80bfcb542d43f91f0ce889111dd24fa56379 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 9 Jul 2020 16:33:54 +0200 Subject: [PATCH 039/180] Refactor deleteUnusedChannels in FxMixerView (#5564) * Refactor deleteUnusedChannels in FxMixerView * Comments + style fix Co-authored-by: Veratil , formatting, suggestions on which lines to comment. Co-authored-by: Kevin Zander * Update weird deleteChannel loop * Use vector instead of array Co-authored-by: Dominic Clark Co-authored-by: Kevin Zander Co-authored-by: Dominic Clark --- src/gui/FxMixerView.cpp | 55 +++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 782aafcd6ed..fae1d5dad7b 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -412,12 +412,9 @@ void FxMixerView::deleteChannel(int index) m_channelAreaWidget->adjustSize(); // make sure every channel knows what index it is - for(int i=0; i index ) - { - m_fxChannelViews[i]->m_fxLine->setChannelIndex(i-1); - } + m_fxChannelViews[i]->m_fxLine->setChannelIndex(i-1); } m_fxChannelViews.remove(index); @@ -439,38 +436,32 @@ void FxMixerView::deleteUnusedChannels() tracks += Engine::getSong()->tracks(); tracks += Engine::getBBTrackContainer()->tracks(); - // go through all FX Channels - for(int i = m_fxChannelViews.size()-1; i > 0; --i) + std::vector inUse(m_fxChannelViews.size(), false); + + //Populate inUse by checking the destination channel for every track + for (Track* t: tracks) { - // check if an instrument references to the current channel - bool empty=true; - for( Track* t : tracks ) + //The channel that this track sends to. Since master channel is always in use, + //setting this to 0 is a safe default (for tracks that don't sent to the mixer). + int channel = 0; + if (t->type() == Track::InstrumentTrack) { - if( t->type() == Track::InstrumentTrack ) - { - InstrumentTrack* inst = dynamic_cast( t ); - if( i == inst->effectChannelModel()->value(0) ) - { - empty=false; - break; - } - } - else if( t->type() == Track::SampleTrack ) - { - SampleTrack *strack = dynamic_cast( t ); - if( i == strack->effectChannelModel()->value(0) ) - { - empty=false; - break; - } - } + InstrumentTrack* inst = dynamic_cast(t); + channel = inst->effectChannelModel()->value(); } - FxChannel * ch = Engine::fxMixer()->effectChannel( i ); - // delete channel if no references found - if( empty && ch->m_receives.isEmpty() ) + else if (t->type() == Track::SampleTrack) { - deleteChannel( i ); + SampleTrack *strack = dynamic_cast(t); + channel = strack->effectChannelModel()->value(); } + inUse[channel] = true; + } + + //Check all channels except master, delete those with no incoming sends + for(int i = m_fxChannelViews.size()-1; i > 0; --i) + { + if (!inUse[i] && Engine::fxMixer()->effectChannel(i)->m_receives.isEmpty()) + { deleteChannel(i); } } } From e07c78d2fcae719f9ae1d97e924dd16206b3905e Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 9 Jul 2020 17:18:57 +0200 Subject: [PATCH 040/180] Better minimum length when resizing notes (#5512) * Limit note length to quantization value Draging a note to it's minimum value of 1 will add this new length to the note if you later choose to stretch it which will not be clearly visible in the Piano Roll unless you zoom in a bit. Limit the note length to the quantization value and use key to override and set a smaller value. * Update src/gui/editors/PianoRoll.cpp Co-authored-by: Spekular * Remember min note length if shorter than quantization() * Find note length modulo quantization, pick smallest from selected notes * Comment on and improve m_minResizeLen calculation Co-authored-by: Oskar Wallgren --- include/PianoRoll.h | 3 +++ src/gui/editors/PianoRoll.cpp | 24 +++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index bf8aa983c06..86a65050c1d 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -406,6 +406,9 @@ protected slots: volume_t m_lastNoteVolume; panning_t m_lastNotePanning; + //When resizing several notes, we want to calculate a common minimum length + MidiTime m_minResizeLen; + int m_startKey; // first key when drawing int m_lastKey; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index e0f4fdadff0..2f948523abc 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -181,6 +181,7 @@ PianoRoll::PianoRoll() : m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), + m_minResizeLen( 0 ), m_startKey( INITIAL_START_KEY ), m_lastKey( 0 ), m_editMode( ModeDraw ), @@ -1727,9 +1728,21 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // then resize the note m_action = ActionResizeNote; + //Calculate the minimum length we should allow when resizing + //each note, and let all notes use the smallest one found + m_minResizeLen = quantization(); for (Note *note : selectedNotes) { - if (note->oldLength() <= 0) { note->setOldLength(4); } + //Notes from the BB editor can have a negative length, so + //change their length to the displayed one before resizing + if (note->oldLength() <= 0) { note->setOldLength(4); } + //Let the note be sized down by quantized increments, stopping + //when the next step down would result in a negative length + int thisMin = note->oldLength() % quantization(); + //The initial value for m_minResizeLen is the minimum length of + //a note divisible by the current Q. Therefore we ignore notes + //where thisMin == 0 when checking for a new minimum + if (thisMin > 0 && thisMin < m_minResizeLen) { m_minResizeLen = thisMin; } } // set resize-cursor @@ -2664,7 +2677,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // If shift is pressed we resize and rearrange only the selected notes // If shift + ctrl then we also rearrange all posterior notes (sticky) // If shift is pressed but only one note is selected, apply sticky - + auto selectedNotes = getSelectedNotes(); if (shift) @@ -2750,11 +2763,12 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) else { // shift is not pressed; stretch length of selected notes but not their position + int minLength = alt ? 1 : m_minResizeLen.getTicks(); + for (Note *note : selectedNotes) { - int newLength = note->oldLength() + off_ticks; - newLength = qMax(1, newLength); - note->setLength( MidiTime(newLength) ); + int newLength = qMax(minLength, note->oldLength() + off_ticks); + note->setLength(MidiTime(newLength)); m_lenOfNewNotes = note->length(); } From 9895472efd9881a876f792e68991244dfff9009f Mon Sep 17 00:00:00 2001 From: Artur Twardowski <32247490+artur-twardowski@users.noreply.github.com> Date: Thu, 9 Jul 2020 22:08:05 +0200 Subject: [PATCH 041/180] Removed the excessive margin in instruments' GUI (#5129) (#5171) * Removed the excessive margin in instruments' GUI (#5129) * Reduced the font size for "master pitch" label. * Reduced the layout spacing for MIDI Input and MIDI Output groups * Shortened labels, so that the LcdSpinBoxes are spaced evenly * Added translator's comments * Whitespace fix --- src/gui/widgets/InstrumentMidiIOView.cpp | 28 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/gui/widgets/InstrumentMidiIOView.cpp b/src/gui/widgets/InstrumentMidiIOView.cpp index 62d1dbb1828..4cc28b15fe9 100644 --- a/src/gui/widgets/InstrumentMidiIOView.cpp +++ b/src/gui/widgets/InstrumentMidiIOView.cpp @@ -53,18 +53,22 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : QHBoxLayout* midiInputLayout = new QHBoxLayout( m_midiInputGroupBox ); midiInputLayout->setContentsMargins( 8, 18, 8, 8 ); - midiInputLayout->setSpacing( 6 ); + midiInputLayout->setSpacing( 4 ); m_inputChannelSpinBox = new LcdSpinBox( 2, m_midiInputGroupBox ); m_inputChannelSpinBox->addTextForValue( 0, "--" ); - m_inputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); + /*: This string must be be short, its width must be less than + * width of LCD spin-box of two digits */ + m_inputChannelSpinBox->setLabel( tr( "CHAN" ) ); m_inputChannelSpinBox->setEnabled( false ); midiInputLayout->addWidget( m_inputChannelSpinBox ); m_fixedInputVelocitySpinBox = new LcdSpinBox( 3, m_midiInputGroupBox ); m_fixedInputVelocitySpinBox->setDisplayOffset( 1 ); m_fixedInputVelocitySpinBox->addTextForValue( 0, "---" ); - m_fixedInputVelocitySpinBox->setLabel( tr( "VELOCITY" ) ); + /*: This string must be be short, its width must be less than + * width of LCD spin-box of three digits */ + m_fixedInputVelocitySpinBox->setLabel( tr( "VELOC" ) ); m_fixedInputVelocitySpinBox->setEnabled( false ); midiInputLayout->addWidget( m_fixedInputVelocitySpinBox ); midiInputLayout->addStretch(); @@ -81,28 +85,37 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : QHBoxLayout* midiOutputLayout = new QHBoxLayout( m_midiOutputGroupBox ); midiOutputLayout->setContentsMargins( 8, 18, 8, 8 ); - midiOutputLayout->setSpacing( 6 ); + midiOutputLayout->setSpacing( 4 ); m_outputChannelSpinBox = new LcdSpinBox( 2, m_midiOutputGroupBox ); m_outputChannelSpinBox->addTextForValue( 0, "--" ); - m_outputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); + /*: This string must be be short, its width must be less than + * width of LCD spin-box of two digits */ + m_outputChannelSpinBox->setLabel( tr( "CHAN" ) ); + m_outputChannelSpinBox->setEnabled( false ); midiOutputLayout->addWidget( m_outputChannelSpinBox ); m_fixedOutputVelocitySpinBox = new LcdSpinBox( 3, m_midiOutputGroupBox ); m_fixedOutputVelocitySpinBox->setDisplayOffset( 1 ); m_fixedOutputVelocitySpinBox->addTextForValue( 0, "---" ); - m_fixedOutputVelocitySpinBox->setLabel( tr( "VELOCITY" ) ); + /*: This string must be be short, its width must be less than + * width of LCD spin-box of three digits */ + m_fixedOutputVelocitySpinBox->setLabel( tr( "VELOC" ) ); m_fixedOutputVelocitySpinBox->setEnabled( false ); midiOutputLayout->addWidget( m_fixedOutputVelocitySpinBox ); m_outputProgramSpinBox = new LcdSpinBox( 3, m_midiOutputGroupBox ); - m_outputProgramSpinBox->setLabel( tr( "PROGRAM" ) ); + /*: This string must be be short, its width must be less than the + * width of LCD spin-box of three digits */ + m_outputProgramSpinBox->setLabel( tr( "PROG" ) ); m_outputProgramSpinBox->setEnabled( false ); midiOutputLayout->addWidget( m_outputProgramSpinBox ); m_fixedOutputNoteSpinBox = new LcdSpinBox( 3, m_midiOutputGroupBox ); m_fixedOutputNoteSpinBox->setDisplayOffset( 1 ); m_fixedOutputNoteSpinBox->addTextForValue( 0, "---" ); + /*: This string must be be short, its width must be less than + * width of LCD spin-box of three digits */ m_fixedOutputNoteSpinBox->setLabel( tr( "NOTE" ) ); m_fixedOutputNoteSpinBox->setEnabled( false ); midiOutputLayout->addWidget( m_fixedOutputNoteSpinBox ); @@ -211,6 +224,7 @@ InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) : QHBoxLayout* masterPitchLayout = new QHBoxLayout( m_pitchGroupBox ); masterPitchLayout->setContentsMargins( 8, 18, 8, 8 ); QLabel *tlabel = new QLabel(tr( "Enables the use of master pitch" ) ); + tlabel->setFont( pointSize<8>( tlabel->font() ) ); m_pitchGroupBox->setModel( &it->m_useMasterPitchModel ); masterPitchLayout->addWidget( tlabel ); layout->addStretch(); From 4b4f117addd2861c60801f7cec9645048a86bde1 Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Fri, 10 Jul 2020 03:58:52 +0200 Subject: [PATCH 042/180] Fix garbage in exported audio caused by resampling (#5552) This makes AudioDevice::resample return the actual number of generated samples. --- include/AudioDevice.h | 2 +- src/core/audio/AudioDevice.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/AudioDevice.h b/include/AudioDevice.h index 35f5ec6207f..c9ad7f4b3cd 100644 --- a/include/AudioDevice.h +++ b/include/AudioDevice.h @@ -115,7 +115,7 @@ class AudioDevice const fpp_t _frames ); // resample given buffer from samplerate _src_sr to samplerate _dst_sr - void resample( const surroundSampleFrame * _src, + fpp_t resample( const surroundSampleFrame * _src, const fpp_t _frames, surroundSampleFrame * _dst, const sample_rate_t _src_sr, diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index ab69e144b12..f794602299b 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -93,10 +93,8 @@ fpp_t AudioDevice::getNextBuffer( surroundSampleFrame * _ab ) // resample if necessary if( mixer()->processingSampleRate() != m_sampleRate ) { - resample( b, frames, _ab, mixer()->processingSampleRate(), - m_sampleRate ); - frames = frames * m_sampleRate / - mixer()->processingSampleRate(); + frames = resample( b, frames, _ab, mixer()->processingSampleRate(), + m_sampleRate ); } else { @@ -184,7 +182,7 @@ void AudioDevice::renamePort( AudioPort * ) -void AudioDevice::resample( const surroundSampleFrame * _src, +fpp_t AudioDevice::resample( const surroundSampleFrame * _src, const fpp_t _frames, surroundSampleFrame * _dst, const sample_rate_t _src_sr, @@ -192,7 +190,7 @@ void AudioDevice::resample( const surroundSampleFrame * _src, { if( m_srcState == NULL ) { - return; + return _frames; } m_srcData.input_frames = _frames; m_srcData.output_frames = _frames; @@ -206,6 +204,7 @@ void AudioDevice::resample( const surroundSampleFrame * _src, printf( "AudioDevice::resample(): error while resampling: %s\n", src_strerror( error ) ); } + return static_cast(m_srcData.output_frames_gen); } From 2da0aaa4606998abd6537a052e0c8b69ee586185 Mon Sep 17 00:00:00 2001 From: Kumar Date: Sat, 11 Jul 2020 15:00:27 +0530 Subject: [PATCH 043/180] Enable LMMS fullscreen and... (long title, read first line of description) (#5563) * Enable fullscreen with hotkey & hotkey to toggle maximise in internal window * Fix an obvious blunder * Add fullscreen menu entry * Change Alt+F11 to Shift+F11 (fix Windows bug) * Move F5-F10 to Ctrl+F*, fullscreen by F11 and fix Linux bug * Remove wrongly placed "fullscreen" attribute * Remove temporary fixes for redundant bug * Rename maximise to maximize * Rename maximise to maximize * Use fullscreen icon instead of maximise icon * Actually include the icon in the commit * Replace .svg icon with .png 16x16 icon * Migrate editor hotkeys to Ctrl+1-7 --- data/themes/default/fullscreen.png | Bin 0 -> 568 bytes include/Editor.h | 2 + include/MainWindow.h | 3 ++ src/gui/MainWindow.cpp | 66 ++++++++++++++++++++--------- src/gui/editors/Editor.cpp | 6 +++ 5 files changed, 56 insertions(+), 21 deletions(-) create mode 100755 data/themes/default/fullscreen.png diff --git a/data/themes/default/fullscreen.png b/data/themes/default/fullscreen.png new file mode 100755 index 0000000000000000000000000000000000000000..56f552e104a355891b734c933c5af6b2f642d0e6 GIT binary patch literal 568 zcmV-80>}M{P)EX>4Tx04R}tkv&MmKpe$iQ%n7*IM_kNAwzW#3!);9QpF-zC~bvS9ZW9$5Slb3 zDK3tJYr(;f#j1mgv#t)Vf*|+-;^gS0=prTlFDbN$@!+^0@9sVB-T^|r%v7^u98fjO zNX27fHn$=MUlBwP0vJV?#7sSrUdX_6eBHyx*Sjds@;>+H=vDG21AHR!EYl5(c%689 z)6zNb6NgzzQi#uq#|*k4@gvt|m)|%S9Ts?I*vO>jh{MEUv5n<6W+g)QjhY-gi5=cUXj4C!zhJ`4t8Yw1Hv>)^E4?6xNxny!} zfRSS!6{wILKlmT~?$#_!PPj>-7!Y`|?T=v~xC=CDw*7r<+l>>z{|sDdEq|pB%zTnw zYiW@qpl=(vxNd3k9&ot>3_R(QAvuztrcfvV?`QN)IiUX*=w5YuYwqLp0mx8SOE@|;fceeNT@0n(QKSMNflx>pHFaQ7m24YJ`L;(K){{a7>y{D4^000SaNLh0L z01ejw01ejxLMWSf00007bV*G`2jm9}1u8ibg)Z&@001;eL_t(I%VYfi|NnmmEP#=b zkrA1XOC1vfg@6%eGP;d~08AgnE*N+NM!{%0Kna5Z%Z&i;4Il+TJ77fs0000setShortcut( Qt::Key_F5 ); + song_editor_window->setShortcut( Qt::CTRL + Qt::Key_1 ); ToolButton * bb_editor_window = new ToolButton( embed::getIconPixmap( "bb_track_btn" ), tr( "Beat+Bassline Editor" ) + - " (F6)", + " (Ctrl+2)", this, SLOT( toggleBBEditorWin() ), m_toolBar ); - bb_editor_window->setShortcut( Qt::Key_F6 ); + bb_editor_window->setShortcut( Qt::CTRL + Qt::Key_2 ); ToolButton * piano_roll_window = new ToolButton( embed::getIconPixmap( "piano" ), tr( "Piano Roll" ) + - " (F7)", + " (Ctrl+3)", this, SLOT( togglePianoRollWin() ), m_toolBar ); - piano_roll_window->setShortcut( Qt::Key_F7 ); + piano_roll_window->setShortcut( Qt::CTRL + Qt::Key_3 ); ToolButton * automation_editor_window = new ToolButton( embed::getIconPixmap( "automation" ), tr( "Automation Editor" ) + - " (F8)", + " (Ctrl+4)", this, SLOT( toggleAutomationEditorWin() ), m_toolBar ); - automation_editor_window->setShortcut( Qt::Key_F8 ); + automation_editor_window->setShortcut( Qt::CTRL + Qt::Key_4 ); ToolButton * fx_mixer_window = new ToolButton( embed::getIconPixmap( "fx_mixer" ), - tr( "FX Mixer" ) + " (F9)", + tr( "FX Mixer" ) + " (Ctrl+5)", this, SLOT( toggleFxMixerWin() ), m_toolBar ); - fx_mixer_window->setShortcut( Qt::Key_F9 ); + fx_mixer_window->setShortcut( Qt::CTRL + Qt::Key_5 ); ToolButton * controllers_window = new ToolButton( embed::getIconPixmap( "controller" ), tr( "Show/hide controller rack" ) + - " (F10)", + " (Ctrl+6)", this, SLOT( toggleControllerRack() ), m_toolBar ); - controllers_window->setShortcut( Qt::Key_F10 ); + controllers_window->setShortcut( Qt::CTRL + Qt::Key_6 ); ToolButton * project_notes_window = new ToolButton( embed::getIconPixmap( "project_notes" ), tr( "Show/hide project notes" ) + - " (F11)", + " (Ctrl+7)", this, SLOT( toggleProjectNotesWin() ), m_toolBar ); - project_notes_window->setShortcut( Qt::Key_F11 ); + project_notes_window->setShortcut( Qt::CTRL + Qt::Key_7 ); m_toolBarLayout->addWidget( song_editor_window, 1, 1 ); m_toolBarLayout->addWidget( bb_editor_window, 1, 2 ); @@ -1007,6 +1010,20 @@ void MainWindow::toggleWindow( QWidget *window, bool forceShow ) +void MainWindow::toggleFullscreen() +{ + if ( !isFullScreen() ) + { + maximized = isMaximized(); + showFullScreen(); + } + else + { + maximized ? showMaximized() : showNormal(); + } +} + + /* * When an editor window with focus is toggled off, attempt to set focus @@ -1093,36 +1110,43 @@ void MainWindow::updateViewMenu() // Not that it's straight visible <-> invisible, more like // not on top -> top <-> invisible m_viewMenu->addAction(embed::getIconPixmap( "songeditor" ), - tr( "Song Editor" ) + " (F5)", + tr( "Song Editor" ) + "\tCtrl+1", this, SLOT( toggleSongEditorWin() ) ); m_viewMenu->addAction(embed::getIconPixmap( "bb_track" ), - tr( "Beat+Bassline Editor" ) + " (F6)", + tr( "Beat+Bassline Editor" ) + "\tCtrl+2", this, SLOT( toggleBBEditorWin() ) ); m_viewMenu->addAction(embed::getIconPixmap( "piano" ), - tr( "Piano Roll" ) + " (F7)", + tr( "Piano Roll" ) + "\tCtrl+3", this, SLOT( togglePianoRollWin() ) ); m_viewMenu->addAction(embed::getIconPixmap( "automation" ), - tr( "Automation Editor" ) + " (F8)", + tr( "Automation Editor" ) + "\tCtrl+4", this, SLOT( toggleAutomationEditorWin()) ); m_viewMenu->addAction(embed::getIconPixmap( "fx_mixer" ), - tr( "FX Mixer" ) + " (F9)", + tr( "FX Mixer" ) + "\tCtrl+5", this, SLOT( toggleFxMixerWin() ) ); m_viewMenu->addAction(embed::getIconPixmap( "controller" ), - tr( "Controller Rack" ) + " (F10)", + tr( "Controller Rack" ) + "\tCtrl+6", this, SLOT( toggleControllerRack() ) ); m_viewMenu->addAction(embed::getIconPixmap( "project_notes" ), - tr( "Project Notes" ) + " (F11)", + tr( "Project Notes" ) + "\tCtrl+7", this, SLOT( toggleProjectNotesWin() ) ); m_viewMenu->addSeparator(); + + m_viewMenu->addAction(embed::getIconPixmap( "fullscreen" ), + tr( "Fullscreen" ) + "\tF11", + this, SLOT( toggleFullscreen() ) + ); + + m_viewMenu->addSeparator(); // Here we should put all look&feel -stuff from configmanager // that is safe to change on the fly. There is probably some diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp index 00a659d0779..dc86a1789dc 100644 --- a/src/gui/editors/Editor.cpp +++ b/src/gui/editors/Editor.cpp @@ -79,6 +79,11 @@ void Editor::togglePause() Engine::getSong()->togglePause(); } +void Editor::toggleMaximize() +{ + isMaximized() ? showNormal() : showMaximized(); +} + Editor::Editor(bool record, bool stepRecord) : m_toolBar(new DropToolBar(this)), m_playAction(nullptr), @@ -110,6 +115,7 @@ Editor::Editor(bool record, bool stepRecord) : connect(m_stopAction, SIGNAL(triggered()), this, SLOT(stop())); new QShortcut(Qt::Key_Space, this, SLOT(togglePlayStop())); new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Space), this, SLOT(togglePause())); + new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F11), this, SLOT(toggleMaximize())); // Add actions to toolbar addButton(m_playAction, "playButton"); From d0ef87543d490aa318dd5880188446025e741914 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Sat, 11 Jul 2020 18:40:12 -0400 Subject: [PATCH 044/180] Remove warning color from oscilloscope (#5492) --- data/themes/classic/style.css | 1 - data/themes/default/style.css | 1 - include/Oscilloscope.h | 5 ----- src/gui/widgets/Oscilloscope.cpp | 17 +---------------- 4 files changed, 1 insertion(+), 23 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index dceb426dfd8..6ae93b49856 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -168,7 +168,6 @@ Oscilloscope { background: none; border: none; qproperty-normalColor: rgb(71, 253, 133); - qproperty-warningColor: rgb(255, 192, 64); qproperty-clippingColor: rgb(255, 64, 64); } diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 4c698a9e239..fc6495921a9 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -200,7 +200,6 @@ Oscilloscope { background: none; border: none; qproperty-normalColor: rgb(71, 253, 133); - qproperty-warningColor: rgb(255, 192, 64); qproperty-clippingColor: rgb(255, 64, 64); } diff --git a/include/Oscilloscope.h b/include/Oscilloscope.h index 49db1393b3e..45e04832a02 100644 --- a/include/Oscilloscope.h +++ b/include/Oscilloscope.h @@ -37,7 +37,6 @@ class Oscilloscope : public QWidget Q_OBJECT public: Q_PROPERTY( QColor normalColor READ normalColor WRITE setNormalColor ) - Q_PROPERTY( QColor warningColor READ warningColor WRITE setWarningColor ) Q_PROPERTY( QColor clippingColor READ clippingColor WRITE setClippingColor ) Oscilloscope( QWidget * _parent ); @@ -48,9 +47,6 @@ class Oscilloscope : public QWidget QColor const & normalColor() const; void setNormalColor(QColor const & normalColor); - QColor const & warningColor() const; - void setWarningColor(QColor const & warningColor); - QColor const & clippingColor() const; void setClippingColor(QColor const & clippingColor); @@ -74,7 +70,6 @@ protected slots: bool m_active; QColor m_normalColor; - QColor m_warningColor; QColor m_clippingColor; } ; diff --git a/src/gui/widgets/Oscilloscope.cpp b/src/gui/widgets/Oscilloscope.cpp index cd808e39a5a..81432bcd4d5 100644 --- a/src/gui/widgets/Oscilloscope.cpp +++ b/src/gui/widgets/Oscilloscope.cpp @@ -44,7 +44,6 @@ Oscilloscope::Oscilloscope( QWidget * _p ) : m_points( new QPointF[Engine::mixer()->framesPerPeriod()] ), m_active( false ), m_normalColor(71, 253, 133), - m_warningColor(255, 192, 64), m_clippingColor(255, 64, 64) { setFixedSize( m_background.width(), m_background.height() ); @@ -121,16 +120,6 @@ void Oscilloscope::setNormalColor(QColor const & normalColor) m_normalColor = normalColor; } -QColor const & Oscilloscope::warningColor() const -{ - return m_warningColor; -} - -void Oscilloscope::setWarningColor(QColor const & warningColor) -{ - m_warningColor = warningColor; -} - QColor const & Oscilloscope::clippingColor() const { return m_clippingColor; @@ -205,14 +194,10 @@ void Oscilloscope::mousePressEvent( QMouseEvent * _me ) QColor const & Oscilloscope::determineLineColor(float level) const { - if( level < 0.9f ) + if( level <= 1.0f ) { return normalColor(); } - else if( level <= 1.0f ) - { - return warningColor(); - } else { return clippingColor(); From 3795fdff646c1dff5ac03067e02b752165e8642c Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sun, 12 Jul 2020 10:37:39 -0300 Subject: [PATCH 045/180] Small refactor on FxMixerView.cpp and FxMixer.cpp The code on FxMixerView.cpp and FxMixer.cpp were using the types TrackContainer::TrackList and QVector unconsistently. TrackContainer::TrackList is just a typedef for QVector so it makes sense that we use it, specially in terms of readability. Places where QVector were used are now replaced with TrackContainer::TrackList. Also, we were not including "TrackContainer.h" directly (the typedef was likely being included indirectly through one of the other include files), so we also include this header on both source codes. --- src/core/FxMixer.cpp | 9 +++++---- src/gui/FxMixerView.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index f04435e0523..4a68c3f3b05 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -34,6 +34,7 @@ #include "InstrumentTrack.h" #include "SampleTrack.h" #include "BBTrackContainer.h" +#include "TrackContainer.h" // For TrackContainer::TrackList typedef FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) : m_from( from ), @@ -383,13 +384,13 @@ void FxMixer::moveChannelLeft( int index ) else if (m_lastSoloed == b) { m_lastSoloed = a; } // go through every instrument and adjust for the channel index change - QVector songTrackList = Engine::getSong()->tracks(); - QVector bbTrackList = Engine::getBBTrackContainer()->tracks(); + TrackContainer::TrackList songTrackList = Engine::getSong()->tracks(); + TrackContainer::TrackList bbTrackList = Engine::getBBTrackContainer()->tracks(); - QVector trackLists[] = {songTrackList, bbTrackList}; + TrackContainer::TrackList trackLists[] = {songTrackList, bbTrackList}; for(int tl=0; tl<2; ++tl) { - QVector trackList = trackLists[tl]; + TrackContainer::TrackList trackList = trackLists[tl]; for(int i=0; itype() == Track::InstrumentTrack ) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index fae1d5dad7b..257e16c958c 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -50,6 +50,7 @@ #include "SampleTrack.h" #include "Song.h" #include "BBTrackContainer.h" +#include "TrackContainer.h" // For TrackContainer::TrackList typedef FxMixerView::FxMixerView() : QWidget(), @@ -237,13 +238,13 @@ void FxMixerView::refreshDisplay() // update the and max. channel number for every instrument void FxMixerView::updateMaxChannelSelector() { - QVector songTrackList = Engine::getSong()->tracks(); - QVector bbTrackList = Engine::getBBTrackContainer()->tracks(); + TrackContainer::TrackList songTrackList = Engine::getSong()->tracks(); + TrackContainer::TrackList bbTrackList = Engine::getBBTrackContainer()->tracks(); - QVector trackLists[] = {songTrackList, bbTrackList}; + TrackContainer::TrackList trackLists[] = {songTrackList, bbTrackList}; for(int tl=0; tl<2; ++tl) { - QVector trackList = trackLists[tl]; + TrackContainer::TrackList trackList = trackLists[tl]; for(int i=0; itype() == Track::InstrumentTrack ) From 159861a9a06323c7303e2c526dbf35f0fe60df75 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 13 Jul 2020 22:20:34 -0300 Subject: [PATCH 046/180] Fixes small conflict on a new commit On the PR #5470 (Allows intruments to keep the midi channel information when forwarding), merged on Jun 1st 2020 commit 97680e0, there's a line removed on the src/gui/widgets/InstrumentMidiIOView.cpp file ( m_outputChannelSpinBox->setEnabled( false ); ), because since the output channel is now relevant even when MIDI forwarding is disabled, we need that spinbox always enabled. It was also disconnected from the LedButton to keep it from disabling/enabling it. On the PR #5171 (Removed the excessive margin in instruments' GUI (#5129)), merged on Jul 9th 2020 commit 9895472, the line was reintroduced, possibly because it was an older PR that wasn't rebased to the latest changes. This broke the output channel spinbox because now it was disabled on the constructor, but it was still disconnected from the LedButton, as a result always disabled. This hotfix removes the line again to fix the issue. --- src/gui/widgets/InstrumentMidiIOView.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/widgets/InstrumentMidiIOView.cpp b/src/gui/widgets/InstrumentMidiIOView.cpp index 4cc28b15fe9..db8ae709bd1 100644 --- a/src/gui/widgets/InstrumentMidiIOView.cpp +++ b/src/gui/widgets/InstrumentMidiIOView.cpp @@ -92,7 +92,6 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : /*: This string must be be short, its width must be less than * width of LCD spin-box of two digits */ m_outputChannelSpinBox->setLabel( tr( "CHAN" ) ); - m_outputChannelSpinBox->setEnabled( false ); midiOutputLayout->addWidget( m_outputChannelSpinBox ); m_fixedOutputVelocitySpinBox = new LcdSpinBox( 3, m_midiOutputGroupBox ); From 920ff35745ea616d76440eadc333610c845d08b2 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 13 Jul 2020 23:16:04 -0300 Subject: [PATCH 047/180] Fix bug from issue 5562 (#5565) Fixes a small bug where projects that are saved with a soloed track can't restore the muted state of other tracks, because it doesn't store the m_mutedBeforeSolo variable on the project file. With this fix, a new attribute is added to the Track DOM element, containing the muted state of tracks before the solo. When loading older projects that don't contain this attribute m_mutedBeforeSolo will be set to false. --- src/core/Track.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 8f7b62bcd2c..0133169ed5e 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include "AutomationPattern.h" @@ -2250,6 +2251,8 @@ void Track::saveSettings( QDomDocument & doc, QDomElement & element ) element.setAttribute( "name", name() ); m_mutedModel.saveSettings( doc, element, "muted" ); m_soloModel.saveSettings( doc, element, "solo" ); + // Save the mutedBeforeSolo value so we can recover the muted state if any solo was active (issue 5562) + element.setAttribute( "mutedBeforeSolo", int(m_mutedBeforeSolo) ); if( m_height >= MINIMAL_TRACK_HEIGHT ) { @@ -2304,6 +2307,9 @@ void Track::loadSettings( const QDomElement & element ) m_mutedModel.loadSettings( element, "muted" ); m_soloModel.loadSettings( element, "solo" ); + // Get the mutedBeforeSolo value so we can recover the muted state if any solo was active. + // Older project files that didn't have this attribute will set the value to false (issue 5562) + m_mutedBeforeSolo = QVariant( element.attribute( "mutedBeforeSolo", "0" ) ).toBool(); if( m_simpleSerializingMode ) { From 5a7ec92cf0082b87b3f2366099b6985a17886cc6 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 20 Jul 2020 11:38:46 +0900 Subject: [PATCH 048/180] Fix crash on CLI rendering (#5579) This is a temporary workaround. To make all export options available in CLI, some properties of TimeLineWidget should be moved to a core class. --- src/core/Song.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 3a7d34c43d6..5f63e6ee859 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -767,12 +767,16 @@ void Song::startExport() m_exportSongEnd += MidiTime(1,0); m_exportSongBegin = MidiTime(0,0); - m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? - m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : MidiTime(0,0); - m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : MidiTime(0,0); + // FIXME: remove this check once we load timeline in headless mode + if (m_playPos[Mode_PlaySong].m_timeLine) + { + m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? + m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : MidiTime(0,0); + m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : MidiTime(0,0); + } m_playPos[Mode_PlaySong].setTicks( 0 ); } From 37d85ef24a467ff86b7c3d7efc7ee554b31d779e Mon Sep 17 00:00:00 2001 From: LAMCILAK Theo Date: Mon, 20 Jul 2020 04:39:53 +0200 Subject: [PATCH 049/180] Implement portable mode (#5561) Adds portable mode by creating a file named portable_mode.txt next to lmms and fixed a typo in the name of the function --- include/ConfigManager.h | 10 ++- src/core/ConfigManager.cpp | 130 ++++++++++++++++++++++--------------- src/gui/SetupDialog.cpp | 2 +- 3 files changed, 85 insertions(+), 57 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 556c455a0c7..de22d22af11 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -50,7 +50,7 @@ const QString LADSPA_PATH ="plugins/ladspa/"; const QString DEFAULT_THEME_PATH = "themes/default/"; const QString TRACK_ICON_PATH = "track_icons/"; const QString LOCALE_PATH = "locale/"; - +const QString PORTABLE_MODE_FILE = "/portable_mode.txt"; class LMMS_EXPORT ConfigManager : public QObject { @@ -71,6 +71,12 @@ class LMMS_EXPORT ConfigManager : public QObject return m_workingDir; } + void initPortableWorkingDir(); + + void initInstalledWorkingDir(); + + void initDevelopmentWorkingDir(); + const QString & dataDir() const { return m_dataDir; @@ -216,7 +222,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString defaultVersion() const; - static QStringList availabeVstEmbedMethods(); + static QStringList availableVstEmbedMethods(); QString vstEmbedMethod() const; // Returns true if the working dir (e.g. ~/lmms) exists on disk. diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index b8e8cd4ae77..53262dac7df 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -51,60 +51,26 @@ static inline QString ensureTrailingSlash(const QString & s ) ConfigManager * ConfigManager::s_instanceOfMe = NULL; -ConfigManager::ConfigManager() : - m_workingDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/lmms/"), - m_dataDir("data:/"), - m_vstDir(m_workingDir + "vst/"), - m_sf2Dir(m_workingDir + SF2_PATH), - m_gigDir(m_workingDir + GIG_PATH), - m_themeDir(defaultThemeDir()), - m_lmmsRcFile(QDir::home().absolutePath() +"/.lmmsrc.xml"), - m_version(defaultVersion()) +ConfigManager::ConfigManager() : m_version(defaultVersion()) { - // Detect < 1.2.0 working directory as a courtesy - if ( QFileInfo( QDir::home().absolutePath() + "/lmms/projects/" ).exists() ) - m_workingDir = QDir::home().absolutePath() + "/lmms/"; - - if (! qgetenv("LMMS_DATA_DIR").isEmpty()) - QDir::addSearchPath("data", QString::fromLocal8Bit(qgetenv("LMMS_DATA_DIR"))); - - // If we're in development (lmms is not installed) let's get the source and - // binary directories by reading the CMake Cache - QDir appPath = qApp->applicationDirPath(); - // If in tests, get parent directory - if (appPath.dirName() == "tests") { - appPath.cdUp(); + if (QFileInfo::exists(qApp->applicationDirPath() + PORTABLE_MODE_FILE)) + { + initPortableWorkingDir(); } - QFile cmakeCache(appPath.absoluteFilePath("CMakeCache.txt")); - if (cmakeCache.exists()) { - cmakeCache.open(QFile::ReadOnly); - QTextStream stream(&cmakeCache); - - // Find the lines containing something like lmms_SOURCE_DIR:static= - // and lmms_BINARY_DIR:static= - int done = 0; - while(! stream.atEnd()) - { - QString line = stream.readLine(); - - if (line.startsWith("lmms_SOURCE_DIR:")) { - QString srcDir = line.section('=', -1).trimmed(); - QDir::addSearchPath("data", srcDir + "/data/"); - done++; - } - if (line.startsWith("lmms_BINARY_DIR:")) { - m_lmmsRcFile = line.section('=', -1).trimmed() + QDir::separator() + - ".lmmsrc.xml"; - done++; - } - if (done == 2) - { - break; - } - } - - cmakeCache.close(); + else + { + initInstalledWorkingDir(); } + m_dataDir = "data:/"; + m_vstDir = m_workingDir + "vst/"; + m_sf2Dir = m_workingDir + SF2_PATH; + m_gigDir = m_workingDir + GIG_PATH; + m_themeDir = defaultThemeDir(); + if (!qgetenv("LMMS_DATA_DIR").isEmpty()) + { + QDir::addSearchPath("data", QString::fromLocal8Bit(qgetenv("LMMS_DATA_DIR"))); + } + initDevelopmentWorkingDir(); #ifdef LMMS_BUILD_WIN32 QDir::addSearchPath("data", qApp->applicationDirPath() + "/data/"); @@ -112,7 +78,6 @@ ConfigManager::ConfigManager() : QDir::addSearchPath("data", qApp->applicationDirPath().section('/', 0, -2) + "/share/lmms/"); #endif - } @@ -193,7 +158,7 @@ QString ConfigManager::defaultVersion() const return LMMS_VERSION; } -QStringList ConfigManager::availabeVstEmbedMethods() +QStringList ConfigManager::availableVstEmbedMethods() { QStringList methods; methods.append("none"); @@ -215,7 +180,7 @@ QStringList ConfigManager::availabeVstEmbedMethods() QString ConfigManager::vstEmbedMethod() const { - QStringList methods = availabeVstEmbedMethods(); + QStringList methods = availableVstEmbedMethods(); QString defaultMethod = *(methods.end() - 1); QString currentMethod = value( "ui", "vstembedmethod", defaultMethod ); return methods.contains(currentMethod) ? currentMethod : defaultMethod; @@ -651,3 +616,60 @@ void ConfigManager::saveConfigFile() outfile.write(xml.toUtf8()); outfile.close(); } + +void ConfigManager::initPortableWorkingDir() +{ + QString applicationPath = qApp->applicationDirPath(); + m_workingDir = applicationPath + "/lmms-workspace/"; + m_lmmsRcFile = applicationPath + "/.lmmsrc.xml"; +} + +void ConfigManager::initInstalledWorkingDir() +{ + m_workingDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/lmms/"; + m_lmmsRcFile = QDir::home().absolutePath() +"/.lmmsrc.xml"; + // Detect < 1.2.0 working directory as a courtesy + if ( QFileInfo( QDir::home().absolutePath() + "/lmms/projects/" ).exists() ) + m_workingDir = QDir::home().absolutePath() + "/lmms/"; +} + +void ConfigManager::initDevelopmentWorkingDir() +{ + // If we're in development (lmms is not installed) let's get the source and + // binary directories by reading the CMake Cache + QDir appPath = qApp->applicationDirPath(); + // If in tests, get parent directory + if (appPath.dirName() == "tests") { + appPath.cdUp(); + } + QFile cmakeCache(appPath.absoluteFilePath("CMakeCache.txt")); + if (cmakeCache.exists()) { + cmakeCache.open(QFile::ReadOnly); + QTextStream stream(&cmakeCache); + + // Find the lines containing something like lmms_SOURCE_DIR:static= + // and lmms_BINARY_DIR:static= + int done = 0; + while(! stream.atEnd()) + { + QString line = stream.readLine(); + + if (line.startsWith("lmms_SOURCE_DIR:")) { + QString srcDir = line.section('=', -1).trimmed(); + QDir::addSearchPath("data", srcDir + "/data/"); + done++; + } + if (line.startsWith("lmms_BINARY_DIR:")) { + m_lmmsRcFile = line.section('=', -1).trimmed() + QDir::separator() + + ".lmmsrc.xml"; + done++; + } + if (done == 2) + { + break; + } + } + + cmakeCache.close(); + } +} diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 2a7aeba7294..b1d7e5aadb4 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -389,7 +389,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : m_vstEmbedComboBox = new QComboBox(plugins_tw); m_vstEmbedComboBox->move(XDelta, YDelta * ++counter); - QStringList embedMethods = ConfigManager::availabeVstEmbedMethods(); + QStringList embedMethods = ConfigManager::availableVstEmbedMethods(); m_vstEmbedComboBox->addItem(tr("No embedding"), "none"); if(embedMethods.contains("qt")) { From a11fa71b9419f419312dec957eed45acb72364e5 Mon Sep 17 00:00:00 2001 From: Kumar Date: Mon, 20 Jul 2020 22:08:45 +0530 Subject: [PATCH 050/180] Remove Xpressive help maximise button (#5570) (#5586) * Remove Xpressive maximise button * Fix grammar errors --- plugins/Xpressive/Xpressive.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index a80a0ae4152..6e648edd481 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -818,7 +818,7 @@ void XpressiveView::usrWaveClicked() { } QString XpressiveHelpView::s_helpText= -"O1, O2 - Two output waves. Panning is controled by PN1 and PN2.
" +"O1, O2 - Two output waves. Panning is controlled by PN1 and PN2.
" "W1, W2, W3 - Wave samples evaluated by expression. In these samples, t variable ranges [0,1).
" "These waves can be used as functions inside the output waves (O1, O2). The wave period is 1.
" "

Available variables:


" @@ -829,10 +829,10 @@ QString XpressiveHelpView::s_helpText= "srate - Sample rate. In wave expression it returns the wave's number of samples.
" "tempo - Song's Tempo. Available only in the output expressions.
" "v - Note's volume. Note that the output is already multiplied by the volume. Available only in the output expressions.
" -"rel - Gives 0.0 while the key is holded, and 1.0 after the key release. Available only in the output expressions.
" -"trel - Time after release. While the note is holded, it gives 0.0. Afterwards, it start counting seconds.
" +"rel - Gives 0.0 while the key is held, and 1.0 after the key release. Available only in the output expressions.
" +"trel - Time after release. While the note is held, it gives 0.0. Afterwards, it starts counting seconds.
" "The time it takes to shift from 0.0 to 1.0 after key release is determined by the REL knob
" -"seed - A random value that remains consistent in the lifetime of a single wave. meant to be used with randsv
" +"seed - A random value that remains consistent in the lifetime of a single wave. Meant to be used with randsv
" "A1, A2, A3 - General purpose knobs. You can reference them only in O1 and O2. In range [-1,1].
" "

Available functions:


" "W1, W2, W3 - As mentioned before. You can reference them only in O1 and O2.
" @@ -877,6 +877,11 @@ XpressiveHelpView::XpressiveHelpView():QTextEdit(s_helpText) parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) ); parentWidget()->setFixedSize( 300, 500); + + // No maximize button + Qt::WindowFlags flags = parentWidget()->windowFlags(); + flags &= ~Qt::WindowMaximizeButtonHint; + parentWidget()->setWindowFlags( flags ); } void XpressiveView::helpClicked() { From 23242b9529b759ae1f6d51c756ca9ea1ddaee2d4 Mon Sep 17 00:00:00 2001 From: Claudius Ellsel Date: Tue, 21 Jul 2020 18:35:36 +0200 Subject: [PATCH 051/180] Fix the outer border for the instrument track (#5594) --- src/tracks/InstrumentTrack.cpp | 2 +- src/tracks/Pattern.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 855902fe961..222c604dfb9 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -22,6 +22,7 @@ * Boston, MA 02110-1301 USA. * */ +#include "InstrumentTrack.h" #include #include @@ -37,7 +38,6 @@ #include #include "FileDialog.h" -#include "InstrumentTrack.h" #include "AutomationPattern.h" #include "BBTrack.h" #include "CaptionMenu.h" diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 125e84a1f57..40f11b3cb30 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -1123,7 +1123,7 @@ void PatternView::paintEvent( QPaintEvent * ) // outer border p.setPen( current ? c.lighter( 130 ) : c.darker( 300 ) ); - p.drawRect( rect() ); + p.drawRect( 0, 0, rect().right(), rect().bottom() ); } // draw the 'muted' pixmap only if the pattern was manually muted From 04d8c0db068691070f20776d14ff0d56720e7a26 Mon Sep 17 00:00:00 2001 From: Cyp <48363+Cyp@users.noreply.github.com> Date: Fri, 24 Jul 2020 03:33:15 +0200 Subject: [PATCH 052/180] =?UTF-8?q?Add=20missing=20=C2=B9=E2=81=84?= =?UTF-8?q?=E2=82=89=E2=82=86=20quantization=20(#5304)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Editor.h | 6 ++++++ src/gui/editors/AutomationEditor.cpp | 28 +++------------------------- src/gui/editors/PianoRoll.cpp | 13 +++---------- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/include/Editor.h b/include/Editor.h index 04765ee071c..d755608a6c9 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -28,6 +28,12 @@ #include #include +static const int Quantizations[] = { + 1, 2, 4, 8, 16, 32, 64, + 3, 6, 12, 24, 48, 96, 192 +}; + + class QAction; class DropToolBar; diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 265977ad5af..8c281bf3e1f 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -124,16 +124,9 @@ AutomationEditor::AutomationEditor() : connect( m_tensionModel, SIGNAL( dataChanged() ), this, SLOT( setTension() ) ); - for( int i = 0; i < 7; ++i ) - { - m_quantizeModel.addItem( "1/" + QString::number( 1 << i ) ); - } - for( int i = 0; i < 5; ++i ) - { - m_quantizeModel.addItem( "1/" + - QString::number( ( 1 << i ) * 3 ) ); + for (auto q : Quantizations) { + m_quantizeModel.addItem(QString("1/%1").arg(q)); } - m_quantizeModel.addItem( "1/192" ); connect( &m_quantizeModel, SIGNAL(dataChanged() ), this, SLOT( setQuantization() ) ); @@ -2160,22 +2153,7 @@ void AutomationEditor::zoomingYChanged() void AutomationEditor::setQuantization() { - int quantization = m_quantizeModel.value(); - if( quantization < 7 ) - { - quantization = 1 << quantization; - } - else if( quantization < 12 ) - { - quantization = 1 << ( quantization - 7 ); - quantization *= 3; - } - else - { - quantization = DefaultTicksPerBar; - } - quantization = DefaultTicksPerBar / quantization; - AutomationPattern::setQuantization( quantization ); + AutomationPattern::setQuantization(DefaultTicksPerBar / Quantizations[m_quantizeModel.value()]); update(); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 2f948523abc..b519fb9e5e1 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -371,15 +371,9 @@ PianoRoll::PianoRoll() : // Set up quantization model m_quantizeModel.addItem( tr( "Note lock" ) ); - for( int i = 0; i <= NUM_EVEN_LENGTHS; ++i ) - { - m_quantizeModel.addItem( "1/" + QString::number( 1 << i ) ); + for (auto q : Quantizations) { + m_quantizeModel.addItem(QString("1/%1").arg(q)); } - for( int i = 0; i < NUM_TRIPLET_LENGTHS; ++i ) - { - m_quantizeModel.addItem( "1/" + QString::number( (1 << i) * 3 ) ); - } - m_quantizeModel.addItem( "1/192" ); m_quantizeModel.setValue( m_quantizeModel.findText( "1/16" ) ); connect( &m_quantizeModel, SIGNAL( dataChanged() ), @@ -4280,8 +4274,7 @@ int PianoRoll::quantization() const } } - QString text = m_quantizeModel.currentText(); - return DefaultTicksPerBar / text.right( text.length() - 2 ).toInt(); + return DefaultTicksPerBar / Quantizations[m_quantizeModel.value() - 1]; } From dc3e8cab45d4e164595750c06223f2c3fe12e941 Mon Sep 17 00:00:00 2001 From: Stefano Bonicatti Date: Fri, 24 Jul 2020 03:33:46 +0200 Subject: [PATCH 053/180] Avoid leaking the track grip QPixmap at each paintEvent (#5590) --- include/Track.h | 2 -- src/core/Track.cpp | 14 ++------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/include/Track.h b/include/Track.h index 89c95059cf9..ff0e3ef88bf 100644 --- a/include/Track.h +++ b/include/Track.h @@ -459,8 +459,6 @@ private slots: void clearTrack(); private: - static QPixmap * s_grip; - TrackView * m_trackView; QPushButton * m_trackOps; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 0133169ed5e..734cb4e12d9 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1830,10 +1830,6 @@ void TrackContentWidget::setEmbossColor( const QBrush & c ) // trackOperationsWidget // =========================================================================== - -QPixmap * TrackOperationsWidget::s_grip = NULL; /*!< grip pixmap */ - - /*! \brief Create a new trackOperationsWidget * * The trackOperationsWidget is the grip and the mute button of a track. @@ -1960,17 +1956,11 @@ void TrackOperationsWidget::paintEvent( QPaintEvent * pe ) if( m_trackView->isMovingTrack() == false ) { - s_grip = new QPixmap( embed::getIconPixmap( - "track_op_grip" ) ); - - p.drawPixmap( 2, 2, *s_grip ); + p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip")); } else { - s_grip = new QPixmap( embed::getIconPixmap( - "track_op_grip_c" ) ); - - p.drawPixmap( 2, 2, *s_grip ); + p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip_c")); } } From 86306042f5bc757280798af36410656d8fc30f7a Mon Sep 17 00:00:00 2001 From: Kumar Date: Fri, 24 Jul 2020 12:33:25 +0530 Subject: [PATCH 054/180] Set default behaviour of playhead to << instead of |<< (#5591) * Set default behaviour, correct spelling * Set default behaviour, correct spelling * Store stop behaviour in project * Change how state is saved & loaded * Change to use enum --- include/TimeLineWidget.h | 1 + src/gui/TimeLineWidget.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index bc0881f82ee..5b33bd98987 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -243,6 +243,7 @@ public slots: void positionChanged( const MidiTime & _t ); void loopPointStateLoaded( int _n ); void positionMarkerMoved(); + void loadBehaviourAtStop( int _n ); } ; diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index bd196de7f6c..8e79410b853 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -130,7 +130,7 @@ void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) NStateButton * behaviourAtStop = new NStateButton( _tool_bar ); behaviourAtStop->addState( embed::getIconPixmap( "back_to_zero" ), - tr( "After stopping go back to begin" ) + tr( "After stopping go back to beginning" ) ); behaviourAtStop->addState( embed::getIconPixmap( "back_to_start" ), tr( "After stopping go back to " @@ -140,6 +140,9 @@ void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) tr( "After stopping keep position" ) ); connect( behaviourAtStop, SIGNAL( changedState( int ) ), this, SLOT( toggleBehaviourAtStop( int ) ) ); + connect( this, SIGNAL( loadBehaviourAtStop( int ) ), behaviourAtStop, + SLOT( changeState( int ) ) ); + behaviourAtStop->changeState( BackToStart ); _tool_bar->addWidget( autoScroll ); _tool_bar->addWidget( loopPoints ); @@ -154,6 +157,7 @@ void TimeLineWidget::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "lp0pos", (int) loopBegin() ); _this.setAttribute( "lp1pos", (int) loopEnd() ); _this.setAttribute( "lpstate", m_loopPoints ); + _this.setAttribute( "stopbehaviour", m_behaviourAtStop ); } @@ -167,6 +171,11 @@ void TimeLineWidget::loadSettings( const QDomElement & _this ) _this.attribute( "lpstate" ).toInt() ); update(); emit loopPointStateLoaded( m_loopPoints ); + + if( _this.hasAttribute( "stopbehaviour" ) ) + { + emit loadBehaviourAtStop( _this.attribute( "stopbehaviour" ).toInt() ); + } } From 67f0324ff52547f4eaa5499eefade662d137d147 Mon Sep 17 00:00:00 2001 From: Rebecca DeField Date: Sun, 26 Jul 2020 11:14:18 -0700 Subject: [PATCH 055/180] Minor icon updates (#5588) * Delete clear_ghost_note.png * Delete ghost_note.png * Delete loop_points_off.png * Delete loop_points_on.png * Updated and recreated icons * Delete trackop.png * New track gear icon --- data/themes/default/clear_ghost_note.png | Bin 1088 -> 399 bytes data/themes/default/ghost_note.png | Bin 3915 -> 375 bytes data/themes/default/loop_points_off.png | Bin 424 -> 302 bytes data/themes/default/loop_points_on.png | Bin 424 -> 266 bytes data/themes/default/record_step_off.png | Bin 443 -> 375 bytes data/themes/default/record_step_on.png | Bin 596 -> 377 bytes data/themes/default/trackop.png | Bin 403 -> 541 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/themes/default/clear_ghost_note.png b/data/themes/default/clear_ghost_note.png index c9f85a2b4ab54a5b206823b56116d87a6936d8d7..b9565269f1a7655343e63ccaf5cee11a617ad051 100644 GIT binary patch delta 337 zcmV-X0j~bQ2#*7hBoYa5NLh0L01m?d01m?e$8V@)kwzzf0USw0K~y-6?UX@o0x=9l zJshEc=rIa6sCodp;S8v|L7asfbTjk-Gv{bmRBG2C=#olocx)WxM$I_0>ZV^BO>8GG zwv|LAA)p650?)uFaMbTZeN&*9TAzYWfH!=-0^?lh1Mr;z4LSn%9`qLY>HrNo03*wa zd$bghn=WmCQ}uOiGUIp6poubS5@Ty`+Q28E*6{*30}HilCw6KB2bF4%^R` z5&&wuu^ezH@y?X)#yoK7W2xDM(rYJ{m2u5aH90ZMDq?VQ*9;X!apiS;L^|jn{CD6d z5!pq)wTQe#zJrL&-CguJRyv-Vv$_XHzyR19|4ipJuDuJg$2{0TWdYHKmGKRLZ5wzN j*+XFU%e)mZY+HK)yP%w83S@f>00000NkvXXu0mjfut|~z delta 1031 zcmV+i1o-=p1HcH7Bq9WJLP=Bz2nYy#2xN!=000SaNLh0L01o{C01o{DxZ2gkkv1oP z1HDN^K~y-6m6T0PR96(ofA=xanc>3$EDcV9eqd5WEs3QhATg7ec0n;HX;{@=1r}{+ zL!Gjz57bm5h1jGiNt+Ud5H`>zf+kvH7h)rADUueNG8qs=s1}@z<1o(0J6$l7j8nn( zzqz^Zob!9g5?TWptT`BdIQp&58>HvD<`uI>Ol>?*!f2_J( zbeEJ62?ha5fhGXcr;zywKFTXEUj?$F#qNYj%|%y3Db)qgs#IoHVd2WE%LTx*MMZz# zo1PZHsYACGgc`tBR9ib82hv4<#0(%JHsHPJb4^6*+F&0rvFdVFsH7zJ9Xm$-#Kbu8 zIbZ{7Oaj%wT7G%?oj7nl;1!W&AQv#j0y7sb^ogb=VHj#lK>>&Q`pDnA7eIi1;5hJ> z2J8fWFLF4l~@?NPkt8Nen|3*VWN5F%g5VA9H<#OTbQlQ?#RTHj#I6 zk)Fy*JhQW;q^I*nM+bY39N8%SRtzi~5=EHo>*L$QhXL4DT1styKUsx^oAm)8rfBO0 zz-)XZ=<_jf-~h8%uc9=Kilawyw6&onCqE6k4Y*?heg_h0wPhvR8L^mS-|9x$XSI1kw7M@Q`viEyd8nXk*r2>JcIa_}IHH*fwYXjC0=xu3E41h! zI!Jd(iQJo-O7xk|ry0?3LxW2PX$5EeY|I_GxpTTwGBr53oKWL`6On8Y@oWVBZD2rj zFm$+~CwlY3_3Iy>%FKMws%gPrdwL|!K3xDJ@_uwbCn75%B1R z*TKBKs_NXr=;+&80gnfzX&y0*ao{$flGD;&4*7hq0;wj8B@kI#(*@4x5O^Xup?mdU zY3c0uPUpkP-rgKw2k?9}e4F)ee;D{q2hWoj{{SdBmq2C$LWuwX002ovPDHLkV1ntX B;I;q& diff --git a/data/themes/default/ghost_note.png b/data/themes/default/ghost_note.png index 073442659782a12383b536f4f1984f7ae892f867..9871fcf0a7e89a7b3760d15547075aff006b601a 100644 GIT binary patch delta 349 zcmV-j0iyoP9`^!}BYyx1a7bBm000id000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~ zcP?peYja~^aAhuUa%Y?FJQ@H10R%}zK~y-6?UOxj!axv(XE{OuITEJf2H^%#4nP{8 z0a52Hz5$J)Md2Pvl}JfVgei^W>PxehhL!inJx^N6`*!D>Cx2^4#3Nt?yaHR`7ijGI zvEB+8`RaYpCGbgHAHY*8^aZ#kKsz(P-x>kel;B@dtME)FaCn9^tt3_lT?P3S0vfnE;RA(RqCcsJgg}u8A4p#?I v`$_|xhL!Rrz$pYSoc$Cid+wFMG*tT!n1>9O-jf)E00000NkvXXu0mjfs1%bz literal 3915 zcmV-R547-!P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=QFawI#lME|jhSprl*EC9zkhG@FMeVv#YD*^)s!qhvBv5fUy8kcuA{T@{Js83_cizN zym`CeITbjD>)*8Q`y1!w{ec|s^Zny_lgB+yxd*y0JO&Ipv**iwU%3Yg{yOjO|4qH0 zYw>A2kKb_?ud`o2pZ<>-Fs3kG@W!v;g5_`0Spq8vB{Yup{0({&pT>FNZ`AuB|QaN57E#hrQUnQyql%+qnFcMRr^9Y1}{tCRnq z&)YzIkAtu+Z&S)u?FmeBxW;V0TI@@v!Dy_I=rIlA%b+rxGcGz*tPCM_i>uzsWyRG`sYVMQe zes48*TTLw%&wcsbYMjpbF@#e&5w#f>^U-1PtQLUKUTtQJi_xp))MmDNoI*TSbE3wX zPA!H7<8ofMd$ZlU<$h~7Bk@Oe^IuucsOkP2mNRO)+j76!?S<85J3$~E1F07(rZ#MZ zjT=^JzBG+VzaIN6VYq+?Ac|OETXwdvB2U`tR4mt&Wl~-&WQ3K{oE@{|xpuHAhn~X> zeG(JPo=(9ZLN^=9r={p`8+o^u*=72$|->M}CKjP{^> zpgJ&rhQk~@+}9u~)>a1?jJpWt0R_6&IS%+a`Iqb{%d zl9vP1ZreQ2t}>-5X%}*^UCEdl$H5xQ0yXqxzSZD-M4wHrz(4#Xyn_1zf4r-e4MRY5 zyUNyb2&3T4aYI;J!Y(ss7e&)7xsOIZo%r4{AC9}c2JR?~EC!FpV~fB@v_gEYu6swU z!S)?6)Wv)*gDejS`?m-JH>5E1k~E2CN#N7T3zr1zqWW?N81AJ zCC!Aiif_)b^PDxwSu&8R*mlh7A`8gyhsdp{p2VIfQ5D@>1E~w_j4^8SzUhvL=tKeJ zc9ad>4{;Ji-qnC^Q`A^Yyd0Ao5Rjjm(tO9(RaTp-Qs4*MddRaibi2VlBq{goypa(L z@o8$ZGuDto5K@z~J}kRK*`(}=)Cb|URa!X^t@NbhX25#va?G;*=mNO`nk zBCrLE+>*+aYo7|#FTN&=b@d3F59f|im32<722yXV`whd*gW~^`;pQ>f4;Y>+gAw-6 z+G1#Dl?D>Cff|wB?JcA}gE|0(7k25@bj5qznn*ax2m9j$`$R@_?f4FjB6nyclZ3in zBZ8XQU+8T+b*;YXZ1HmZc(2pEFF2?fugwuZ4kC;^4+!afTv&XK>ih+_Z<+|QBAgSC zjAy0Dc8P1_4pE~lNpL2WVCbAkZCjC!QG>=~giAwHS=3)-0?w=o%P+g+EAvsh5>fV( zcdHp-YwutmP>|3^%nhcuo@&r6rkL(xU65uv*+QII$R)+i^V%KOekE)xHa=DK=1vGYl#m9ske>wp*$!1 z)3%Fdf5l{3>kyFKb-`KEH~39TN91c%Qv+5q-k8Mv#+J!iU}mq3v;4`Q!)olu9NZoF zjZ3(qgZUWyEPGyZ*8rNF_O}jW@E8(lE(MLwP;eD5YL3gh`z81|r_)?@25KL7asbqh z@|Axg*>w_QXCGxc=o`Do{XO-iJ!y+N4FTY@Fx@VBJG(mNaY^&RC-FY#SM4G7upFUf zM;s=RBIQnIKt4$JUr)oLl0FvovMb+khA=iYjC~KKjDNuhlz)XrU4`EbmO7?2B-NG4 zt~}FKnQ^p6?yr&BIat-F`^H4wXn>cKL}B$n(%56s91^~OT(ezkQ4-vUVx$@ga538v zi7^RrwiLz+T$f}12W2&A-;})x%e6R(uw|+{)QYa(S-GQ{C$Qy2oQlZ@@*|`nF2_89 z4QhEHpIr7^x?uhk)4r<<=4)a2nJyUnr`f)FG(W}SD=5rQvG@uK^HVIov$dZdKR{uA z9xq-)VSXMjUPEDiip4*{=&y_Xb&Z5$XZ;Asrb>f?(e0# z6c~Y&MZBgg)sj)Vbu#lM*9@A5PB$M}3pI0Rc~`kkGKl#rRd*uglS=8`$+lB%OG+d= zBaf#l-{kPCkyy^QM#ZB$#sSfXI~EUnR>sT@n@u-b$JmT}1P$z4+%YmW(NV zg|#0I!)-bVaDfhQ9mCjjWl<&F9mA$Chx{Y;mc_^)c)eh3D9)tPRY&ay+R>~_HX|8>GN%va?ec^rHD@+&7 zWU+ss;r;wq?g4^-HrIN5R)d0zVhF*HYAq$bf8M%4|M zS3Kf5Z2(WINvf9iTnEB~-o_xc77B*ZVs^S(-JwqGxIveSdjQH@^J9d=d{frs|}a$ef+ zW6&)_#ov{?FcHbxqJajDa?7>rEX4pTnT>ye3EhNyey>T*xBVJp*G`?ad^VIlmZDbU zMwsI_z4V0#dg{uF=GDz+rFIHl%G)*lYUmM;7pIMda$T50+uq5YW?E$Vvi*3qm8YFp zrKvfMo|4}hJ#C&w&rEIQ)%498qSEv|LP58**`BM6u9Z+3QYn?Hey;}Z;Fi!Ze`r*` zJR)^YyS%JT7kRi%6$wkiHuyCB+qf0Jmsu{ffe)j3zvf6I?X^-4$e2u_2YU=aU zv1#vFP8-n>F17jobOpiJYg|xY|NZ0dPn&=C`1{l5*T;c%_M0UsC`;CSBZ=YwGu}xu z_@gW{hb8(}H)(XuH%or^G7TG4R`4Iibv7{tq+x#m000JJOGiWiCjc-23ppEfa{vGU z32;bRa{vGi{Qv+C{Q{Of>5cc?Q*If2>-2U$&+~qId+R($iUcSEo4^(DWX~0_0Th)rKp${OdJcfzMy10* zg~n3>hSH>Mz$Mvp0kp=%C2On$3qankWiJq&17+advH6(z#F};j-VVH3ZO!o+Cq9me z@2qJ(;0bVVwTYniY`hi|*R81v^gBN1PPXKD!QYQ}C85}{M@eN#U6LjpuS?pNbSr6A zQpcApNs>mynd8MRYEu5ie@Fb$Eb-QFh^ybQBT4%yuCv%D{QwLCwP2b}0i(dHyUp; ZA)oDt9LV^L_F(`3002ovPDHLkV1h83oofI9 diff --git a/data/themes/default/loop_points_off.png b/data/themes/default/loop_points_off.png index 23ba7c6babb8f5e3fac254966560d831613a1209..a08b304dd8760fc77e5e6be0ac7a7f29178529a1 100644 GIT binary patch delta 239 zcmVJQY!n&7q^VrO+0Jgw5zM-}E%0lfm_2m4Xi?4)c zw)mMa$mSkWVI2a+o7#>ns;!%|cn&u_$EJm(ouji#(jo1z1giKp2X)Aloen*i*$i01 ptMinsN0D>}PB|GDpT?&DJOC$eH|*fZ=+yuK002ovPDHLkV1nY>YKQ;; delta 362 zcmV-w0hRu)0;mI!Bq9WJLP=Bz2nYy#2xN!=000SaNLh0L01ejw01ejxLMWSfkv1oP z0VPR9K~y-6&D1+f13?hL@&EZm5CSQJOTcN|+D3~Mfso{5pD#oN8(U$uSVUGtgj8aD zHhW?9Zuc%Bg;U)!Ge72Ths$<$_gH4EEL96ZXU+DaNZ1=rICj>w7QkA-xC-0?M$Ve{ z99SC|`Q8!;un7#EHJt`n2N?RESyWwr$~rJ`)^rnC7Z~{3UlF_IRiN*zkqS(JzON}o zYHc#0=d776s(haO8VSV6H?@nqmPu0rKLU{rf-m4=JiuA*%RVa{vGU07*qo IM6N<$f-Jw5p#T5? diff --git a/data/themes/default/loop_points_on.png b/data/themes/default/loop_points_on.png index ee6cf0d20f049979d7d26d80729cd13b5e0375d2..908eccd9cb99555125423b90826fb787de808ba9 100644 GIT binary patch delta 203 zcmV;+05t!o1BwEWBoYa5NLh0L01m?d01m?e$8V@)kwzzf0GCNbK~y-6?UKO_gD?z5 zzgVQq0o|tT&}-%*bg#nY^+KdTT_r}S>Y?uz#eT|nEdBXifEJj5nR;d*0vAj8aESIq zOPF5PIa2lMrPd_?&Pl!XHC{o9LqZ_b6jenALSFZ=FZ-0fBufI|x*nfa@nr3TB{LXP zT|UE`q$p`FDaFoMB#n{=N6#k3{pflXrKbs3X*gB*RbDfbF#5@QAmIQ2002ovPDHLk FV1gyESup?r delta 362 zcmV-w0hRuW0;mI!Bq9WJLP=Bz2nYy#2xN!=000SaNLh0L01ejw01ejxLMWSfkv1oP z0VPR9K~y-6&D1e#0znXl;a5pv69}YnEwT`@m9?cPSP3?^g1wL*;x7=>3u$cxD?zce zw%Tf&Y!wKkNfAPtF>G>od#7BHsqSv(dl=Z|D)qYu%B*UsS_pb)?=MBdY`EdxJKMYf zYXbW;umSA6v#m3*7O<0S0tC1PHs0BPZUL+dY$WqjX@_zRti7{?2sQxLGW)BtPPqmw zy|befYy>Q2CQ-3#a|JBCvy%Xu01N45hGJC(=HA&Gd7tLYmw&PIHv9q}l{QZR-q|-` z7F#5?;U_RYVF2*)Im-HF&M@1j#0eB;0XjT;NB}K96|zt z=wVQIcmePt=!OLIbHR*h%5)r;yri4tsSWS>XmVY=7`otqrCa{s(l4Cw$9m zj|F;NU~es;RIja2JV)jo?omePB_67?TJyHd;xR`pjYrA&gC^578K+J}RIk880_x{ez>Wi{?D@ou{ScU5uO;2m`Ve)wCt4A(X1EMhK`9#=(C z6d{E7xbM+TnpJBzr}XmLd8TEPj+(u5myQFCDV1wZugh7(wUB u_^I{|f1-9+0v=$3H9o7g#$z_Klh6hUK>Hq)%NJ&INR5;7cl)Xv=K@^40m;!un|e4Phkq5Km>~*LJA2?Ajr&oHWOJlKYzQ0N-iu6?7ionyZ76X z<~_``7uMQpsZ=^N#?*~5+mZx40-fP-cvCKy-*b0y&NaOEJ*I^BzUQ24;reBq^VA=wbj&^%~{Qp<6CR1liamZ6IEj21JDOPffwKk z*h(fk=UOA3;(zzLjj5-4EJ^B1dW+|Rq|Nvo{Xk{BRth0>Qx30zLUdUHc7bEyDZQK! zLN|{fz%y_P>;WrL+yEDug5LXf1|?(6oF-+2AlvNnPC6PulF05!y5lwUvm{-*ltEI$KoCVAmf{_PClI^XkxIpkC$R<@--_68o+nFIO>8jrJ{CS=J=>)De1W$rWuzwdEL+%B0!RQjuz2Hso zEl?s)f~BC}1@@5v`P^iof|U_Bf{CCo=Y`-UoVB_|&NDk^nQ2_Q@rNJ8`^{uIG z&68T~tmWJq=5yNJ-xv!NssFz#IJmCUh^7eRHcor!0&K$NBBCw306#mdNy@{z<=hzN z+VyzoK@Zsd?N=KOe-{L!umVXBjdtqR<}oE)<~$S(0?fXYD%BW4U+{jxr?(c|C4mOI waO1~*4%Jzh0}ll=K`ru6K0@MVMB!32COGiWi{{UhDV%x}L$p8QV32;bRa{vGf6951U69E94oEQKA z00(qQO+^Rd2n`S&2PkaW`Tzg{;Ymb6R5;7cl+8;UQ5400V-gKXv}M#XgapxrAlO|& zArPb>7^qAj6oQalaX?&%4J|=vHr=$i=`yrXN;h4&Xx-@2U4JXIe}J}yLdH-U2nI9^ zVJ5e$gicIG7gD_N;C*n<<=k`Lg_|BqzlXDYUJIILPN`Jpl;h)h0GXctBGKrf6^p%( zY;XU*auwS!bVpUI0@7)TL%$N3qw6pG0bKyr^%tI$ZNt!grP4vsqiqevWg%1EyDBN+`Cz;&2 zAc{nzhpq~nn|Dd2O2lHn@cV5-p>vweFaU#t=Pt#&b9>pw(=w~8nSQqEZ|p>3m)Y5O zjE;UL5HzS_+Yu})&dJFG_V%7ouiy4G_H})|Yab5(XMEkL_zR;PS8El6#Jd0h002ov JPDHLkV1mna3tj*K diff --git a/data/themes/default/trackop.png b/data/themes/default/trackop.png index b26dd6ef26a3f426043288746533a1a94d5e7769..dd200d095f28cc60a268b899d9f74c4fe6c9cf41 100644 GIT binary patch delta 480 zcmbQtJeOsHiV$akM`SSr1K(i~W;~w1B4whRdOd?oiEBhjNM=b+Dua)&Z?J-YL28~t zaDHh~a;k!7a(-U%ylB=L3=9lnAa#zVB^mie3_+<$smaNS3NEQ`nW;G`#_xY?164$T zR5<4sR2F5XXOu8FI~ynjlqTh5CM&q)=O$+60hOm@CMT9;=I1G7l#~=$>FX!g7o`FP z%2Jc_b8~@q>E#!t>lc7ErGWM5SEUx^>l^AB=pTE$_b1SjKb|g*Ar`$yrx@lPau9H> zU+|F0qtjuI!ZVJLD<+m~1=?qPnOLkec5rIObFmply?@ZF)OtT+-v96K|9tIRn$FOOzYRn$u=lC` zQ9IUa&AdK0kWDx3UdfjaJiPLgs@~PAuAF2tZA;nLc}s2{ez(TCK5aEmymg)MOqUf# zx4wJGxPCiPyEiZU%o1k#o##HhzINhdQ|hE8Y+4C(W-y-d=J{We8?^aSw{z{r=QA^W zGW+f|yq=Le=c2lS`MQiRY6luq=H%tgG>LbyXTP7h?3zK(7S|4*|E%^=2HHZAd-=V9 PA;IA3>gTe~DWM4fRr$kl delta 341 zcmV-b0jmC;1d{`hBq9WJLP=Bz2nYy#2xN!=000SaNLh0L01ejw01ejxLMWSfkv1oP z0T4+2L|#QEOUH;(iZwG=3IRYix1_0Q&Que_mb`Yd+6`Y3(mXSezeXcC#) zyiw~C&>z5EA@|^Nt@i?C3$_X&yk~%wq(jR4Yhj0+F$+$TUY&Pc0N%L!E@GU-chQb| z1L~LrTmgHRTVKLuqXllUDz?~a=#wu&0ztof!2XqU}fGYkIf<5 nzD8PRSLyauQVy8X#XI&F1w%jy@=XZZ00000NkvXXu0mjf=Q)^6 From 17565caf535769a69eb06987e86220d2f9acb24d Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 28 Jul 2020 17:07:35 +0200 Subject: [PATCH 056/180] Improved relative paths (#5117) * Create PathUtils * Replace old SampleBuffer calls * Fix automatic track names * Fix things * Remove accidental duplicate file * Add includes * More incldues * PhysSong's code review + style * Fix vestige loading? Seems more reasonable to convert from relative on load and to relative on save than vice versa. * Typo fix * More Bases * Enable more bases * Add missing semicolons in prefixes * Nicer sample track tooltip * Use correct directories "userXDir" gives the default dir for ladspa, sf2, and gig. "xDir" gives the user dir. * Make relative to both default and custom locations Part 1 * Make relative to both default and custom locations Part 2 * Typofix * Typofix * Fix upgrade function after base renaming * Fix Tests * Update tests/src/core/RelativePathsTest.cpp Co-Authored-By: Hyunjin Song * Choose UserXBase over DefaultXBase if identical toShortestRelative sticks with the first base found if two bases give the same path length. By placing UserXBase Bases before DefaultXBase Bases in the relativeBases vector, toShortestRelative will prioritize them. * Ensure baseLocation always has trailing slash Otherwise, a user configuring a path without one will break things. * Move loc declaration out of switch * Semicolon * Apply suggestions from code review... * Include PathUtil and sort includes * More granular includes * Apply suggestions from code review Co-Authored-By: Hyunjin Song * Update include/PathUtil.h * Leave empty paths alone * Fix stupid merge * Really fix merge. Hopefully * Switch Base from enum to class enum * Don't pass Base by reference * Use QStringLiteral for static QString allocation in basePrefix method * Make VST loading more similar to previous implementation * Fix tests after enum change * Attempt to fix VST loading, nicer name for sample clips * Fix last review comment Don't append a "/" that will be removed by cleanPath later * Apply suggestions from code review Co-authored-by: Dominic Clark Co-authored-by: Hyunjin Song Co-authored-by: Dominic Clark --- include/PathUtil.h | 41 +++++ include/SampleBuffer.h | 5 +- plugins/GigPlayer/GigPlayer.cpp | 19 +-- .../audio_file_processor.cpp | 29 ++-- plugins/patman/patman.cpp | 22 +-- plugins/sf2_player/sf2_player.cpp | 14 +- plugins/vestige/vestige.cpp | 18 +-- plugins/vst_base/VstPlugin.cpp | 12 +- src/core/CMakeLists.txt | 1 + src/core/PathUtil.cpp | 148 ++++++++++++++++++ src/core/SampleBuffer.cpp | 67 +------- src/tracks/SampleTrack.cpp | 28 ++-- tests/src/core/RelativePathsTest.cpp | 28 +++- 13 files changed, 282 insertions(+), 150 deletions(-) create mode 100644 include/PathUtil.h create mode 100644 src/core/PathUtil.cpp diff --git a/include/PathUtil.h b/include/PathUtil.h new file mode 100644 index 00000000000..cc6b982a114 --- /dev/null +++ b/include/PathUtil.h @@ -0,0 +1,41 @@ +#ifndef PATHUTIL_H +#define PATHUTIL_H + +#include "lmms_export.h" + +#include + +namespace PathUtil +{ + enum class Base { Absolute, ProjectDir, FactorySample, UserSample, UserVST, Preset, + UserLADSPA, DefaultLADSPA, UserSoundfont, DefaultSoundfont, UserGIG, DefaultGIG }; + + //! Return the directory associated with a given base as a QString + QString LMMS_EXPORT baseLocation(const Base base); + //! Return the directory associated with a given base as a QDir + QDir LMMS_EXPORT baseQDir (const Base base); + //! Return the prefix used to denote this base in path strings + QString LMMS_EXPORT basePrefix(const Base base); + //! Check the prefix of a path and return the base it corresponds to + //! Defaults to Base::Absolute + Base LMMS_EXPORT baseLookup(const QString & path); + + //! Remove the prefix from a path, iff there is one + QString LMMS_EXPORT stripPrefix(const QString & path); + //! Get the filename for a path, handling prefixed paths correctly + QString LMMS_EXPORT cleanName(const QString & path); + + //! Upgrade prefix-less relative paths to the new format + QString LMMS_EXPORT oldRelativeUpgrade(const QString & input); + + //! Make this path absolute + QString LMMS_EXPORT toAbsolute(const QString & input); + //! Make this path relative to a given base, return an absolute path if that fails + QString LMMS_EXPORT relativeOrAbsolute(const QString & input, const Base base); + //! Make this path relative to any base, choosing the shortest if there are + //! multiple options. Defaults to an absolute path if all bases fail. + QString LMMS_EXPORT toShortestRelative(const QString & input); + +} + +#endif diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 26e85602508..89a3add3b2a 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -84,7 +84,7 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject { m_isBackwards = _backwards; } - + int interpolationMode() const { return m_interpolationMode; @@ -251,9 +251,6 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject m_varLock.unlock(); } - static QString tryToMakeRelative( const QString & _file ); - static QString tryToMakeAbsolute(const QString & file); - public slots: void setAudioFile( const QString & _audio_file ); diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index b358e24e93c..334e2bd7737 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -28,6 +28,7 @@ * */ +#include "GigPlayer.h" #include #include @@ -35,18 +36,18 @@ #include #include -#include "FileDialog.h" -#include "GigPlayer.h" +#include "ConfigManager.h" +#include "endian_handling.h" #include "Engine.h" +#include "FileDialog.h" #include "InstrumentTrack.h" #include "InstrumentPlayHandle.h" +#include "Knob.h" #include "Mixer.h" #include "NotePlayHandle.h" -#include "Knob.h" +#include "PathUtil.h" #include "SampleBuffer.h" #include "Song.h" -#include "ConfigManager.h" -#include "endian_handling.h" #include "PatchesDialog.h" #include "ToolTip.h" @@ -211,8 +212,8 @@ void GigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) try { - m_instance = new GigInstance( SampleBuffer::tryToMakeAbsolute( _gigFile ) ); - m_filename = SampleBuffer::tryToMakeRelative( _gigFile ); + m_instance = new GigInstance( PathUtil::toAbsolute( _gigFile ) ); + m_filename = PathUtil::toShortestRelative( _gigFile ); } catch( ... ) { @@ -225,7 +226,7 @@ void GigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) if( updateTrackName == true ) { - instrumentTrack()->setName( QFileInfo( _gigFile ).baseName() ); + instrumentTrack()->setName(PathUtil::cleanName( _gigFile ) ); updatePatch(); } } @@ -1057,7 +1058,7 @@ void GigInstrumentView::showFileDialog() if( k->m_filename != "" ) { - QString f = SampleBuffer::tryToMakeAbsolute( k->m_filename ); + QString f = PathUtil::toAbsolute( k->m_filename ); ofd.setDirectory( QFileInfo( f ).absolutePath() ); ofd.selectFile( QFileInfo( f ).fileName() ); } diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index c8850392eb4..51af71f55eb 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -22,6 +22,7 @@ * */ +#include "audio_file_processor.h" #include #include @@ -31,18 +32,18 @@ #include -#include "audio_file_processor.h" #include "ConfigManager.h" +#include "DataFile.h" #include "Engine.h" -#include "Song.h" +#include "gui_templates.h" #include "InstrumentTrack.h" +#include "interpolation.h" #include "Mixer.h" #include "NotePlayHandle.h" -#include "interpolation.h" -#include "gui_templates.h" -#include "ToolTip.h" +#include "PathUtil.h" +#include "Song.h" #include "StringPairDrag.h" -#include "DataFile.h" +#include "ToolTip.h" #include "embed.h" #include "plugin_export.h" @@ -97,13 +98,13 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : this, SLOT( loopPointChanged() ) ); connect( &m_stutterModel, SIGNAL( dataChanged() ), this, SLOT( stutterModelChanged() ) ); - + //interpolation modes m_interpolationModel.addItem( tr( "None" ) ); m_interpolationModel.addItem( tr( "Linear" ) ); m_interpolationModel.addItem( tr( "Sinc" ) ); m_interpolationModel.setValue( 1 ); - + pointChanged(); } @@ -237,7 +238,7 @@ void audioFileProcessor::loadSettings( const QDomElement & _this ) { setAudioFile( _this.attribute( "src" ), false ); - QString absolutePath = m_sampleBuffer.tryToMakeAbsolute( m_sampleBuffer.audioFile() ); + QString absolutePath = PathUtil::toAbsolute( m_sampleBuffer.audioFile() ); if ( !QFileInfo( absolutePath ).exists() ) { QString message = tr( "Sample not found: %1" ).arg( m_sampleBuffer.audioFile() ); @@ -329,7 +330,7 @@ void audioFileProcessor::setAudioFile( const QString & _audio_file, m_sampleBuffer.audioFile().isEmpty() ) ) { // then set it to new one - instrumentTrack()->setName( QFileInfo( _audio_file).fileName() ); + instrumentTrack()->setName( PathUtil::cleanName( _audio_file ) ); } // else we don't touch the track-name, because the user named it self @@ -363,7 +364,7 @@ void audioFileProcessor::stutterModelChanged() } -void audioFileProcessor::startPointChanged( void ) +void audioFileProcessor::startPointChanged( void ) { // check if start is over end and swap values if so if( m_startPointModel.value() > m_endPointModel.value() ) @@ -390,7 +391,7 @@ void audioFileProcessor::startPointChanged( void ) { m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) ); } - + pointChanged(); } @@ -1284,7 +1285,3 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * model, void *) } - - - - diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index 8a0c7134011..e5170383438 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -3,7 +3,7 @@ * * Copyright (c) 2007-2008 Javier Serrano Polo * Copyright (c) 2009-2014 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -32,15 +32,15 @@ #include "ConfigManager.h" #include "endian_handling.h" #include "Engine.h" +#include "FileDialog.h" #include "gui_templates.h" #include "InstrumentTrack.h" #include "NotePlayHandle.h" +#include "PathUtil.h" #include "PixmapButton.h" #include "Song.h" #include "StringPairDrag.h" #include "ToolTip.h" -#include "FileDialog.h" -#include "ConfigManager.h" #include "embed.h" @@ -192,14 +192,13 @@ void patmanInstrument::setFile( const QString & _patch_file, bool _rename ) m_patchFile == "" ) ) { // then set it to new one - instrumentTrack()->setName( QFileInfo( _patch_file - ).fileName() ); + instrumentTrack()->setName( PathUtil::cleanName( _patch_file ) ); } // else we don't touch the instrument-track-name, because the user // named it self - m_patchFile = SampleBuffer::tryToMakeRelative( _patch_file ); - LoadErrors error = loadPatch( SampleBuffer::tryToMakeAbsolute( _patch_file ) ); + m_patchFile = PathUtil::toShortestRelative( _patch_file ); + LoadErrors error = loadPatch( PathUtil::toAbsolute( _patch_file ) ); if( error ) { printf("Load error\n"); @@ -625,8 +624,8 @@ void PatmanView::paintEvent( QPaintEvent * ) QPainter p( this ); p.setFont( pointSize<8>( font() ) ); - p.drawText( 8, 116, 235, 16, - Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, + p.drawText( 8, 116, 235, 16, + Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, m_displayFilename ); } @@ -641,8 +640,3 @@ void PatmanView::modelChanged( void ) connect( m_pi, SIGNAL( fileChanged() ), this, SLOT( updateFilename() ) ); } - - - - - diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 2f9456370e1..7065a08002c 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -23,6 +23,8 @@ * */ +#include "sf2_player.h" + #include #include #include @@ -30,14 +32,14 @@ #include "ConfigManager.h" #include "FileDialog.h" -#include "sf2_player.h" #include "ConfigManager.h" #include "Engine.h" #include "InstrumentTrack.h" #include "InstrumentPlayHandle.h" +#include "Knob.h" #include "Mixer.h" #include "NotePlayHandle.h" -#include "Knob.h" +#include "PathUtil.h" #include "SampleBuffer.h" #include "Song.h" @@ -372,8 +374,8 @@ void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName ) emit fileLoading(); // Used for loading file - char * sf2Ascii = qstrdup( qPrintable( SampleBuffer::tryToMakeAbsolute( _sf2File ) ) ); - QString relativePath = SampleBuffer::tryToMakeRelative( _sf2File ); + char * sf2Ascii = qstrdup( qPrintable( PathUtil::toAbsolute( _sf2File ) ) ); + QString relativePath = PathUtil::toShortestRelative( _sf2File ); // free reference to soundfont if one is selected freeFont(); @@ -435,7 +437,7 @@ void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName ) if( updateTrackName || instrumentTrack()->displayName() == displayName() ) { - instrumentTrack()->setName( QFileInfo( _sf2File ).baseName() ); + instrumentTrack()->setName( PathUtil::cleanName( _sf2File ) ); } } @@ -1145,7 +1147,7 @@ void sf2InstrumentView::showFileDialog() if( k->m_filename != "" ) { - QString f = SampleBuffer::tryToMakeAbsolute( k->m_filename ); + QString f = PathUtil::toAbsolute( k->m_filename ); ofd.setDirectory( QFileInfo( f ).absolutePath() ); ofd.selectFile( QFileInfo( f ).fileName() ); } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 0d75992a4e5..b17a1684541 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -40,24 +40,24 @@ #include -#include "ConfigManager.h" #include "BufferManager.h" #include "ConfigManager.h" #include "Engine.h" +#include "FileDialog.h" +#include "GuiApplication.h" #include "gui_templates.h" #include "InstrumentPlayHandle.h" #include "InstrumentTrack.h" #include "LocaleHelper.h" #include "MainWindow.h" #include "Mixer.h" -#include "GuiApplication.h" +#include "PathUtil.h" #include "PixmapButton.h" #include "SampleBuffer.h" #include "Song.h" #include "StringPairDrag.h" #include "TextFloat.h" #include "ToolTip.h" -#include "FileDialog.h" #include "embed.h" @@ -271,7 +271,7 @@ void vestigeInstrument::reloadPlugin() void vestigeInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - _this.setAttribute( "plugin", m_pluginDLL ); + _this.setAttribute( "plugin", PathUtil::toShortestRelative(m_pluginDLL) ); m_pluginMutex.lock(); if( m_plugin != NULL ) { @@ -338,14 +338,14 @@ void vestigeInstrument::loadFile( const QString & _file ) // if the same is loaded don't load again (for preview) if (instrumentTrack() != NULL && instrumentTrack()->isPreviewMode() && - m_pluginDLL == SampleBuffer::tryToMakeRelative( _file )) + m_pluginDLL == PathUtil::toShortestRelative( _file )) return; if ( m_plugin != NULL ) { closePlugin(); } - m_pluginDLL = SampleBuffer::tryToMakeRelative( _file ); + m_pluginDLL = PathUtil::toShortestRelative( _file ); TextFloat * tf = NULL; if( gui ) { @@ -686,7 +686,7 @@ void VestigeInstrumentView::openPlugin() if( m_vi->m_pluginDLL != "" ) { - QString f = SampleBuffer::tryToMakeAbsolute( m_vi->m_pluginDLL ); + QString f = PathUtil::toAbsolute( m_vi->m_pluginDLL ); ofd.setDirectory( QFileInfo( f ).absolutePath() ); ofd.selectFile( QFileInfo( f ).fileName() ); } @@ -1233,7 +1233,3 @@ Q_DECL_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) } - - - - diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index cd4844b8128..7d6a45940d2 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -59,6 +59,7 @@ #include "LocaleHelper.h" #include "MainWindow.h" #include "Mixer.h" +#include "PathUtil.h" #include "Song.h" #include "FileDialog.h" @@ -121,7 +122,7 @@ class FileInfo VstPlugin::VstPlugin( const QString & _plugin ) : - m_plugin( _plugin ), + m_plugin( PathUtil::toAbsolute(_plugin) ), m_pluginWindowID( 0 ), m_embedMethod( gui ? ConfigManager::inst()->vstEmbedMethod() @@ -129,11 +130,6 @@ VstPlugin::VstPlugin( const QString & _plugin ) : m_version( 0 ), m_currentProgram() { - if( QDir::isRelativePath( m_plugin ) ) - { - m_plugin = ConfigManager::inst()->vstDir() + m_plugin; - } - setSplittedChannels( true ); PE::MachineType machineType; @@ -804,7 +800,3 @@ QString VstPlugin::embedMethod() const { return m_embedMethod; } - - - - diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 42d0f6784ae..730791bf770 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -46,6 +46,7 @@ set(LMMS_SRCS core/Note.cpp core/NotePlayHandle.cpp core/Oscillator.cpp + core/PathUtil.cpp core/PeakController.cpp core/PerfLog.cpp core/Piano.cpp diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp new file mode 100644 index 00000000000..ab81c49417c --- /dev/null +++ b/src/core/PathUtil.cpp @@ -0,0 +1,148 @@ +#include "PathUtil.h" + +#include +#include +#include + +#include "ConfigManager.h" + +namespace PathUtil +{ + Base relativeBases[] = { Base::ProjectDir, Base::FactorySample, Base::UserSample, Base::UserVST, Base::Preset, + Base::UserLADSPA, Base::DefaultLADSPA, Base::UserSoundfont, Base::DefaultSoundfont, Base::UserGIG, Base::DefaultGIG }; + + QString baseLocation(const Base base) + { + QString loc = ""; + switch (base) + { + case Base::ProjectDir : loc = ConfigManager::inst()->userProjectsDir(); break; + case Base::FactorySample : + { + QDir fsd = QDir(ConfigManager::inst()->factorySamplesDir()); + loc = fsd.absolutePath(); break; + } + case Base::UserSample : loc = ConfigManager::inst()->userSamplesDir(); break; + case Base::UserVST : loc = ConfigManager::inst()->userVstDir(); break; + case Base::Preset : loc = ConfigManager::inst()->userPresetsDir(); break; + case Base::UserLADSPA : loc = ConfigManager::inst()->ladspaDir(); break; + case Base::DefaultLADSPA : loc = ConfigManager::inst()->userLadspaDir(); break; + case Base::UserSoundfont : loc = ConfigManager::inst()->sf2Dir(); break; + case Base::DefaultSoundfont : loc = ConfigManager::inst()->userSf2Dir(); break; + case Base::UserGIG : loc = ConfigManager::inst()->gigDir(); break; + case Base::DefaultGIG : loc = ConfigManager::inst()->userGigDir(); break; + default : return QString(""); + } + return QDir::cleanPath(loc) + "/"; + } + + QDir baseQDir (const Base base) { return QDir(baseLocation(base)); } + + QString basePrefix(const Base base) + { + switch (base) + { + case Base::ProjectDir : return QStringLiteral("userprojects:"); + case Base::FactorySample : return QStringLiteral("factorysample:"); + case Base::UserSample : return QStringLiteral("usersample:"); + case Base::UserVST : return QStringLiteral("uservst:"); + case Base::Preset : return QStringLiteral("preset:"); + case Base::UserLADSPA : return QStringLiteral("userladspa:"); + case Base::DefaultLADSPA : return QStringLiteral("defaultladspa:"); + case Base::UserSoundfont : return QStringLiteral("usersoundfont:"); + case Base::DefaultSoundfont : return QStringLiteral("defaultsoundfont:"); + case Base::UserGIG : return QStringLiteral("usergig:"); + case Base::DefaultGIG : return QStringLiteral("defaultgig:"); + default : return QStringLiteral(""); + } + } + + Base baseLookup(const QString & path) + { + for (auto base: relativeBases) + { + QString prefix = basePrefix(base); + if ( path.startsWith(prefix) ) { return base; } + } + return Base::Absolute; + } + + + + + QString stripPrefix(const QString & path) + { + return path.mid( basePrefix(baseLookup(path)).length() ); + } + + QString cleanName(const QString & path) + { + return stripPrefix(QFileInfo(path).baseName()); + } + + + + + QString oldRelativeUpgrade(const QString & input) + { + if (input.isEmpty()) { return input; } + + //Start by assuming that the file is a user sample + Base assumedBase = Base::UserSample; + + //Check if it's a factory sample + QString factoryPath = baseLocation(Base::FactorySample) + input; + QFileInfo factoryInfo(factoryPath); + if (factoryInfo.exists()) { assumedBase = Base::FactorySample; } + + //Check if it's a VST + QString vstPath = baseLocation(Base::UserVST) + input; + QFileInfo vstInfo(vstPath); + if (vstInfo.exists()) { assumedBase = Base::UserVST; } + + //Assume we've found the correct base location, return the full path + return basePrefix(assumedBase) + input; + } + + + + + QString toAbsolute(const QString & input) + { + //First, do no harm to absolute paths + QFileInfo inputFileInfo = QFileInfo(input); + if (inputFileInfo.isAbsolute()) { return input; } + //Next, handle old relative paths with no prefix + QString upgraded = input.contains(":") ? input : oldRelativeUpgrade(input); + + Base base = baseLookup(upgraded); + return baseLocation(base) + upgraded.remove(0, basePrefix(base).length()); + } + + QString relativeOrAbsolute(const QString & input, const Base base) + { + if (input.isEmpty()) { return input; } + QString absolutePath = toAbsolute(input); + QString relativePath = baseQDir(base).relativeFilePath(absolutePath); + return relativePath.startsWith("..") ? absolutePath : relativePath; + } + + QString toShortestRelative(const QString & input) + { + QFileInfo inputFileInfo = QFileInfo(input); + QString absolutePath = inputFileInfo.isAbsolute() ? input : toAbsolute(input); + + Base shortestBase = Base::Absolute; + QString shortestPath = relativeOrAbsolute(absolutePath, shortestBase); + for (auto base: relativeBases) + { + QString otherPath = relativeOrAbsolute(absolutePath, base); + if (otherPath.length() < shortestPath.length()) + { + shortestBase = base; + shortestPath = otherPath; + } + } + return basePrefix(shortestBase) + relativeOrAbsolute(absolutePath, shortestBase); + } +} diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index d0c39b13aa8..cd943638d4f 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -24,7 +24,6 @@ #include "SampleBuffer.h" - #include #include #include @@ -55,11 +54,11 @@ #include "Engine.h" #include "GuiApplication.h" #include "Mixer.h" +#include "PathUtil.h" #include "FileDialog.h" - SampleBuffer::SampleBuffer() : m_audioFile( "" ), m_origData( NULL ), @@ -179,7 +178,7 @@ void SampleBuffer::update( bool _keep_settings ) } else if( !m_audioFile.isEmpty() ) { - QString file = tryToMakeAbsolute( m_audioFile ); + QString file = PathUtil::toAbsolute( m_audioFile ); int_sample_t * buf = NULL; sample_t * fbuf = NULL; ch_cnt_t channels = DEFAULT_CHANNELS; @@ -781,7 +780,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, } } - if( tmp != NULL ) + if( tmp != NULL ) { MM_FREE( tmp ); } @@ -1031,7 +1030,7 @@ QString SampleBuffer::openAudioFile() const { return QString(); } - return tryToMakeRelative( ofd.selectedFiles()[0] ); + return PathUtil::toShortestRelative( ofd.selectedFiles()[0] ); } return QString(); @@ -1222,7 +1221,7 @@ SampleBuffer * SampleBuffer::resample( const sample_rate_t _src_sr, void SampleBuffer::setAudioFile( const QString & _audio_file ) { - m_audioFile = tryToMakeRelative( _audio_file ); + m_audioFile = PathUtil::toShortestRelative( _audio_file ); update(); } @@ -1419,60 +1418,6 @@ void SampleBuffer::setReversed( bool _on ) -QString SampleBuffer::tryToMakeRelative( const QString & file ) -{ - if( QFileInfo( file ).isRelative() == false ) - { - // Normalize the path - QString f( QDir::cleanPath( file ) ); - - // First, look in factory samples - // Isolate "samples/" from "data:/samples/" - QString samplesSuffix = ConfigManager::inst()->factorySamplesDir().mid( ConfigManager::inst()->dataDir().length() ); - - // Iterate over all valid "data:/" searchPaths - for ( const QString & path : QDir::searchPaths( "data" ) ) - { - QString samplesPath = QDir::cleanPath( path + samplesSuffix ) + "/"; - if ( f.startsWith( samplesPath ) ) - { - return QString( f ).mid( samplesPath.length() ); - } - } - - // Next, look in user samples - QString usd = ConfigManager::inst()->userSamplesDir(); - usd.replace( QDir::separator(), '/' ); - if( f.startsWith( usd ) ) - { - return QString( f ).mid( usd.length() ); - } - } - return file; -} - - - - -QString SampleBuffer::tryToMakeAbsolute(const QString& file) -{ - QFileInfo f(file); - - if(f.isRelative()) - { - f = QFileInfo(ConfigManager::inst()->userSamplesDir() + file); - - if(! f.exists()) - { - f = QFileInfo(ConfigManager::inst()->factorySamplesDir() + file); - } - } - - if (f.exists()) { - return f.absoluteFilePath(); - } - return file; -} @@ -1488,7 +1433,7 @@ SampleBuffer::handleState::handleState( bool _varying_pitch, int interpolation_m { int error; m_interpolationMode = interpolation_mode; - + if( ( m_resamplingData = src_new( interpolation_mode, DEFAULT_CHANNELS, &error ) ) == NULL ) { qDebug( "Error: src_new() failed in sample_buffer.cpp!\n" ); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 9232cc8b0d4..86e1861c691 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -34,23 +34,24 @@ #include #include +#include "BBTrack.h" +#include "EffectRackView.h" +#include "embed.h" +#include "FxMixerView.h" #include "gui_templates.h" #include "GuiApplication.h" -#include "Song.h" -#include "embed.h" -#include "ToolTip.h" -#include "BBTrack.h" +#include "Knob.h" +#include "MainWindow.h" +#include "Mixer.h" +#include "PathUtil.h" #include "SamplePlayHandle.h" #include "SampleRecordHandle.h" +#include "Song.h" #include "SongEditor.h" #include "StringPairDrag.h" -#include "TimeLineWidget.h" -#include "Knob.h" -#include "MainWindow.h" -#include "Mixer.h" -#include "EffectRackView.h" -#include "FxMixerView.h" #include "TabWidget.h" +#include "TimeLineWidget.h" +#include "ToolTip.h" #include "TrackLabelButton.h" SampleTCO::SampleTCO( Track * _track ) : @@ -333,7 +334,7 @@ void SampleTCOView::updateSample() // set tooltip to filename so that user can see what sample this // sample-tco contains ToolTip::add( this, ( m_tco->m_sampleBuffer->audioFile() != "" ) ? - m_tco->m_sampleBuffer->audioFile() : + PathUtil::toAbsolute(m_tco->m_sampleBuffer->audioFile()) : tr( "Double-click to open sample" ) ); } @@ -535,9 +536,8 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) qMax( static_cast( m_tco->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); m_tco->m_sampleBuffer->visualize( p, r, pe->rect() ); - QFileInfo fileInfo(m_tco->m_sampleBuffer->audioFile()); - QString filename = fileInfo.fileName(); - paintTextLabel(filename, p); + QString name = PathUtil::cleanName(m_tco->m_sampleBuffer->audioFile()); + paintTextLabel(name, p); // disable antialiasing for borders, since its not needed p.setRenderHint( QPainter::Antialiasing, false ); diff --git a/tests/src/core/RelativePathsTest.cpp b/tests/src/core/RelativePathsTest.cpp index 6a75483776a..3f1712e1b5a 100644 --- a/tests/src/core/RelativePathsTest.cpp +++ b/tests/src/core/RelativePathsTest.cpp @@ -26,6 +26,7 @@ #include "ConfigManager.h" #include "SampleBuffer.h" +#include "PathUtil.h" #include @@ -33,18 +34,35 @@ class RelativePathsTest : QTestSuite { Q_OBJECT private slots: - void RelativePathComparisonTests() + void PathUtilComparisonTests() { QFileInfo fi(ConfigManager::inst()->factorySamplesDir() + "/drums/kick01.ogg"); QVERIFY(fi.exists()); QString absPath = fi.absoluteFilePath(); - QString relPath = "drums/kick01.ogg"; + QString oldRelPath = "drums/kick01.ogg"; + QString relPath = PathUtil::basePrefix(PathUtil::Base::FactorySample) + "drums/kick01.ogg"; QString fuzPath = absPath; fuzPath.replace(relPath, "drums/.///kick01.ogg"); - QCOMPARE(SampleBuffer::tryToMakeRelative(absPath), relPath); - QCOMPARE(SampleBuffer::tryToMakeAbsolute(relPath), absPath); - QCOMPARE(SampleBuffer::tryToMakeRelative(fuzPath), relPath); + + //Test nicely formatted paths + QCOMPARE(PathUtil::toShortestRelative(absPath), relPath); + QCOMPARE(PathUtil::toAbsolute(relPath), absPath); + + //Test upgrading old paths + QCOMPARE(PathUtil::toShortestRelative(oldRelPath), relPath); + QCOMPARE(PathUtil::toAbsolute(oldRelPath), absPath); + + //Test weird but valid paths + QCOMPARE(PathUtil::toShortestRelative(fuzPath), relPath); + QCOMPARE(PathUtil::toAbsolute(fuzPath), absPath); + + //Empty paths should stay empty + QString empty = QString(""); + QCOMPARE(PathUtil::stripPrefix(""), empty); + QCOMPARE(PathUtil::cleanName(""), empty); + QCOMPARE(PathUtil::toAbsolute(""), empty); + QCOMPARE(PathUtil::toShortestRelative(""), empty); } } RelativePathTests; From cbb1ec9a56420aa711f4a0fb1b03648024dea007 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Wed, 29 Jul 2020 13:58:07 -0300 Subject: [PATCH 057/180] Removes unused variable on SongEditor.cpp (#5600) There was a variable declared but unused on the SongEditor.cpp file (method SongEditor::keyPressEvent), called QVector tcoViews. The variable was removed. --- src/gui/editors/SongEditor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index a7f67c743f8..b6647d44a3b 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -575,7 +575,6 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) } else if( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) { - QVector tcoViews; QVector so = selectedObjects(); for( QVector::iterator it = so.begin(); it != so.end(); ++it ) From e37f3793f381d84658c9dafd115f8c10bcba9c1a Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Wed, 29 Jul 2020 13:26:20 -0500 Subject: [PATCH 058/180] Fix validPattern to check if hasAutomation as well (#5124) --- include/AutomationEditor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 520416be80d..0a288f8afcf 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -70,7 +70,7 @@ class AutomationEditor : public QWidget, public JournallingObject inline bool validPattern() const { - return m_pattern != nullptr; + return m_pattern != nullptr && m_pattern->hasAutomation(); } void saveSettings(QDomDocument & doc, QDomElement & parent) override; From 639f3a49a3022aff4305e31d47afb7f491392567 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sat, 1 Aug 2020 15:03:23 -0300 Subject: [PATCH 059/180] Changes the behavior of "solo" so it doesn't mute Automation Tracks (#5547) * Changes the toggleSolo method - Changes the toggleSolo method so it doesn't mute automation tracks (keeps their original state) * Stop restoring Automation Track's mute values - Since Automation Tracks are not affected by enabling solo on other tracks, there's no need (and it's even counter intuitive) to restore their previous mute value. - Reduces a line by using "else if". * Saves two lines merging 2 conditionals in 1 * Adds the new solo behavior as a new LED button To allow the user choosing between the old solo behavior and the new one, a new LED button was added which will be used to enable the solo keeping the automation tracks states, while the red LED will enable the solo with the current behavior. Changes to the code: - Added a purple LED image that will be used on the new button. - Increased the default width of the widget that holds the mute and solo buttons so the third one fits. - Changed the positioning of the LEDs on both the standard and compact track modes to accomodate them. - Added a new model called m_soloAutomationsModel, which is connected to the new LED button (m_soloAutomationsBtn). This will dictate the behavior of the toggleSolo method. - The red LED calls the toggleSolo method as before. The purple LED will change the m_soloAutomationsModel value and toggle the red LED, which will then call toggleSolo. But since the value of m_soloAutomationsModel will be different, the new behavior will be used on the method. * Revert "Adds the new solo behavior as a new LED button" This reverts commit fdbc8b2712d85cb7cafaf0298531557135cf9968. After consulting fellow users and devs about this change to the PR, it was decided that adding a third button for the new solo behavior was not the best approach. This reverts the commit so we go back to just changing the solo behavior. Later an option can be added on LMMS settings to choose between the old and new behaviors. * Adds an option to use the legacy solo behavior This commit adds an option on LMMS settings (saved to the config file) to go back to the legacy behavior of the track solo button. The option can be found on Settings>General>"Use solo legacy behavior" Changes to the code: - Since there's a change to the configuration file, an upgrade method was created (upgrade_1_3_0) to add the value to the config XML if it isn't already present (safety check). - An attribute is added to the DOM structure of the app configuration under the "app" tag, called "sololegacybehavior", which can be either 0 or 1. - Changes were made to include/SetupDialog.h, include/ConfigManager.h and src/core/ConfigManager.cpp to implement this new configuration option. - The toggleSolo method was changed to behave according to the value of the "sololegacybehavior" configuration. * Changes the description of the solo setting Changes the description of the solo setting on the Setup Dialog from "Use solo legacy behavior" to "Mute automation tracks during solo" since the latter is more descriptive and new users wouldn't be confused about what the legacy behavior was. * Merges "if"s and "if-else"s where possible A conditional could be merged by using the "||" logical operator and there was a "if" nested inside an "else" that could be merged into a single "if-else". Very small code format change (keeping code block curly braces in separate lines). * Uses default value instead of upgrading ConfigFile Instead of using an upgrade method on the ConfigManager class to set a value to the sololegacybehavior parameter, we now use a default value on every call to ConfigManager::value that requests it. * Removes repetitive method call To make the loop more efficient, a local variable was created to hold the behavior of the solo selected in the setup dialog, instead of calling the ConfigManager::value method repeated times. Observation: Since no code was added/removed from ConfigManager.cpp, it was restored to its original state. There's however a TAB character in a blank line on line 145, which was there at the beginning of this PR but removed during it. It was written again in this commit to remove ConfigManager.cpp from the "Files changed" list. * Saves one line of code and adds a comment --- include/SetupDialog.h | 2 ++ src/core/Track.cpp | 20 ++++++++++++++++++-- src/gui/SetupDialog.cpp | 10 ++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index fa0ebde6af6..7aabde08618 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -75,6 +75,7 @@ private slots: void toggleCompactTrackButtons(bool enabled); void toggleOneInstrumentTrackWindow(bool enabled); void toggleSideBarOnRight(bool enabled); + void toggleSoloLegacyBehavior(bool enabled); void toggleMMPZ(bool enabled); void toggleDisableBackup(bool enabled); void toggleOpenLastProject(bool enabled); @@ -132,6 +133,7 @@ private slots: bool m_compactTrackButtons; bool m_oneInstrumentTrackWindow; bool m_sideBarOnRight; + bool m_soloLegacyBehavior; bool m_MMPZ; bool m_disableBackup; bool m_openLastProject; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 734cb4e12d9..6505ffabda9 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -2639,6 +2639,9 @@ void Track::toggleSolo() } const bool solo = m_soloModel.value(); + // Should we use the new behavior of solo or the older/legacy one? + const bool soloLegacyBehavior = ConfigManager::inst()->value("app", "sololegacybehavior", "0").toInt(); + for( TrackContainer::TrackList::const_iterator it = tl.begin(); it != tl.end(); ++it ) { @@ -2649,7 +2652,15 @@ void Track::toggleSolo() { ( *it )->m_mutedBeforeSolo = ( *it )->isMuted(); } - ( *it )->setMuted( *it == this ? false : true ); + // Don't mute AutomationTracks (keep their original state) unless we are on the sololegacybehavior mode + if( *it == this ) + { + ( *it )->setMuted( false ); + } + else if( soloLegacyBehavior || ( *it )->type() != AutomationTrack ) + { + ( *it )->setMuted( true ); + } if( *it != this ) { ( *it )->m_soloModel.setValue( false ); @@ -2657,7 +2668,12 @@ void Track::toggleSolo() } else if( !soloBefore ) { - ( *it )->setMuted( ( *it )->m_mutedBeforeSolo ); + // Unless we are on the sololegacybehavior mode, only restores the + // mute state if the track isn't an Automation Track + if( soloLegacyBehavior || ( *it )->type() != AutomationTrack ) + { + ( *it )->setMuted( ( *it )->m_mutedBeforeSolo ); + } } } } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index b1d7e5aadb4..e542039c50e 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -102,6 +102,8 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : "ui", "oneinstrumenttrackwindow").toInt()), m_sideBarOnRight(ConfigManager::inst()->value( "ui", "sidebaronright").toInt()), + m_soloLegacyBehavior(ConfigManager::inst()->value( + "app", "sololegacybehavior", "0").toInt()), m_MMPZ(!ConfigManager::inst()->value( "app", "nommpz").toInt()), m_disableBackup(!ConfigManager::inst()->value( @@ -233,6 +235,8 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); addLedCheckBox("Show sidebar on the right-hand side", gui_tw, counter, m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true); + addLedCheckBox("Mute automation tracks during solo", gui_tw, counter, + m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false); gui_tw->setFixedHeight(YDelta + YDelta * counter); @@ -905,6 +909,8 @@ void SetupDialog::accept() QString::number(m_oneInstrumentTrackWindow)); ConfigManager::inst()->setValue("ui", "sidebaronright", QString::number(m_sideBarOnRight)); + ConfigManager::inst()->setValue("app", "sololegacybehavior", + QString::number(m_soloLegacyBehavior)); ConfigManager::inst()->setValue("app", "nommpz", QString::number(!m_MMPZ)); ConfigManager::inst()->setValue("app", "disablebackup", @@ -1041,6 +1047,10 @@ void SetupDialog::setLanguage(int lang) } +void SetupDialog::toggleSoloLegacyBehavior(bool enabled) +{ + m_soloLegacyBehavior = enabled; +} // Performance settings slots. From b0333b6281979c358557e7e4c2695699fb8cb6f8 Mon Sep 17 00:00:00 2001 From: pmerry96 <60325403+pmerry96@users.noreply.github.com> Date: Wed, 5 Aug 2020 03:49:23 -0400 Subject: [PATCH 060/180] RemoteVstPlugin32: Fix Qt version check with MSVC 2019 + Qt 5.15 (#5607) --- plugins/vst_base/RemoteVstPlugin32.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index cba9a26c8ab..466752aa5da 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -20,7 +20,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) IF(NOT QT_32_PREFIX) SET(LMMS_MSVC_YEAR_FOR_QT ${LMMS_MSVC_YEAR}) - if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019) + if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019 AND Qt5_VERSION VERSION_LESS "5.15") SET(LMMS_MSVC_YEAR_FOR_QT 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible endif() From df296b7931420661cf62ae1be2b4a37b46edf40d Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Thu, 6 Aug 2020 18:36:54 +0200 Subject: [PATCH 061/180] Fix metronome playing when song is paused. (#5612) --- src/core/Mixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 476d54fef25..29bc725b6b6 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -357,7 +357,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer() currentPlayMode == Song::Mode_PlayBB; if( playModeSupportsMetronome && m_metronomeActive && !song->isExporting() && - p != last_metro_pos && + !song->isPaused() && p != last_metro_pos && // Stop crash with metronome if empty project Engine::getSong()->countTracks() ) { From 7a9b33627d8fb32253f4f6770a2bd793e2a5967c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 9 Aug 2020 22:59:37 +0200 Subject: [PATCH 062/180] Implement Lv2 Urid feature (#5517) This includes implementing general feature handling, since this is the first supported feature. --- include/Lv2Features.h | 81 +++++++++++++++++++++++++++++ include/Lv2Manager.h | 23 +++++++++ include/Lv2Proc.h | 4 ++ include/Lv2UridMap.h | 69 +++++++++++++++++++++++++ src/core/CMakeLists.txt | 2 + src/core/lv2/Lv2Features.cpp | 97 +++++++++++++++++++++++++++++++++++ src/core/lv2/Lv2Manager.cpp | 20 ++++++++ src/core/lv2/Lv2Proc.cpp | 25 +++++++-- src/core/lv2/Lv2UridMap.cpp | 99 ++++++++++++++++++++++++++++++++++++ 9 files changed, 417 insertions(+), 3 deletions(-) create mode 100644 include/Lv2Features.h create mode 100644 include/Lv2UridMap.h create mode 100644 src/core/lv2/Lv2Features.cpp create mode 100644 src/core/lv2/Lv2UridMap.cpp diff --git a/include/Lv2Features.h b/include/Lv2Features.h new file mode 100644 index 00000000000..f036c6d1f67 --- /dev/null +++ b/include/Lv2Features.h @@ -0,0 +1,81 @@ +/* + * Lv2Features.h - Lv2Features class + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2FEATURES_H +#define LV2FEATURES_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include "Lv2Manager.h" + +/** + Feature container + + References all available features for a plugin and maps them to their URIs. + + The public member functions should be called in descending order: + + 1. initCommon: map plugin-common features + 2. operator[]: map plugin-specific features + 3. createFeatureVectors: create the feature vectors required for + lilv_plugin_instantiate + 4. access the latter +*/ +class Lv2Features +{ +public: + //! Return if a feature is supported by LMMS + static bool isFeatureSupported(const char *featName); + + Lv2Features(); + + //! Register only plugin-common features + void initCommon(); + //! Return reference to feature data with given URI featName + void*& operator[](const char* featName); + //! Fill m_features and m_featurePointers with all features + void createFeatureVectors(); + //! Return LV2_Feature pointer vector, suited for lilv_plugin_instantiate + const LV2_Feature* const* featurePointers() const + { + return m_featurePointers.data(); + } + +private: + //! feature storage + std::vector m_features; + //! pointers to m_features, required for lilv_plugin_instantiate + std::vector m_featurePointers; + //! features + data, ordered by URI + std::map m_featureByUri; +}; + +#endif // LMMS_HAVE_LV2 + +#endif // LV2FEATURES_H diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 8437715619e..0b5fc692354 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -30,9 +30,11 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include "Lv2Basics.h" +#include "Lv2UridMap.h" #include "Plugin.h" @@ -114,10 +116,31 @@ class Lv2Manager Iterator begin() { return m_lv2InfoMap.begin(); } Iterator end() { return m_lv2InfoMap.end(); } + //! strcmp based key comparator for std::set and std::map + struct CmpStr + { + bool operator()(char const *a, char const *b) const; + }; + + UridMap& uridMap() { return m_uridMap; } + //! Return all + const std::set& supportedFeatureURIs() const + { + return m_supportedFeatureURIs; + } + bool isFeatureSupported(const char* featName) const; + private: + // general data bool m_debug; //!< if set, debug output will be printed LilvWorld* m_world; Lv2InfoMap m_lv2InfoMap; + std::set m_supportedFeatureURIs; + + // feature data that are common for all Lv2Proc + UridMap m_uridMap; + + // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index fb1f3466634..1c1cc11d8ac 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -34,6 +34,7 @@ #include #include "Lv2Basics.h" +#include "Lv2Features.h" #include "LinkedModelGroups.h" #include "Plugin.h" #include "PluginIssue.h" @@ -156,6 +157,7 @@ class Lv2Proc : public LinkedModelGroup const LilvPlugin* m_plugin; LilvInstance* m_instance; + Lv2Features m_features; std::vector> m_ports; StereoPortRef m_inPorts, m_outPorts; @@ -163,6 +165,8 @@ class Lv2Proc : public LinkedModelGroup //! models for the controls, sorted by port symbols std::map m_connectedModels; + void initPluginSpecificFeatures(); + //! load a file in the plugin, but don't do anything in LMMS void loadFileInternal(const QString &file); //! allocate m_ports, fill all with metadata, and assign meaning of ports diff --git a/include/Lv2UridMap.h b/include/Lv2UridMap.h new file mode 100644 index 00000000000..f1ddb6253b7 --- /dev/null +++ b/include/Lv2UridMap.h @@ -0,0 +1,69 @@ +/* + * Lv2UridMap.cpp - Lv2UridMap class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2URIDMAP_H +#define LV2URIDMAP_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include // TODO: use semaphore, even though this is not realtime critical +#include +#include + +/** + * Complete implementation of the Lv2 Urid Map extension + */ +class UridMap +{ + std::unordered_map m_map; + std::vector m_unMap; + + //! mutex for both m_map and m_unMap + //! the URID map is global, which is why a mutex is required here + std::mutex m_MapMutex; + + LV2_URID_Map m_mapFeature; + LV2_URID_Unmap m_unmapFeature; + + LV2_URID m_lastUrid = 0; + +public: + //! constructor; will set up the features + UridMap(); + + //! map feature function + LV2_URID map(const char* uri); + //! unmap feature function + const char* unmap(LV2_URID urid); + + // access the features + LV2_URID_Map* mapFeature() { return &m_mapFeature; } + LV2_URID_Unmap* unmapFeature() { return &m_unmapFeature; } +}; + +#endif // LMMS_HAVE_LV2 +#endif // LV2URIDMAP_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 730791bf770..b7685522c24 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -93,10 +93,12 @@ set(LMMS_SRCS core/lv2/Lv2Basics.cpp core/lv2/Lv2ControlBase.cpp + core/lv2/Lv2Features.cpp core/lv2/Lv2Ports.cpp core/lv2/Lv2Proc.cpp core/lv2/Lv2Manager.cpp core/lv2/Lv2SubPluginFeatures.cpp + core/lv2/Lv2UridMap.cpp core/midi/MidiAlsaRaw.cpp core/midi/MidiAlsaSeq.cpp diff --git a/src/core/lv2/Lv2Features.cpp b/src/core/lv2/Lv2Features.cpp new file mode 100644 index 00000000000..fe668807e06 --- /dev/null +++ b/src/core/lv2/Lv2Features.cpp @@ -0,0 +1,97 @@ +/* + * Lv2Features.cpp - Lv2Features implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Features.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "Engine.h" +#include "Lv2Manager.h" + + +bool Lv2Features::isFeatureSupported(const char* featName) +{ + return Engine::getLv2Manager()->isFeatureSupported(featName); +} + + + + +Lv2Features::Lv2Features() +{ + const Lv2Manager* man = Engine::getLv2Manager(); + // create (yet empty) map feature URI -> feature + for(const char* uri : man->supportedFeatureURIs()) + { + m_featureByUri.emplace(uri, nullptr); + } +} + + + + +void Lv2Features::initCommon() +{ + Lv2Manager* man = Engine::getLv2Manager(); + // init m_featureByUri with the plugin-common features + operator[](LV2_URID__map) = man->uridMap().mapFeature(); + operator[](LV2_URID__unmap) = man->uridMap().unmapFeature(); +} + + + + +void Lv2Features::createFeatureVectors() +{ + // create vector of features + for(std::pair& pr : m_featureByUri) + { + Q_ASSERT(pr.second != nullptr); + m_features.push_back(LV2_Feature { pr.first, pr.second }); + } + + // create pointer vector (for lilv_plugin_instantiate) + m_featurePointers.reserve(m_features.size() + 1); + for(std::size_t i = 0; i < m_features.size(); ++i) + { + m_featurePointers.push_back(&m_features[i]); + } + m_featurePointers.push_back(nullptr); +} + + + + +void *&Lv2Features::operator[](const char *featName) +{ + auto itr = m_featureByUri.find(featName); + Q_ASSERT(itr != m_featureByUri.end()); + return itr->second; +} + + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index bce3bf372ca..69fbd0137c5 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -27,6 +27,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include #include @@ -50,6 +51,9 @@ Lv2Manager::Lv2Manager() m_world = lilv_world_new(); lilv_world_load_all(m_world); + + m_supportedFeatureURIs.insert(LV2_URID__map); + m_supportedFeatureURIs.insert(LV2_URID__unmap); } @@ -133,6 +137,22 @@ void Lv2Manager::initPlugins() +bool Lv2Manager::CmpStr::operator()(const char *a, const char *b) const +{ + return std::strcmp(a, b) < 0; +} + + + + +bool Lv2Manager::isFeatureSupported(const char *featName) const +{ + return m_supportedFeatureURIs.find(featName) != m_supportedFeatureURIs.end(); +} + + + + // unused + untested yet bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 2d77f3f9835..86235f145b2 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -31,6 +31,7 @@ #include "AutomatableModel.h" #include "ComboBoxModel.h" #include "Engine.h" +#include "Lv2Features.h" #include "Lv2Manager.h" #include "Lv2Ports.h" #include "Mixer.h" @@ -74,8 +75,12 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); LILV_FOREACH (nodes, itr, reqFeats.get()) { - issues.emplace_back(featureNotSupported, - lilv_node_as_string(lilv_nodes_get(reqFeats.get(), itr))); + const char* reqFeatName = lilv_node_as_string( + lilv_nodes_get(reqFeats.get(), itr)); + if(!Lv2Features::isFeatureSupported(reqFeatName)) + { + issues.emplace_back(featureNotSupported, reqFeatName); + } } if (printIssues && issues.size()) @@ -240,11 +245,15 @@ AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) void Lv2Proc::initPlugin() { + m_features.initCommon(); + initPluginSpecificFeatures(); + m_features.createFeatureVectors(); + createPorts(); m_instance = lilv_plugin_instantiate(m_plugin, Engine::mixer()->processingSampleRate(), - nullptr); + m_features.featurePointers()); if (m_instance) { @@ -276,6 +285,16 @@ void Lv2Proc::shutdownPlugin() +void Lv2Proc::initPluginSpecificFeatures() +{ + // nothing yet + // it would look like this: + // m_features[LV2_URID__map] = m_uridMapFeature +} + + + + void Lv2Proc::loadFileInternal(const QString &file) { (void)file; diff --git a/src/core/lv2/Lv2UridMap.cpp b/src/core/lv2/Lv2UridMap.cpp new file mode 100644 index 00000000000..7e4fa864f1e --- /dev/null +++ b/src/core/lv2/Lv2UridMap.cpp @@ -0,0 +1,99 @@ +/* + * Lv2UridMap.cpp - Lv2UridMap implementation + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "Lv2UridMap.h" + +#ifdef LMMS_HAVE_LV2 + +static LV2_URID staticMap(LV2_URID_Map_Handle handle, const char* uri) +{ + UridMap* map = static_cast(handle); + return map->map(uri); +} + +static const char* staticUnmap(LV2_URID_Unmap_Handle handle, LV2_URID urid) +{ + UridMap* map = static_cast(handle); + return map->unmap(urid); +} + +UridMap::UridMap() +{ + m_mapFeature.handle = static_cast(this); + m_mapFeature.map = staticMap; + m_unmapFeature.handle = static_cast(this); + m_unmapFeature.unmap = staticUnmap; +} + +LV2_URID UridMap::map(const char *uri) +{ + LV2_URID result = 0u; + + // the Lv2 docs say that 0 should be returned in any case + // where creating an ID for the given URI fails + try + { + // TODO: + // when using C++14, we can get around any string allocation + // in the case the URI is already inside the map: + // * use `m_map.find(uri)` instead of `m_map.find(uriStr)` + // * to avoid temporary string construction in the `find` call, create + // m_map like this: + // std::unordered_map, std::equal<>> m_map; + // * move the try block inside the case where the URI is not in the map + const std::string uriStr = uri; + + std::lock_guard guard (m_MapMutex); + + auto itr = m_map.find(uriStr); + if (itr == m_map.end()) + { + // 1 is the first free URID + std::size_t index = 1u + m_unMap.size(); + auto pr = m_map.emplace(std::move(uriStr), index); + if (pr.second) + { + m_unMap.emplace_back(pr.first->first.c_str()); + result = static_cast(index); + } + } + else { result = itr->second; } + } + catch(...) { /* result variable is already 0 */ } + + return result; +} + +const char *UridMap::unmap(LV2_URID urid) +{ + std::size_t idx = static_cast(urid) - 1; + + std::lock_guard guard (m_MapMutex); + return (idx < m_unMap.size()) ? m_unMap[idx] : nullptr; +} + +#endif // LMMS_HAVE_LV2 + From ef961e53de0cc7beac0175c17ec8316cb0890d6e Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Sun, 9 Aug 2020 18:01:35 -0500 Subject: [PATCH 063/180] Refactor PianoRoll (#5253) * Rework PianoRoll paintEvent + some extras * Split out PositionLine class to own file * Refactor PianoRoll Q_PROPERTYs * Reduce code by using Q_PROPERTY's MEMBER function and removing getter/setter functions After looking at the getters and setters, they did nothing different than what direct access would allow. Nothing outside of PianoRoll used the public functions as well. Considering these factors we can reduce the number of functions by 2x the number of Q_PROPERTIES, and go with direct access instead. * Remove need for keyboard pixmaps With the recent change to allow zooming vertically, aligning pixmaps is a PITA. Since we have themes which can take brushes and colors, it would be simpler to take a solid color or a gradient with some extra style properties to resize the keys and text colors. While it will slightly be a downgrade from pixmaps since they can be anything really, this will allow us to customize the piano roll further moving forward. * Added the ability to update margins for TimeLineWidget and StepRecorderWidget These take a X coordinate, which was hardcoded to WHITE_KEY_WIDTH, and never looked back. Now we can adjust on the fly if we need to. Currently this just allows us to shift the left margin to the style-defined white key width. * Fix phantom pixmaps when PianoRoll not focused * Update PositionLine class changes related to #5543 --- data/themes/classic/style.css | 13 +- data/themes/default/style.css | 13 +- include/PianoRoll.h | 121 ++- include/PositionLine.h | 49 ++ include/SongEditor.h | 28 +- include/StepRecorderWidget.h | 2 + include/TimeLineWidget.h | 2 + src/gui/CMakeLists.txt | 1 + src/gui/TimeLineWidget.cpp | 8 + src/gui/editors/PianoRoll.cpp | 1070 +++++++++++------------- src/gui/editors/SongEditor.cpp | 82 +- src/gui/widgets/PositionLine.cpp | 98 +++ src/gui/widgets/StepRecorderWidget.cpp | 13 + 13 files changed, 732 insertions(+), 768 deletions(-) create mode 100644 include/PositionLine.h create mode 100644 src/gui/widgets/PositionLine.cpp diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 6ae93b49856..3e64c9da5a9 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -118,7 +118,7 @@ QMenu::indicator:selected { background-color: #747474; } -positionLine { +PositionLine { qproperty-tailGradient: false; qproperty-lineColor: rgb(255, 255, 255); } @@ -138,6 +138,17 @@ PianoRoll { qproperty-ghostNoteBorders: true; qproperty-barColor: #4afd85; qproperty-markedSemitoneColor: rgba( 0, 255, 200, 60 ); + /* Piano keys */ + qproperty-whiteKeyWidth: 64; + qproperty-whiteKeyActiveTextColor: #000; + qproperty-whiteKeyActiveTextShadow: rgb( 240, 240, 240 ); + qproperty-whiteKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-whiteKeyInactiveTextColor: rgb( 128, 128, 128); + qproperty-whiteKeyInactiveTextShadow: rgb( 240, 240, 240 ); + qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff); + qproperty-blackKeyWidth: 48; + qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000); /* Grid colors */ qproperty-lineColor: rgba( 128, 128, 128, 80 ); qproperty-beatLineColor: rgba( 128, 128, 128, 160 ); diff --git a/data/themes/default/style.css b/data/themes/default/style.css index fc6495921a9..4dee86788ec 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -150,7 +150,7 @@ QMenu::indicator:selected { background-color: #101213; } -positionLine { +PositionLine { qproperty-tailGradient: true; qproperty-lineColor: rgb(255, 255, 255); } @@ -170,6 +170,17 @@ PianoRoll { qproperty-ghostNoteBorders: false; qproperty-barColor: #078f3a; qproperty-markedSemitoneColor: rgba(255, 255, 255, 30); + /* Piano keys */ + qproperty-whiteKeyWidth: 64; + qproperty-whiteKeyActiveTextColor: #000; + qproperty-whiteKeyActiveTextShadow: #fff; + qproperty-whiteKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-whiteKeyInactiveTextColor: #000; + qproperty-whiteKeyInactiveTextShadow: #fff; + qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff); + qproperty-blackKeyWidth: 48; + qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000); /* Grid colors */ qproperty-lineColor: #292929; qproperty-beatLineColor: #2d6b45; diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 86a65050c1d..7a99e7b1f80 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -40,6 +40,7 @@ #include "ToolTip.h" #include "StepRecorder.h" #include "StepRecorderWidget.h" +#include "PositionLine.h" class QPainter; class QPixmap; @@ -55,25 +56,38 @@ class TimeLineWidget; class PianoRoll : public QWidget { Q_OBJECT - Q_PROPERTY( QColor barLineColor READ barLineColor WRITE setBarLineColor ) - Q_PROPERTY( QColor beatLineColor READ beatLineColor WRITE setBeatLineColor ) - Q_PROPERTY( QColor lineColor READ lineColor WRITE setLineColor ) - Q_PROPERTY( QColor noteModeColor READ noteModeColor WRITE setNoteModeColor ) - Q_PROPERTY( QColor noteColor READ noteColor WRITE setNoteColor ) - Q_PROPERTY( QColor ghostNoteColor READ ghostNoteColor WRITE setGhostNoteColor ) - Q_PROPERTY( QColor noteTextColor READ noteTextColor WRITE setNoteTextColor ) - Q_PROPERTY( QColor ghostNoteTextColor READ ghostNoteTextColor WRITE setGhostNoteTextColor ) - Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor ) - Q_PROPERTY( QColor selectedNoteColor READ selectedNoteColor WRITE setSelectedNoteColor ) - Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) - Q_PROPERTY( QColor textColorLight READ textColorLight WRITE setTextColorLight ) - Q_PROPERTY( QColor textShadow READ textShadow WRITE setTextShadow ) - Q_PROPERTY( QColor markedSemitoneColor READ markedSemitoneColor WRITE setMarkedSemitoneColor ) - Q_PROPERTY( int noteOpacity READ noteOpacity WRITE setNoteOpacity ) - Q_PROPERTY( bool noteBorders READ noteBorders WRITE setNoteBorders ) - Q_PROPERTY( int ghostNoteOpacity READ ghostNoteOpacity WRITE setGhostNoteOpacity ) - Q_PROPERTY( bool ghostNoteBorders READ ghostNoteBorders WRITE setGhostNoteBorders ) - Q_PROPERTY( QColor backgroundShade READ backgroundShade WRITE setBackgroundShade ) + Q_PROPERTY(QColor barLineColor MEMBER m_barLineColor) + Q_PROPERTY(QColor beatLineColor MEMBER m_beatLineColor) + Q_PROPERTY(QColor lineColor MEMBER m_lineColor) + Q_PROPERTY(QColor noteModeColor MEMBER m_noteModeColor) + Q_PROPERTY(QColor noteColor MEMBER m_noteColor) + Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor) + Q_PROPERTY(QColor noteTextColor MEMBER m_noteTextColor) + Q_PROPERTY(QColor ghostNoteTextColor MEMBER m_ghostNoteTextColor) + Q_PROPERTY(QColor barColor MEMBER m_barColor) + Q_PROPERTY(QColor selectedNoteColor MEMBER m_selectedNoteColor) + Q_PROPERTY(QColor textColor MEMBER m_textColor) + Q_PROPERTY(QColor textColorLight MEMBER m_textColorLight) + Q_PROPERTY(QColor textShadow MEMBER m_textShadow) + Q_PROPERTY(QColor markedSemitoneColor MEMBER m_markedSemitoneColor) + Q_PROPERTY(int noteOpacity MEMBER m_noteOpacity) + Q_PROPERTY(bool noteBorders MEMBER m_noteBorders) + Q_PROPERTY(int ghostNoteOpacity MEMBER m_ghostNoteOpacity) + Q_PROPERTY(bool ghostNoteBorders MEMBER m_ghostNoteBorders) + Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade) + + /* white key properties */ + Q_PROPERTY(int whiteKeyWidth MEMBER m_whiteKeyWidth) + Q_PROPERTY(QColor whiteKeyInactiveTextColor MEMBER m_whiteKeyInactiveTextColor) + Q_PROPERTY(QColor whiteKeyInactiveTextShadow MEMBER m_whiteKeyInactiveTextShadow) + Q_PROPERTY(QBrush whiteKeyInactiveBackground MEMBER m_whiteKeyInactiveBackground) + Q_PROPERTY(QColor whiteKeyActiveTextColor MEMBER m_whiteKeyActiveTextColor) + Q_PROPERTY(QColor whiteKeyActiveTextShadow MEMBER m_whiteKeyActiveTextShadow) + Q_PROPERTY(QBrush whiteKeyActiveBackground MEMBER m_whiteKeyActiveBackground) + /* black key properties */ + Q_PROPERTY(int blackKeyWidth MEMBER m_blackKeyWidth) + Q_PROPERTY(QBrush blackKeyInactiveBackground MEMBER m_blackKeyInactiveBackground) + Q_PROPERTY(QBrush blackKeyActiveBackground MEMBER m_blackKeyActiveBackground) public: enum EditModes { @@ -125,47 +139,6 @@ class PianoRoll : public QWidget int quantization() const; - // qproperty access functions - QColor barLineColor() const; - void setBarLineColor( const QColor & c ); - QColor beatLineColor() const; - void setBeatLineColor( const QColor & c ); - QColor lineColor() const; - void setLineColor( const QColor & c ); - QColor noteModeColor() const; - void setNoteModeColor( const QColor & c ); - QColor noteColor() const; - void setNoteColor( const QColor & c ); - QColor noteTextColor() const; - void setNoteTextColor( const QColor & c ); - QColor barColor() const; - void setBarColor( const QColor & c ); - QColor selectedNoteColor() const; - void setSelectedNoteColor( const QColor & c ); - QColor textColor() const; - void setTextColor( const QColor & c ); - QColor textColorLight() const; - void setTextColorLight( const QColor & c ); - QColor textShadow() const; - void setTextShadow( const QColor & c ); - QColor markedSemitoneColor() const; - void setMarkedSemitoneColor( const QColor & c ); - int noteOpacity() const; - void setNoteOpacity( const int i ); - bool noteBorders() const; - void setNoteBorders( const bool b ); - QColor ghostNoteColor() const; - void setGhostNoteColor( const QColor & c ); - QColor ghostNoteTextColor() const; - void setGhostNoteTextColor( const QColor & c ); - int ghostNoteOpacity() const; - void setGhostNoteOpacity( const int i ); - bool ghostNoteBorders() const; - void setGhostNoteBorders( const bool b ); - QColor backgroundShade() const; - void setBackgroundShade( const QColor & c ); - - protected: void keyPressEvent( QKeyEvent * ke ) override; void keyReleaseEvent( QKeyEvent * ke ) override; @@ -188,7 +161,6 @@ class PianoRoll : public QWidget void selectAll(); NoteVector getSelectedNotes() const; void selectNotesOnKey(); - int xCoordOfTick( int tick ); // for entering values with dblclick in the vol/pan bars void enterValue( NoteVector* nv ); @@ -279,6 +251,8 @@ protected slots: PR_BLACK_KEY }; + PositionLine * m_positionLine; + QVector m_nemStr; // gui names of each edit mode QMenu * m_noteEditMenu; // when you right click below the key area @@ -306,6 +280,9 @@ protected slots: void playChordNotes(int key, int velocity=-1); void pauseChordNotes(int key); + void updateScrollbars(); + void updatePositionLineHeight(); + QList getAllOctavesForKey( int keyToMirror ) const; int noteEditTop() const; @@ -320,12 +297,6 @@ protected slots: static const int cm_scrollAmtHoriz = 10; static const int cm_scrollAmtVert = 1; - static QPixmap * s_whiteKeyBigPm; - static QPixmap * s_whiteKeyBigPressedPm; - static QPixmap * s_whiteKeySmallPm; - static QPixmap * s_whiteKeySmallPressedPm; - static QPixmap * s_blackKeyPm; - static QPixmap * s_blackKeyPressedPm; static QPixmap * s_toolDraw; static QPixmap * s_toolErase; static QPixmap * s_toolSelect; @@ -389,10 +360,11 @@ protected slots: int m_moveStartX; int m_moveStartY; - int m_oldNotesEditHeight; int m_notesEditHeight; + int m_userSetNotesEditHeight; int m_ppb; // pixels per bar int m_totalKeysToScroll; + int m_pianoKeysVisible; int m_keyLineHeight; int m_octaveHeight; @@ -458,6 +430,18 @@ protected slots: bool m_noteBorders; bool m_ghostNoteBorders; QColor m_backgroundShade; + /* white key properties */ + int m_whiteKeyWidth; + QColor m_whiteKeyActiveTextColor; + QColor m_whiteKeyActiveTextShadow; + QBrush m_whiteKeyActiveBackground; + QColor m_whiteKeyInactiveTextColor; + QColor m_whiteKeyInactiveTextShadow; + QBrush m_whiteKeyInactiveBackground; + /* black key properties */ + int m_blackKeyWidth; + QBrush m_blackKeyActiveBackground; + QBrush m_blackKeyInactiveBackground; signals: void positionChanged( const MidiTime & ); @@ -501,6 +485,7 @@ class PianoRollWindow : public Editor, SerializingObject } QSize sizeHint() const override; + bool hasFocus() const; signals: void currentPatternChanged(); diff --git a/include/PositionLine.h b/include/PositionLine.h new file mode 100644 index 00000000000..d48fd3df1cb --- /dev/null +++ b/include/PositionLine.h @@ -0,0 +1,49 @@ +/* + * PositionLine.h - declaration of class PositionLine, a simple widget that + * draws a line, mainly works with TimeLineWidget + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef POSITION_LINE_H +#define POSITION_LINE_H + +#include + +class PositionLine : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool tailGradient MEMBER m_hasTailGradient) + Q_PROPERTY(QColor lineColor MEMBER m_lineColor) +public: + PositionLine(QWidget* parent); + +public slots: + void zoomChange(double zoom); + +private: + void paintEvent(QPaintEvent* pe) override; + + bool m_hasTailGradient; + QColor m_lineColor; +}; + +#endif \ No newline at end of file diff --git a/include/SongEditor.h b/include/SongEditor.h index c4c25d7d0c9..7e0fe986a8c 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -33,6 +33,7 @@ #include "ActionGroup.h" #include "Editor.h" #include "TrackContainerView.h" +#include "PositionLine.h" class QLabel; class QScrollBar; @@ -46,31 +47,6 @@ class Song; class TextFloat; class TimeLineWidget; -class positionLine : public QWidget -{ - Q_OBJECT - Q_PROPERTY ( bool tailGradient READ hasTailGradient WRITE setHasTailGradient ) - Q_PROPERTY ( QColor lineColor READ lineColor WRITE setLineColor ) -public: - positionLine ( QWidget* parent ); - - // qproperty access functions - bool hasTailGradient () const; - void setHasTailGradient ( const bool g ); - QColor lineColor () const; - void setLineColor ( const QColor & c ); - -public slots: - void zoomChange (double zoom); - -private: - void paintEvent( QPaintEvent* pe ) override; - - bool m_hasTailGradient; - QColor m_lineColor; - -}; - class SongEditor : public TrackContainerView { @@ -156,7 +132,7 @@ private slots: TextFloat * m_mvsStatus; TextFloat * m_mpsStatus; - positionLine * m_positionLine; + PositionLine * m_positionLine; ComboBoxModel* m_zoomingModel; ComboBoxModel* m_snappingModel; diff --git a/include/StepRecorderWidget.h b/include/StepRecorderWidget.h index 14cfc2eedce..67f2a4b6547 100644 --- a/include/StepRecorderWidget.h +++ b/include/StepRecorderWidget.h @@ -45,7 +45,9 @@ class StepRecorderWidget : public QWidget //API used by PianoRoll void setPixelsPerBar(int ppb); void setCurrentPosition(MidiTime currentPosition); + void setMargins(const QMargins &qm); void setBottomMargin(const int marginBottom); + QMargins margins(); //API used by StepRecorder void setStepsLength(MidiTime stepsLength); diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 5b33bd98987..8a9dc5044ea 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -150,6 +150,8 @@ class TimeLineWidget : public QWidget, public JournallingObject update(); } + void setXOffset(const int x); + void addToolButtons(QToolBar* _tool_bar ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 81e588c6659..3c6529e0b78 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -81,6 +81,7 @@ SET(LMMS_SRCS gui/widgets/NStateButton.cpp gui/widgets/Oscilloscope.cpp gui/widgets/PixmapButton.cpp + gui/widgets/PositionLine.cpp gui/widgets/ProjectNotes.cpp gui/widgets/RenameDialog.cpp gui/widgets/Rubberband.cpp diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index 8e79410b853..070ac0a40d1 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -109,6 +109,14 @@ TimeLineWidget::~TimeLineWidget() +void TimeLineWidget::setXOffset(const int x) +{ + m_xOffset = x; + if (s_posMarkerPixmap != nullptr) { m_xOffset -= s_posMarkerPixmap->width() / 2; } +} + + + void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index b519fb9e5e1..3d35c9c5133 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #ifndef __USE_XOPEN #define __USE_XOPEN @@ -72,7 +73,7 @@ typedef AutomationPattern::timeMap timeMap; // some constants... const int INITIAL_PIANOROLL_WIDTH = 860; -const int INITIAL_PIANOROLL_HEIGHT = 480; +const int INITIAL_PIANOROLL_HEIGHT = 485; const int SCROLLBAR_SIZE = 12; const int PIANO_X = 0; @@ -86,9 +87,9 @@ const int DEFAULT_CELL_WIDTH = 12; const int NOTE_EDIT_RESIZE_BAR = 6; const int NOTE_EDIT_MIN_HEIGHT = 50; -const int KEY_AREA_MIN_HEIGHT = 100; +const int KEY_AREA_MIN_HEIGHT = DEFAULT_KEY_LINE_HEIGHT * 10; const int PR_BOTTOM_MARGIN = SCROLLBAR_SIZE; -const int PR_TOP_MARGIN = 16; +const int PR_TOP_MARGIN = 18; const int PR_RIGHT_MARGIN = SCROLLBAR_SIZE; @@ -107,12 +108,6 @@ const int NUM_TRIPLET_LENGTHS = 5; -QPixmap * PianoRoll::s_whiteKeySmallPm = NULL; -QPixmap * PianoRoll::s_whiteKeySmallPressedPm = NULL; -QPixmap * PianoRoll::s_whiteKeyBigPm = NULL; -QPixmap * PianoRoll::s_whiteKeyBigPressedPm = NULL; -QPixmap * PianoRoll::s_blackKeyPm = NULL; -QPixmap * PianoRoll::s_blackKeyPressedPm = NULL; QPixmap * PianoRoll::s_toolDraw = NULL; QPixmap * PianoRoll::s_toolErase = NULL; QPixmap * PianoRoll::s_toolSelect = NULL; @@ -170,14 +165,14 @@ PianoRoll::PianoRoll() : m_mouseDownTick( 0 ), m_lastMouseX( 0 ), m_lastMouseY( 0 ), - m_oldNotesEditHeight( 100 ), m_notesEditHeight( 100 ), + m_userSetNotesEditHeight(100), m_ppb( DEFAULT_PR_PPB ), m_keyLineHeight(DEFAULT_KEY_LINE_HEIGHT), m_octaveHeight(m_keyLineHeight * KeysPerOctave), - m_whiteKeySmallHeight(round(m_keyLineHeight * 1.5)), + m_whiteKeySmallHeight(qFloor(m_keyLineHeight * 1.5)), m_whiteKeyBigHeight(m_keyLineHeight * 2), - m_blackKeyHeight(round(m_keyLineHeight * 1.3333)), + m_blackKeyHeight(m_keyLineHeight), m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), @@ -207,7 +202,9 @@ PianoRoll::PianoRoll() : m_ghostNoteOpacity( 255 ), m_noteBorders( true ), m_ghostNoteBorders( true ), - m_backgroundShade( 0, 0, 0 ) + m_backgroundShade( 0, 0, 0 ), + m_whiteKeyWidth(WHITE_KEY_WIDTH), + m_blackKeyWidth(BLACK_KEY_WIDTH) { // gui names of edit modes m_nemStr.push_back( tr( "Note Velocity" ) ); @@ -252,36 +249,6 @@ PianoRoll::PianoRoll() : m_semiToneMarkerMenu->addAction( copyAllNotesAction ); // init pixmaps - if( s_whiteKeySmallPm == NULL ) - { - s_whiteKeySmallPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_small" ) ); - } - if( s_whiteKeySmallPressedPm == NULL ) - { - s_whiteKeySmallPressedPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_small_pressed" ) ); - } - if( s_whiteKeyBigPm == NULL ) - { - s_whiteKeyBigPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_big" ) ); - } - if( s_whiteKeyBigPressedPm == NULL ) - { - s_whiteKeyBigPressedPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_big_pressed" ) ); - } - if( s_blackKeyPm == NULL ) - { - s_blackKeyPm = new QPixmap( embed::getIconPixmap( - "pr_black_key" ) ); - } - if( s_blackKeyPressedPm == NULL ) - { - s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( - "pr_black_key_pressed" ) ); - } if( s_toolDraw == NULL ) { s_toolDraw = new QPixmap( embed::getIconPixmap( "edit_draw" ) ); @@ -312,7 +279,7 @@ PianoRoll::PianoRoll() : setAttribute( Qt::WA_OpaquePaintEvent, true ); // add time-line - m_timeLine = new TimeLineWidget( WHITE_KEY_WIDTH, 0, m_ppb, + m_timeLine = new TimeLineWidget(m_whiteKeyWidth, 0, m_ppb, Engine::getSong()->getPlayPos( Song::Mode_PlayPattern ), m_currentPosition, @@ -322,6 +289,9 @@ PianoRoll::PianoRoll() : connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( updatePosition( const MidiTime & ) ) ); + // white position line follows timeline marker + m_positionLine = new PositionLine(this); + //update timeline when in step-recording mode connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( updatePositionStepRecording( const MidiTime & ) ) ); @@ -768,8 +738,8 @@ void PianoRoll::hidePattern( Pattern* pattern ) void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) { - xStart -= WHITE_KEY_WIDTH; - xEnd -= WHITE_KEY_WIDTH; + xStart -= m_whiteKeyWidth; + xEnd -= m_whiteKeyWidth; // select an area of notes int posTicks = xStart * MidiTime::ticksPerBar() / m_ppb + @@ -804,127 +774,6 @@ void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) - - -/** \brief qproperty access implementation */ - -QColor PianoRoll::barLineColor() const -{ return m_barLineColor; } - -void PianoRoll::setBarLineColor( const QColor & c ) -{ m_barLineColor = c; } - -QColor PianoRoll::beatLineColor() const -{ return m_beatLineColor; } - -void PianoRoll::setBeatLineColor( const QColor & c ) -{ m_beatLineColor = c; } - -QColor PianoRoll::lineColor() const -{ return m_lineColor; } - -void PianoRoll::setLineColor( const QColor & c ) -{ m_lineColor = c; } - -QColor PianoRoll::noteModeColor() const -{ return m_noteModeColor; } - -void PianoRoll::setNoteModeColor( const QColor & c ) -{ m_noteModeColor = c; } - -QColor PianoRoll::noteColor() const -{ return m_noteColor; } - -void PianoRoll::setNoteColor( const QColor & c ) -{ m_noteColor = c; } - -QColor PianoRoll::noteTextColor() const -{ return m_noteTextColor; } - -void PianoRoll::setNoteTextColor( const QColor & c ) -{ m_noteTextColor = c; } - -QColor PianoRoll::barColor() const -{ return m_barColor; } - -void PianoRoll::setBarColor( const QColor & c ) -{ m_barColor = c; } - -QColor PianoRoll::selectedNoteColor() const -{ return m_selectedNoteColor; } - -void PianoRoll::setSelectedNoteColor( const QColor & c ) -{ m_selectedNoteColor = c; } - -QColor PianoRoll::textColor() const -{ return m_textColor; } - -void PianoRoll::setTextColor( const QColor & c ) -{ m_textColor = c; } - -QColor PianoRoll::textColorLight() const -{ return m_textColorLight; } - -void PianoRoll::setTextColorLight( const QColor & c ) -{ m_textColorLight = c; } - -QColor PianoRoll::textShadow() const -{ return m_textShadow; } - -void PianoRoll::setTextShadow( const QColor & c ) -{ m_textShadow = c; } - -QColor PianoRoll::markedSemitoneColor() const -{ return m_markedSemitoneColor; } - -void PianoRoll::setMarkedSemitoneColor( const QColor & c ) -{ m_markedSemitoneColor = c; } - -int PianoRoll::noteOpacity() const -{ return m_noteOpacity; } - -void PianoRoll::setNoteOpacity( const int i ) -{ m_noteOpacity = i; } - -bool PianoRoll::noteBorders() const -{ return m_noteBorders; } - -void PianoRoll::setNoteBorders( const bool b ) -{ m_noteBorders = b; } - -QColor PianoRoll::ghostNoteColor() const -{ return m_ghostNoteColor; } - -void PianoRoll::setGhostNoteColor( const QColor & c ) -{ m_ghostNoteColor = c; } - -QColor PianoRoll::ghostNoteTextColor() const -{ return m_ghostNoteTextColor; } - -void PianoRoll::setGhostNoteTextColor( const QColor & c ) -{ m_ghostNoteTextColor = c; } - -int PianoRoll::ghostNoteOpacity() const -{ return m_ghostNoteOpacity; } - -void PianoRoll::setGhostNoteOpacity( const int i ) -{ m_ghostNoteOpacity = i; } - -bool PianoRoll::ghostNoteBorders() const -{ return m_ghostNoteBorders; } - -void PianoRoll::setGhostNoteBorders( const bool b ) -{ m_ghostNoteBorders = b; } - -QColor PianoRoll::backgroundShade() const -{ return m_backgroundShade; } - -void PianoRoll::setBackgroundShade( const QColor & c ) -{ m_backgroundShade = c; } - - - - void PianoRoll::drawNoteRect( QPainter & p, int x, int y, int width, const Note * n, const QColor & noteCol, const QColor & noteTextColor, const QColor & selCol, const int noteOpc, const bool borders, bool drawNoteName ) @@ -1039,9 +888,11 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, int _y ) const { int middle_y = _y + m_keyLineHeight / 2; - _p.setPen( noteColor() ); - _p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, + _p.setPen(m_noteColor); + _p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, keyAreaBottom() - PR_TOP_MARGIN); int old_x = 0; @@ -1449,8 +1300,7 @@ void PianoRoll::leaveEvent(QEvent * e ) int PianoRoll::noteEditTop() const { - return height() - PR_BOTTOM_MARGIN - - m_notesEditHeight + NOTE_EDIT_RESIZE_BAR; + return keyAreaBottom() + NOTE_EDIT_RESIZE_BAR; } @@ -1474,7 +1324,7 @@ int PianoRoll::noteEditRight() const int PianoRoll::noteEditLeft() const { - return WHITE_KEY_WIDTH; + return m_whiteKeyWidth; } @@ -1539,11 +1389,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_moveStartY = me->y(); } - if( me->y() > keyAreaBottom() && me->y() < noteEditTop() ) + if(me->button() == Qt::LeftButton && + me->y() > keyAreaBottom() && me->y() < noteEditTop()) { // resizing the note edit area m_action = ActionResizeNoteEditArea; - m_oldNotesEditHeight = m_notesEditHeight; return; } @@ -1556,11 +1406,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) int x = me->x(); - if( x > WHITE_KEY_WIDTH ) + if (x > m_whiteKeyWidth) { // set, move or resize note - x -= WHITE_KEY_WIDTH; + x -= m_whiteKeyWidth; // get tick in which the user clicked int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + @@ -1827,7 +1677,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) else if( me->buttons() == Qt::LeftButton ) { // left click - play the note - int v = ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity; + int v = ((float) x) / ((float) m_whiteKeyWidth) * MidiDefaultVelocity; m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num, v); // if a chord is set, play the chords notes as well: playChordNotes(key_num, v); @@ -1870,7 +1720,7 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) { // get values for going through notes int pixel_range = 4; - int x = me->x() - WHITE_KEY_WIDTH; + int x = me->x() - m_whiteKeyWidth; const int ticks_start = ( x-pixel_range/2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; const int ticks_end = ( x+pixel_range/2 ) * @@ -2203,14 +2053,23 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } else if( m_action == ActionResizeNoteEditArea ) { + // Don't try to show more keys than the full keyboard, bail if trying to + if (m_pianoKeysVisible == NumKeys && me->y() > m_moveStartY) + { + return; + } + int newHeight = height() - me->y(); + if (me->y() < KEY_AREA_MIN_HEIGHT) + { + newHeight = height() - KEY_AREA_MIN_HEIGHT - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN; // - NOTE_EDIT_RESIZE_BAR + } // change m_notesEditHeight and then repaint - m_notesEditHeight = qBound( - NOTE_EDIT_MIN_HEIGHT, - m_oldNotesEditHeight - ( me->y() - m_moveStartY ), - height() - PR_TOP_MARGIN - NOTE_EDIT_RESIZE_BAR - - PR_BOTTOM_MARGIN - KEY_AREA_MIN_HEIGHT ); - + m_notesEditHeight = qMax(NOTE_EDIT_MIN_HEIGHT, newHeight); + m_userSetNotesEditHeight = m_notesEditHeight; m_stepRecorderWidget.setBottomMargin(PR_BOTTOM_MARGIN + m_notesEditHeight); + updateScrollbars(); + updatePositionLineHeight(); repaint(); return; } @@ -2225,17 +2084,17 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) int x = me->x(); // see if they clicked on the keyboard on the left - if( x < WHITE_KEY_WIDTH && m_action == ActionNone + if (x < m_whiteKeyWidth && m_action == ActionNone && ! edit_note && key_num != m_lastKey && me->buttons() & Qt::LeftButton ) { // clicked on a key, play the note - testPlayKey( key_num, ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity, 0 ); + testPlayKey(key_num, ((float) x) / ((float) m_whiteKeyWidth) * MidiDefaultVelocity, 0); update(); return; } - x -= WHITE_KEY_WIDTH; + x -= m_whiteKeyWidth; if( me->buttons() & Qt::LeftButton && m_editMode == ModeDraw @@ -2508,12 +2367,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_action == ActionSelectNotes ) { - int x = me->x() - WHITE_KEY_WIDTH; + int x = me->x() - m_whiteKeyWidth; if( x < 0 && m_currentPosition > 0 ) { x = 0; QCursor::setPos( mapToGlobal( QPoint( - WHITE_KEY_WIDTH, + m_whiteKeyWidth, me->y() ) ) ); if( m_currentPosition >= 4 ) { @@ -2525,9 +2384,9 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_leftRightScroll->setValue( 0 ); } } - else if( x > width() - WHITE_KEY_WIDTH ) + else if (x > width() - m_whiteKeyWidth) { - x = width() - WHITE_KEY_WIDTH; + x = width() - m_whiteKeyWidth; QCursor::setPos( mapToGlobal( QPoint( width(), me->y() ) ) ); m_leftRightScroll->setValue( m_currentPosition + @@ -2774,11 +2633,8 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) Engine::getSong()->setModified(); } -int PianoRoll::xCoordOfTick( int tick ) -{ - return WHITE_KEY_WIDTH + ( ( tick - m_currentPosition ) - * m_ppb / MidiTime::ticksPerBar() ); -} + + void PianoRoll::paintEvent(QPaintEvent * pe ) { @@ -2794,353 +2650,337 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // fill with bg color p.fillRect( 0, 0, width(), height(), bgColor ); - // set font-size to 8 - p.setFont( pointSize<8>( p.font() ) ); + // set font-size to 80% of key line height + QFont f = p.font(); + f.setPixelSize(m_keyLineHeight * 0.8); + p.setFont(f); // font size doesn't change without this for some reason QFontMetrics fontMetrics(p.font()); - QRect const boundingRect = fontMetrics.boundingRect(QChar::fromLatin1('H')); - // This is two times of the y coordinate of the center of the bounding rectangle - // (-(top+bottom)=-2(center)) but labelHeight is more intuitive/describing name - int const labelHeight = - boundingRect.top() - boundingRect.bottom(); + // G4 is one of the widest + QRect const boundingRect = fontMetrics.boundingRect(QString("G4")); + + // Order of drawing + // - vertical quantization lines + // - piano roll + horizontal key lines + // - alternating bar colors + // - vertical beat lines + // - vertical bar lines + // - marked semitones + // - note editing + // - notes + // - selection frame + // - highlight hovered note + // - note edit area resize bar + // - cursor mode icon - // y_offset is used to align the piano-keys on the key-lines - int y_offset = 0; - - // calculate y_offset according to first key - switch( prKeyOrder[m_startKey % KeysPerOctave] ) + if (hasValidPattern()) { - case PR_BLACK_KEY: y_offset = m_keyLineHeight / 4; break; - case PR_WHITE_KEY_BIG: y_offset = m_keyLineHeight / 2; break; - case PR_WHITE_KEY_SMALL: - if( prKeyOrder[( ( m_startKey + 1 ) % - KeysPerOctave)] != PR_BLACK_KEY ) + int pianoAreaHeight, partialKeyVisible, topKey, topNote; + pianoAreaHeight = keyAreaBottom() - keyAreaTop(); + m_pianoKeysVisible = pianoAreaHeight / m_keyLineHeight; + partialKeyVisible = pianoAreaHeight % m_keyLineHeight; + // check if we're below the minimum key area size + if (m_pianoKeysVisible * m_keyLineHeight < KEY_AREA_MIN_HEIGHT) + { + m_pianoKeysVisible = KEY_AREA_MIN_HEIGHT / m_keyLineHeight; + partialKeyVisible = KEY_AREA_MIN_HEIGHT % m_keyLineHeight; + // if we have a partial key, just show it + if (partialKeyVisible > 0) { - y_offset = m_keyLineHeight / 2; + m_pianoKeysVisible += 1; + partialKeyVisible = 0; } - break; - } - // start drawing at the bottom - int key_line_y = qMin(keyAreaBottom() - 1, m_keyLineHeight * NumKeys); - // we need to set m_notesEditHeight here because it needs to fill in the - // rest of the window if key_line_y is bound to m_keyLineHeight * NumKeys - if (key_line_y == m_keyLineHeight * NumKeys) { - m_notesEditHeight = height() - (PR_TOP_MARGIN + m_keyLineHeight * NumKeys); - } - // used for aligning black-keys later - int first_white_key_height = m_whiteKeySmallHeight; - // key-counter - only needed for finding out whether the processed - // key is the first one - int keys_processed = 0; - - int key = m_startKey; - - // draw all white keys... - for( int y = key_line_y + 1 + y_offset; y > PR_TOP_MARGIN; - key_line_y -= m_keyLineHeight, ++keys_processed ) - { - // check for white key that is only half visible on the - // bottom of piano-roll - if( keys_processed == 0 && - prKeyOrder[m_startKey % KeysPerOctave] == - PR_BLACK_KEY ) + // have to modifiy the notes edit area height instead + m_notesEditHeight = height() - (m_pianoKeysVisible * m_keyLineHeight) + - PR_TOP_MARGIN - PR_BOTTOM_MARGIN; + } + // check if we're trying to show more keys than available + else if (m_pianoKeysVisible >= NumKeys) { - // draw it! - p.drawPixmap( PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPm ); - // update y-pos - y -= m_whiteKeySmallHeight / 2; - // move first black key down (we didn't draw whole - // white key so black key needs to be lifted down) - // (default for first_white_key_height = - // m_whiteKeySmallHeight, so m_whiteKeySmallHeight/2 - // is smaller) - first_white_key_height = m_whiteKeySmallHeight / 2; + m_pianoKeysVisible = NumKeys; + // have to modify the notes edit area height instead + m_notesEditHeight = height() - (NumKeys * m_keyLineHeight) - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN; + partialKeyVisible = 0; } - // check whether to draw a big or a small white key - if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_SMALL ) + topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); + topNote = topKey % KeysPerOctave; + // if not resizing the note edit area, we can change m_notesEditHeight + if (m_action != ActionResizeNoteEditArea && partialKeyVisible != 0) { - // draw a small one while checking if it is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) + // calculate the height change adding and subtracting the partial key + int noteAreaPlus = (m_notesEditHeight + partialKeyVisible) - m_userSetNotesEditHeight; + int noteAreaMinus = m_userSetNotesEditHeight - (m_notesEditHeight - partialKeyVisible); + // if adding the partial key to height is more distant from the set height + // we want to subtract the partial key + if (noteAreaPlus > noteAreaMinus) { - p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPressedPm); + m_notesEditHeight -= partialKeyVisible; + // since we're adding a partial key, we add one to the number visible + m_pianoKeysVisible += 1; } - else - { - p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPm); - } - // update y-pos - y -= m_whiteKeySmallHeight; + // otherwise we add height + else { m_notesEditHeight += partialKeyVisible; } + } + updatePositionLineHeight(); + int x, q = quantization(), tick; + // draw vertical quantization lines + // If we're over 100% zoom, we allow all quantization level grids + if (m_zoomingModel.value() <= 3) + { + // we're under 100% zoom + // allow quantization grid up to 1/24 for triplets + if (q % 3 != 0 && q < 8) { q = 8; } + // allow quantization grid up to 1/32 for normal notes + else if (q < 6) { q = 6; } } - else if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_BIG ) + auto xCoordOfTick = [=](int tick) { + return m_whiteKeyWidth + ( + (tick - m_currentPosition) * m_ppb / MidiTime::ticksPerBar() + ); + }; + p.setPen(m_lineColor); + for (tick = m_currentPosition - m_currentPosition % q, + x = xCoordOfTick(tick); + x <= width(); + tick += q, x = xCoordOfTick(tick)) { - // draw a big one while checking if it is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) - { - p.drawPixmap(PIANO_X, y - m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, - *s_whiteKeyBigPressedPm); - } - else - { - p.drawPixmap(PIANO_X, y-m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, - *s_whiteKeyBigPm); - } - // if a big white key has been the first key, - // black keys needs to be lifted up - if( keys_processed == 0 ) - { - first_white_key_height = m_whiteKeyBigHeight; - } - // update y-pos - y -= m_whiteKeyBigHeight; + p.drawLine(x, keyAreaTop(), x, noteEditBottom()); } - // Compute the corrections for the note names - int yCorrectionForNoteLabels = 0; + // draw horizontal grid lines and piano notes + p.setClipRect(0, keyAreaTop(), width(), keyAreaBottom() - keyAreaTop()); + // the first grid line from the top Y position + int grid_line_y = keyAreaTop() + m_keyLineHeight - 1; - int keyCode = key % KeysPerOctave; - switch (keyCode) + // lambda function for returning the height of a key + auto keyHeight = [&]( + const int key + ) -> int { - case 0: // C - case 5: // F - yCorrectionForNoteLabels = (m_whiteKeySmallHeight - labelHeight + 1) / -2; - break; - case 2: // D - case 7: // G - case 9: // A - yCorrectionForNoteLabels = (m_whiteKeyBigHeight / 2 - labelHeight + 1) / -2; - break; - case 4: // E - case 11: // B - // calculate center point of key and move half of text - yCorrectionForNoteLabels = -(((m_whiteKeySmallHeight - (m_whiteKeySmallHeight * 2 + 3) / 6) / 4) - - labelHeight / 2); - break; - } - - if( Piano::isWhiteKey( key ) ) + switch (prKeyOrder[key % KeysPerOctave]) + { + case PR_WHITE_KEY_BIG: + return m_whiteKeyBigHeight; + case PR_WHITE_KEY_SMALL: + return m_whiteKeySmallHeight; + case PR_BLACK_KEY: + return m_blackKeyHeight; + } + return 0; // should never happen + }; + // lambda function for returning the distance to the top of a key + auto gridCorrection = [&]( + const int key + ) -> int { - // Draw note names if activated in the preferences, C notes are always drawn - if ( (key % 12 == 0 || drawNoteNames) && m_keyLineHeight > 10 ) + const int keyCode = key % KeysPerOctave; + switch (prKeyOrder[keyCode]) { - QString noteString = getNoteString( key ); - - QPoint textStart( WHITE_KEY_WIDTH - 18, key_line_y ); - textStart += QPoint( 0, yCorrectionForNoteLabels ); - - p.setPen( textShadow() ); - p.drawText( textStart + QPoint( 1, 1 ), noteString ); - // The C key is painted darker than the other ones - if ( key % 12 == 0 ) + case PR_WHITE_KEY_BIG: + return m_whiteKeySmallHeight; + case PR_WHITE_KEY_SMALL: + // These two keys need to adjust up small height instead of only key line height + if (keyCode == Key_C || keyCode == Key_F) { - p.setPen( textColor() ); + return m_whiteKeySmallHeight; } - else - { - p.setPen( textColorLight() ); - } - p.drawText( textStart, noteString ); + case PR_BLACK_KEY: + return m_blackKeyHeight; } - } - ++key; - } - - // reset all values, because now we're going to draw all black keys - key = m_startKey; - keys_processed = 0; - int white_cnt = 0; - key_line_y = qMin(keyAreaBottom(), m_keyLineHeight * NumKeys); - - // and go! - for( int y = key_line_y + y_offset; - y > PR_TOP_MARGIN; ++keys_processed ) - { - // check for black key that is only half visible on the bottom - // of piano-roll - if( keys_processed == 0 - // current key may not be a black one - && prKeyOrder[key % KeysPerOctave] != PR_BLACK_KEY - // but the previous one must be black (we must check this - // because there might be two white keys (E-F) - && prKeyOrder[( key - 1 ) % KeysPerOctave] == - PR_BLACK_KEY ) + return 0; // should never happen + }; + auto keyWidth = [&]( + const int key + ) -> int { - // draw the black key! - p.drawPixmap( PIANO_X, y - m_blackKeyHeight / 2, BLACK_KEY_WIDTH, m_blackKeyHeight, - *s_blackKeyPm ); - // is the one after the start-note a black key?? - if( prKeyOrder[( key + 1 ) % KeysPerOctave] != - PR_BLACK_KEY ) + switch (prKeyOrder[key % KeysPerOctave]) { - // no, then move it up! - y -= m_keyLineHeight / 2; + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + return m_whiteKeyWidth; + case PR_BLACK_KEY: + return m_blackKeyWidth; } - } - // current key black? - if( prKeyOrder[key % KeysPerOctave] == PR_BLACK_KEY) + return 0; // should never happen + }; + // lambda function to draw a key + auto drawKey = [&]( + const int key, + const int yb) { - // then draw it (calculation of y very complicated, - // but that's the only working solution, sorry...) - // check if the key is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) + const bool pressed = m_pattern->instrumentTrack()->pianoModel()->isKeyPressed(key); + const int keyCode = key % KeysPerOctave; + const int yt = yb - gridCorrection(key); + const int kh = keyHeight(key); + const int kw = keyWidth(key); + // set key colors + p.setPen(QColor(0, 0, 0)); + switch (prKeyOrder[keyCode]) { - p.drawPixmap( PIANO_X, y - ( first_white_key_height - - m_whiteKeySmallHeight ) - - m_whiteKeySmallHeight/2 - 1 - - m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPressedPm ); + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + p.setBrush(pressed ? m_whiteKeyActiveBackground : m_whiteKeyInactiveBackground); + break; + case PR_BLACK_KEY: + p.setBrush(pressed ? m_blackKeyActiveBackground : m_blackKeyInactiveBackground); } - else + // draw key + p.drawRect(PIANO_X, yt, kw, kh); + // draw note name + if (keyCode == Key_C || (drawNoteNames && Piano::isWhiteKey(key))) { - p.drawPixmap( PIANO_X, y - ( first_white_key_height - - m_whiteKeySmallHeight ) - - m_whiteKeySmallHeight/2 - 1 - - m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPm ); + // small font sizes have 1 pixel offset instead of 2 + auto zoomOffset = m_zoomYLevels[m_zoomingYModel.value()] > 1.0f ? 2 : 1; + QString noteString = getNoteString(key); + QRect textRect( + m_whiteKeyWidth - boundingRect.width() - 2, + yb - m_keyLineHeight + zoomOffset, + boundingRect.width(), + boundingRect.height() + ); + p.setPen(pressed ? m_whiteKeyActiveTextShadow : m_whiteKeyInactiveTextShadow); + p.drawText(textRect.adjusted(0, 1, 1, 0), Qt::AlignRight | Qt::AlignHCenter, noteString); + p.setPen(pressed ? m_whiteKeyActiveTextColor : m_whiteKeyInactiveTextColor); + // if (keyCode == Key_C) { p.setPen(textColor()); } + // else { p.setPen(textColorLight()); } + p.drawText(textRect, Qt::AlignRight | Qt::AlignHCenter, noteString); } - // update y-pos - y -= m_whiteKeyBigHeight; - // reset white-counter - white_cnt = 0; - } - else + }; + // lambda for drawing the horizontal grid line + auto drawHorizontalLine = [&]( + const int key, + const int y + ) { - // simple workaround for increasing x if there were - // two white keys (e.g. between E and F) - ++white_cnt; - if( white_cnt > 1 ) - { - y -= m_whiteKeyBigHeight/2; - } - } - - ++key; - } - - - // erase the area below the piano, because there might be keys that - // should be only half-visible - p.fillRect( QRect( 0, key_line_y, - WHITE_KEY_WIDTH, noteEditBottom() - key_line_y ), bgColor ); - - // display note editing info - QFont f = p.font(); - f.setBold( false ); - p.setFont( pointSize<10>( f ) ); - p.setPen( noteModeColor() ); - p.drawText( QRect( 0, key_line_y, - WHITE_KEY_WIDTH, noteEditBottom() - key_line_y ), - Qt::AlignCenter | Qt::TextWordWrap, - m_nemStr.at( m_noteEditMode ) + ":" ); - - // set clipping area, because we are not allowed to paint over - // keyboard... - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN ); - - // draw the grid - if( hasValidPattern() ) - { - int q, x, tick; - - if( m_zoomingModel.value() > 3 ) + if (key % KeysPerOctave == Key_C) { p.setPen(m_beatLineColor); } + else { p.setPen(m_lineColor); } + p.drawLine(m_whiteKeyWidth, y, width(), y); + }; + // correct y offset of the top key + switch (prKeyOrder[topNote]) { - // If we're over 100% zoom, we allow all quantization level grids - q = quantization(); - } - else if( quantization() % 3 != 0 ) - { - // If we're under 100% zoom, we allow quantization grid up to 1/24 for triplets - // to ensure a dense doesn't fill out the background - q = quantization() < 8 ? 8 : quantization(); - } - else { - // If we're under 100% zoom, we allow quantization grid up to 1/32 for normal notes - q = quantization() < 6 ? 6 : quantization(); - } - - // First we draw the vertical quantization lines - for( tick = m_currentPosition - m_currentPosition % q, x = xCoordOfTick( tick ); - x <= width(); tick += q, x = xCoordOfTick( tick ) ) - { - p.setPen( lineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + break; + case PR_BLACK_KEY: + // draw extra white key + drawKey(topKey + 1, grid_line_y - m_keyLineHeight); } - - // Draw horizontal lines - key = m_startKey; - for( int y = key_line_y - 1; y > PR_TOP_MARGIN; - y -= m_keyLineHeight ) + // loop through visible keys + const int lastKey = qMax(0, topKey - m_pianoKeysVisible); + for (int key = topKey; key > lastKey; --key) { - if( static_cast( key % KeysPerOctave ) == Key_C ) + bool whiteKey = Piano::isWhiteKey(key); + if (whiteKey) { - // C note gets accented - p.setPen( beatLineColor() ); + drawKey(key, grid_line_y); + drawHorizontalLine(key, grid_line_y); + grid_line_y += m_keyLineHeight; } else { - p.setPen( lineColor() ); + // draw next white key + drawKey(key - 1, grid_line_y + m_keyLineHeight); + drawHorizontalLine(key - 1, grid_line_y + m_keyLineHeight); + // draw black key over previous and next white key + drawKey(key, grid_line_y); + drawHorizontalLine(key, grid_line_y); + // drew two grid keys so skip ahead properly + grid_line_y += m_keyLineHeight + m_keyLineHeight; + // capture double key draw + --key; } - p.drawLine( WHITE_KEY_WIDTH, y, width(), y ); - ++key; } + // don't draw over keys + p.setClipRect(m_whiteKeyWidth, keyAreaTop(), width(), noteEditBottom() - keyAreaTop()); - // Draw alternating shades on bars - float timeSignature = static_cast( Engine::getSong()->getTimeSigModel().getNumerator() ) - / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); + // draw alternating shading on bars + float timeSignature = + static_cast(Engine::getSong()->getTimeSigModel().getNumerator()) / + static_cast(Engine::getSong()->getTimeSigModel().getDenominator()); float zoomFactor = m_zoomLevels[m_zoomingModel.value()]; //the bars which disappears at the left side by scrolling int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); - //iterates the visible bars and draw the shading on uneven bars - for( int x = WHITE_KEY_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppb, ++barCount ) + for (int x = m_whiteKeyWidth, barCount = leftBars; + x < width() + m_currentPosition * zoomFactor / timeSignature; + x += m_ppb, ++barCount) { - if( ( barCount + leftBars ) % 2 != 0 ) + if ((barCount + leftBars) % 2 != 0) { - p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, PR_TOP_MARGIN, m_ppb, - height() - ( PR_BOTTOM_MARGIN + PR_TOP_MARGIN ), backgroundShade() ); + p.fillRect(x - m_currentPosition * zoomFactor / timeSignature, + PR_TOP_MARGIN, + m_ppb, + height() - (PR_BOTTOM_MARGIN + PR_TOP_MARGIN), + m_backgroundShade); } } - // Draw the vertical beat lines + // draw vertical beat lines int ticksPerBeat = DefaultTicksPerBar / Engine::getSong()->getTimeSigModel().getDenominator(); - - for( tick = m_currentPosition - m_currentPosition % ticksPerBeat, - x = xCoordOfTick( tick ); x <= width(); - tick += ticksPerBeat, x = xCoordOfTick( tick ) ) + p.setPen(m_beatLineColor); + for(tick = m_currentPosition - m_currentPosition % ticksPerBeat, + x = xCoordOfTick( tick ); + x <= width(); + tick += ticksPerBeat, x = xCoordOfTick(tick)) { - p.setPen( beatLineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } - // Draw the vertical bar lines - for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), - x = xCoordOfTick( tick ); x <= width(); - tick += MidiTime::ticksPerBar(), x = xCoordOfTick( tick ) ) + // draw vertical bar lines + p.setPen(m_barLineColor); + for(tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), + x = xCoordOfTick( tick ); + x <= width(); + tick += MidiTime::ticksPerBar(), x = xCoordOfTick(tick)) { - p.setPen( barLineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } // draw marked semitones after the grid - for( int i = 0; i < m_markedSemiTones.size(); i++ ) + for(x = 0; x < m_markedSemiTones.size(); ++x) { - const int key_num = m_markedSemiTones.at( i ); - const int y = key_line_y + 5 - - m_keyLineHeight * ( key_num - m_startKey + 1 ); - - if( y > key_line_y ) - { - break; - } - - p.fillRect( WHITE_KEY_WIDTH + 1, y - m_keyLineHeight / 2, width() - 10, m_keyLineHeight + 1, - markedSemitoneColor() ); + const int key_num = m_markedSemiTones.at(x); + const int y = keyAreaBottom() + 5 - m_keyLineHeight * + (key_num - m_startKey + 1); + if(y > keyAreaBottom()) { break; } + p.fillRect(m_whiteKeyWidth + 1, + y - m_keyLineHeight / 2, + width() - 10, + m_keyLineHeight + 1, + m_markedSemitoneColor); } } + // reset clip + p.setClipRect(0, 0, width(), height()); + + // erase the area below the piano, because there might be keys that + // should be only half-visible + p.fillRect( QRect( 0, keyAreaBottom(), + m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), bgColor); + + // display note editing info + //QFont f = p.font(); + f.setBold( false ); + p.setFont( pointSize<10>( f ) ); + p.setPen(m_noteModeColor); + p.drawText( QRect( 0, keyAreaBottom(), + m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), + Qt::AlignCenter | Qt::TextWordWrap, + m_nemStr.at( m_noteEditMode ) + ":" ); + + // set clipping area, because we are not allowed to paint over + // keyboard... + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN); + // following code draws all notes in visible area // and the note editing stuff (volume, panning, etc) @@ -3159,15 +2999,17 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) qSwap( sel_key_start, sel_key_end ); } - int y_base = key_line_y - 1; + int y_base = keyAreaBottom() - 1; if( hasValidPattern() ) { - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN ); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN); - const int visible_keys = ( key_line_y-keyAreaTop() ) / - m_keyLineHeight + 2; + const int topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); + const int bottomKey = topKey - m_pianoKeysVisible; QPolygonF editHandles; @@ -3194,21 +3036,20 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { - // we've done and checked all, let's draw the - // note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(), - ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames ); + // we've done and checked all, let's draw the note + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_ghostNoteColor, m_ghostNoteTextColor, m_selectedNoteColor, + m_ghostNoteOpacity, m_ghostNoteBorders, drawNoteNames); } } @@ -3236,31 +3077,30 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { - // we've done and checked all, let's draw the - // note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, noteColor(), noteTextColor(), selectedNoteColor(), - noteOpacity(), noteBorders(), drawNoteNames ); + // we've done and checked all, let's draw the note + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_noteColor, m_noteTextColor, m_selectedNoteColor, + m_noteOpacity, m_noteBorders, drawNoteNames); } // draw note editing stuff int editHandleTop = 0; if( m_noteEditMode == NoteEditVolume ) { - QColor color = barColor().lighter( 30 + ( note->getVolume() * 90 / MaxVolume ) ); + QColor color = m_barColor.lighter(30 + (note->getVolume() * 90 / MaxVolume)); if( note->selected() ) { - color = selectedNoteColor(); + color = m_selectedNoteColor; } p.setPen( QPen( color, NOTE_EDIT_LINE_WIDTH ) ); @@ -3275,10 +3115,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } else if( m_noteEditMode == NoteEditPanning ) { - QColor color = noteColor(); + QColor color = m_noteColor; if( note->selected() ) { - color = selectedNoteColor(); + color = m_selectedNoteColor; } p.setPen( QPen( color, NOTE_EDIT_LINE_WIDTH ) ); @@ -3297,11 +3137,11 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) if( note->hasDetuningInfo() ) { - drawDetuningInfo( p, note, - x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight ); - p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, + drawDetuningInfo(p, note, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, height() - PR_TOP_MARGIN); } } @@ -3324,24 +3164,24 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { // we've done and checked all, let's draw the note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, m_stepRecorder.curStepNoteColor(), noteTextColor(), selectedNoteColor(), - noteOpacity(), noteBorders(), drawNoteNames ); + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_stepRecorder.curStepNoteColor(), m_noteTextColor, m_selectedNoteColor, + m_noteOpacity, m_noteBorders, drawNoteNames); } } - p.setPen( QPen( noteColor(), NOTE_EDIT_LINE_WIDTH + 2 ) ); + p.setPen(QPen(m_noteColor, NOTE_EDIT_LINE_WIDTH + 2)); p.drawPoints( editHandles ); } @@ -3352,14 +3192,16 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.setFont( pointSize<14>( f ) ); p.setPen( QApplication::palette().color( QPalette::Active, QPalette::BrightText ) ); - p.drawText( WHITE_KEY_WIDTH + 20, PR_TOP_MARGIN + 40, + p.drawText(m_whiteKeyWidth + 20, PR_TOP_MARGIN + 40, tr( "Please open a pattern by double-clicking " "on it!" ) ); } - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - - WHITE_KEY_WIDTH, height() - PR_TOP_MARGIN - - m_notesEditHeight - PR_BOTTOM_MARGIN ); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN - m_notesEditHeight - PR_BOTTOM_MARGIN); // now draw selection-frame int x = ( ( sel_pos_start - m_currentPosition ) * m_ppb ) / @@ -3368,9 +3210,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) MidiTime::ticksPerBar() ) - x; int y = (int) y_base - sel_key_start * m_keyLineHeight; int h = (int) y_base - sel_key_end * m_keyLineHeight - y; - p.setPen( selectedNoteColor() ); + p.setPen(m_selectedNoteColor); p.setBrush( Qt::NoBrush ); - p.drawRect( x + WHITE_KEY_WIDTH, y, w, h ); + p.drawRect(x + m_whiteKeyWidth, y, w, h); // TODO: Get this out of paint event int l = ( hasValidPattern() )? (int) m_pattern->length() : 0; @@ -3383,70 +3225,92 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } // set line colors - QColor editAreaCol = QColor( lineColor() ); - QColor currentKeyCol = QColor( beatLineColor() ); + QColor editAreaCol = QColor(m_lineColor); + QColor currentKeyCol = QColor(m_beatLineColor); editAreaCol.setAlpha( 64 ); currentKeyCol.setAlpha( 64 ); // horizontal line for the key under the cursor - if( hasValidPattern() ) + if(hasValidPattern() && gui->pianoRoll()->hasFocus()) { int key_num = getKey( mapFromGlobal( QCursor::pos() ).y() ); - p.fillRect( 10, key_line_y + 3 - m_keyLineHeight * + p.fillRect( 10, keyAreaBottom() + 3 - m_keyLineHeight * ( key_num - m_startKey + 1 ), width() - 10, m_keyLineHeight - 7, currentKeyCol ); } // bar to resize note edit area p.setClipRect( 0, 0, width(), height() ); - p.fillRect( QRect( 0, key_line_y, + p.fillRect( QRect( 0, keyAreaBottom(), width()-PR_RIGHT_MARGIN, NOTE_EDIT_RESIZE_BAR ), editAreaCol ); - const QPixmap * cursor = NULL; - // draw current edit-mode-icon below the cursor - switch( m_editMode ) - { - case ModeDraw: - if( m_mouseDownRight ) - { - cursor = s_toolErase; - } - else if( m_action == ActionMoveNote ) - { - cursor = s_toolMove; - } - else - { - cursor = s_toolDraw; - } - break; - case ModeErase: cursor = s_toolErase; break; - case ModeSelect: cursor = s_toolSelect; break; - case ModeEditDetuning: cursor = s_toolOpen; break; - } - QPoint mousePosition = mapFromGlobal( QCursor::pos() ); - if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) + if (gui->pianoRoll()->hasFocus()) { - p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor ); + const QPixmap * cursor = NULL; + // draw current edit-mode-icon below the cursor + switch( m_editMode ) + { + case ModeDraw: + if( m_mouseDownRight ) + { + cursor = s_toolErase; + } + else if( m_action == ActionMoveNote ) + { + cursor = s_toolMove; + } + else + { + cursor = s_toolDraw; + } + break; + case ModeErase: cursor = s_toolErase; break; + case ModeSelect: cursor = s_toolSelect; break; + case ModeEditDetuning: cursor = s_toolOpen; break; + } + QPoint mousePosition = mapFromGlobal( QCursor::pos() ); + if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) + { + p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor ); + } } } +void PianoRoll::updateScrollbars() +{ + m_leftRightScroll->setGeometry( + m_whiteKeyWidth, + height() - SCROLLBAR_SIZE, + width() - m_whiteKeyWidth, + SCROLLBAR_SIZE + ); + m_topBottomScroll->setGeometry( + width() - SCROLLBAR_SIZE, + PR_TOP_MARGIN, + SCROLLBAR_SIZE, + height() - PR_TOP_MARGIN - SCROLLBAR_SIZE + ); + int pianoAreaHeight = keyAreaBottom() - PR_TOP_MARGIN; + int numKeysVisible = pianoAreaHeight / m_keyLineHeight; + m_totalKeysToScroll = qMax(0, NumKeys - numKeysVisible); + m_topBottomScroll->setRange(0, m_totalKeysToScroll); + if (m_startKey > m_totalKeysToScroll) + { + m_startKey = qMax(0, m_totalKeysToScroll); + } + m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); +} + // responsible for moving/resizing scrollbars after window-resizing void PianoRoll::resizeEvent(QResizeEvent * re) { - m_leftRightScroll->setGeometry( WHITE_KEY_WIDTH, - height() - - SCROLLBAR_SIZE, - width()-WHITE_KEY_WIDTH, - SCROLLBAR_SIZE ); - updateYScroll(); - - Engine::getSong()->getPlayPos( Song::Mode_PlayPattern - ).m_timeLine->setFixedWidth( width() ); - + updatePositionLineHeight(); + updateScrollbars(); + Engine::getSong()->getPlayPos(Song::Mode_PlayPattern) + .m_timeLine->setFixedWidth(width()); update(); } @@ -3463,7 +3327,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) if (!hasValidPattern()) {return;} // get values for going through notes int pixel_range = 8; - int x = we->x() - WHITE_KEY_WIDTH; + int x = we->x() - m_whiteKeyWidth; int ticks_start = ( x - pixel_range / 2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * @@ -3568,7 +3432,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (we->x() - WHITE_KEY_WIDTH)* MidiTime::ticksPerBar(); + int x = (we->x() - m_whiteKeyWidth)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -3619,24 +3483,16 @@ void PianoRoll::focusInEvent( QFocusEvent * ) -int PianoRoll::getKey(int y ) const +int PianoRoll::getKey(int y) const { - int key_line_y = keyAreaBottom() - 1; - // pressed key on piano - int key_num = ( key_line_y - y ) / m_keyLineHeight; - key_num += m_startKey; - - // some range-checking-stuff - if( key_num < 0 ) - { - key_num = 0; - } - - if( key_num >= KeysPerOctave * NumOctaves ) - { - key_num = KeysPerOctave * NumOctaves - 1; - } - + // handle case that very top pixel maps to next key above + if (y - keyAreaTop() <= 1) { y = keyAreaTop() + 2; } + int key_num = qBound( + 0, + // add + 1 to stay within the grid lines + ((keyAreaBottom() - y + 1) / m_keyLineHeight) + m_startKey, + NumKeys - 1 + ); return key_num; } @@ -3854,7 +3710,7 @@ void PianoRoll::horScrolled(int new_pos ) void PianoRoll::verScrolled( int new_pos ) { // revert value - m_startKey = m_totalKeysToScroll - new_pos; + m_startKey = qMax(0, m_totalKeysToScroll - new_pos); update(); } @@ -4008,13 +3864,13 @@ void PianoRoll::updateYScroll() int total_pixels = m_octaveHeight * NumOctaves - (height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN - m_notesEditHeight); - m_totalKeysToScroll = total_pixels * KeysPerOctave / m_octaveHeight; + m_totalKeysToScroll = qMax(0, total_pixels * KeysPerOctave / m_octaveHeight); m_topBottomScroll->setRange(0, m_totalKeysToScroll); if(m_startKey > m_totalKeysToScroll) { - m_startKey = m_totalKeysToScroll; + m_startKey = qMax(0, m_totalKeysToScroll); } m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); } @@ -4161,7 +4017,7 @@ bool PianoRoll::deleteSelectedNotes() void PianoRoll::autoScroll( const MidiTime & t ) { - const int w = width() - WHITE_KEY_WIDTH; + const int w = width() - m_whiteKeyWidth; if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) { m_leftRightScroll->setValue( t.getBar() * MidiTime::ticksPerBar() ); @@ -4187,6 +4043,22 @@ void PianoRoll::updatePosition( const MidiTime & t ) { autoScroll( t ); } + const int pos = m_timeLine->pos() * m_ppb / MidiTime::ticksPerBar(); + if (pos >= m_currentPosition && pos <= m_currentPosition + width() - m_whiteKeyWidth) + { + m_positionLine->show(); + m_positionLine->move(pos - (m_positionLine->width() - 1) - m_currentPosition + m_whiteKeyWidth, keyAreaTop()); + } + else + { + m_positionLine->hide(); + } +} + + +void PianoRoll::updatePositionLineHeight() +{ + m_positionLine->setFixedHeight(keyAreaBottom() - keyAreaTop()); } @@ -4230,6 +4102,7 @@ void PianoRoll::zoomingChanged() m_timeLine->setPixelsPerBar( m_ppb ); m_stepRecorderWidget.setPixelsPerBar( m_ppb ); + m_positionLine->zoomChange(m_zoomLevels[m_zoomingModel.value()]); update(); } @@ -4239,9 +4112,9 @@ void PianoRoll::zoomingYChanged() { m_keyLineHeight = m_zoomYLevels[m_zoomingYModel.value()] * DEFAULT_KEY_LINE_HEIGHT; m_octaveHeight = m_keyLineHeight * KeysPerOctave; - m_whiteKeySmallHeight = round(m_keyLineHeight * 1.5); + m_whiteKeySmallHeight = qFloor(m_keyLineHeight * 1.5); m_whiteKeyBigHeight = m_keyLineHeight * 2; - m_blackKeyHeight = round(m_keyLineHeight * 1.3333); + m_blackKeyHeight = m_keyLineHeight; //round(m_keyLineHeight * 1.3333); updateYScroll(); update(); @@ -4360,7 +4233,7 @@ Note * PianoRoll::noteUnderMouse() { QPoint pos = mapFromGlobal( QCursor::pos() ); - if( pos.x() <= WHITE_KEY_WIDTH + if (pos.x() <= m_whiteKeyWidth || pos.x() > width() - SCROLLBAR_SIZE || pos.y() < PR_TOP_MARGIN || pos.y() > keyAreaBottom() ) @@ -4369,7 +4242,7 @@ Note * PianoRoll::noteUnderMouse() } int key_num = getKey( pos.y() ); - int pos_ticks = ( pos.x() - WHITE_KEY_WIDTH ) * + int pos_ticks = (pos.x() - m_whiteKeyWidth) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // loop through whole note-vector... @@ -4721,6 +4594,14 @@ void PianoRollWindow::loadSettings( const QDomElement & de ) m_editor->loadMarkedSemiTones(de.firstChildElement("markedSemiTones")); MainWindow::restoreWidgetState( this, de ); + + // update margins here because we're later in the startup process + // We can't earlier because everything is still starting with the + // WHITE_KEY_WIDTH default + QMargins qm = m_editor->m_stepRecorderWidget.margins(); + qm.setLeft(m_editor->m_whiteKeyWidth); + m_editor->m_stepRecorderWidget.setMargins(qm); + m_editor->m_timeLine->setXOffset(m_editor->m_whiteKeyWidth); } @@ -4733,6 +4614,13 @@ QSize PianoRollWindow::sizeHint() const +bool PianoRollWindow::hasFocus() const +{ + return m_editor->hasFocus(); +} + + + void PianoRollWindow::updateAfterPatternChange() { patternRenamed(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index b6647d44a3b..d60b986d898 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -52,86 +52,6 @@ #include "PianoRoll.h" #include "Track.h" -positionLine::positionLine( QWidget* parent ) : - QWidget( parent ), - m_hasTailGradient ( false ), - m_lineColor (0, 0, 0, 0) -{ - resize( 8, height() ); - - setAttribute( Qt::WA_NoSystemBackground, true ); - setAttribute( Qt::WA_TransparentForMouseEvents ); -} - -void positionLine::paintEvent( QPaintEvent* pe ) -{ - QPainter p( this ); - - // If width is 1, we don't need a gradient - if (width() == 1) - { - p.fillRect( rect(), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); - } - - // If width > 1, we need the gradient - else - { - // Create the gradient trail behind the line - QLinearGradient gradient( rect().bottomLeft(), rect().bottomRight() ); - - // If gradient is enabled, we're in focus and we're playing, enable gradient - if (Engine::getSong()->isPlaying() && m_hasTailGradient && - Engine::getSong()->playMode() == Song::Mode_PlaySong) - { - gradient.setColorAt(( ( width() - 1.0 )/width() ), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 60) ); - } - else - { - gradient.setColorAt(( ( width() - 1.0 )/width() ), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); - } - - // Fill in the remaining parts - gradient.setColorAt(0, - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); - gradient.setColorAt(1, - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); - - // Fill line - p.fillRect( rect(), gradient ); - } -} - -// QProperty handles -bool positionLine::hasTailGradient() const -{ return m_hasTailGradient; } - -void positionLine::setHasTailGradient( const bool g ) -{ m_hasTailGradient = g; } - -QColor positionLine::lineColor() const -{ return m_lineColor; } - -void positionLine::setLineColor( const QColor & c ) -{ m_lineColor = c; } - -// NOTE: the move() implementation fixes a bug where the position line would appear -// in an unexpected location when positioned at the start of the track -void positionLine::zoomChange( double zoom ) -{ - int playHeadPos = x() + width() - 1; - - resize( 8.0 * zoom, height() ); - move( playHeadPos - width() + 1, y() ); - - update(); -} - - - - const QVector SongEditor::m_zoomLevels = { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f }; @@ -172,7 +92,7 @@ SongEditor::SongEditor( Song * song ) : connect( m_timeLine, SIGNAL( selectionFinished() ), this, SLOT( stopRubberBand() ) ); - m_positionLine = new positionLine( this ); + m_positionLine = new PositionLine(this); static_cast( layout() )->insertWidget( 1, m_timeLine ); connect( m_song, SIGNAL( playbackStateChanged() ), diff --git a/src/gui/widgets/PositionLine.cpp b/src/gui/widgets/PositionLine.cpp new file mode 100644 index 00000000000..ef003c5ebad --- /dev/null +++ b/src/gui/widgets/PositionLine.cpp @@ -0,0 +1,98 @@ +/* + * PositionLine.cpp + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "PositionLine.h" + +#include + +#include "GuiApplication.h" +#include "Song.h" + + +PositionLine::PositionLine(QWidget* parent) : + QWidget(parent), + m_hasTailGradient(false), + m_lineColor(0, 0, 0, 0) +{ + resize(8, height()); + + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TransparentForMouseEvents); +} + +void PositionLine::paintEvent(QPaintEvent* pe) +{ + QPainter p(this); + QColor c = QColor(m_lineColor); + + // If width is 1, we don't need a gradient + if (width() == 1) + { + c.setAlpha(153); + p.fillRect(rect(), c); + } + // If width > 1, we need the gradient + else + { + // Create the gradient trail behind the line + QLinearGradient gradient(rect().bottomLeft(), rect().bottomRight()); + qreal w = (width() - 1.0) / width(); + + // If gradient is enabled, we're in focus and we're playing, enable gradient + if (m_hasTailGradient && + Engine::getSong()->isPlaying() && + (Engine::getSong()->playMode() == Song::Mode_PlaySong || + Engine::getSong()->playMode() == Song::Mode_PlayPattern)) + { + c.setAlpha(60); + gradient.setColorAt(w, c); + } + else + { + c.setAlpha(0); + gradient.setColorAt(w, c); + } + + // Fill in the remaining parts + c.setAlpha(0); + gradient.setColorAt(0, c); + c.setAlpha(153); + gradient.setColorAt(1, c); + + // Fill line + p.fillRect(rect(), gradient); + } +} + +// NOTE: the move() implementation fixes a bug where the position line would appear +// in an unexpected location when positioned at the start of the track +void PositionLine::zoomChange(double zoom) +{ + int playHeadPos = x() + width() - 1; + + resize(8.0 * zoom, height()); + move(playHeadPos - width() + 1, y()); + + update(); +} \ No newline at end of file diff --git a/src/gui/widgets/StepRecorderWidget.cpp b/src/gui/widgets/StepRecorderWidget.cpp index a546c2a2cdc..d2157ef66bc 100644 --- a/src/gui/widgets/StepRecorderWidget.cpp +++ b/src/gui/widgets/StepRecorderWidget.cpp @@ -58,6 +58,19 @@ void StepRecorderWidget::setCurrentPosition(MidiTime currentPosition) m_currentPosition = currentPosition; } +void StepRecorderWidget::setMargins(const QMargins &qm) +{ + m_left = qm.left(); + m_right = qm.right(); + m_top = qm.top(); + m_bottom = qm.bottom(); +} + +QMargins StepRecorderWidget::margins() +{ + return QMargins(m_left, m_top, m_right, m_bottom); +} + void StepRecorderWidget::setBottomMargin(const int marginBottom) { m_marginBottom = marginBottom; From f2887bda689f0762751fc4e8a81180330b7ec09d Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 10 Aug 2020 12:16:00 -0300 Subject: [PATCH 064/180] Improve the context menu actions when multiple TCOs are selected (#5601) * Starts implementing the feature The idea of this branch is to allow actions triggered through the context menu of a TCO on the song editor to affect all the selected TCOs. With this commit, only the "Mute/unmute" action affects all selected TCOs, while the others retain their old behavior (only affect the TCO that owns the context menu). For that, a method was created that processes all actions (the triggered action is parsed as a parameter to the method). In the case of the "Mute" action, it checks if the song editor has selected TCOs, and if it does it mutes/unmutes all of them. * Allows selected TCOs to be removed too Now the "Remove" action from the context menu will remove all selected TCOs if there are any. * Starts implementing selected TCO cut and copy Now, when multiple TCOs are selected, the context menu actions Cut and Copy will write a DataFile to the clipboard containing the TCO information, so it can later be used to paste it. The Paste action now checks if there's data in the QApplication clipboard. If there is, it will later paste the TCOs (for now it just prints the data with qWarning). If there's not, it uses the regular TCO paste method that uses the internal LMMS clipboard. Because it now have to decide between the QApplication clipboard and the LMMS internal clipboard, the Clipboard::copy() method now clears anything in the QApplication clipboard, making it empty so LMMS can know it should use the internal clipboard instead in that situation. Adds safety checks for the selected TCO views. * Overloads TCW paste selection methods This commit is a step towards implementing the paste feature of the TCO context menu. It overloads the TrackContentWidget::canPasteSelection and TrackContentWidget::pasteSelection methods so they can be called without having a QDropEvent (only the QMimeData with the copied TCOs information). The overloaded canPasteSelection(MidiTime, QMimeData, bool) method required a third argument which says whether pasting over the same bar should be allowed or not, because it shouldn't when the QDropEvent comes from the same application but should when it doesn't. That is defined in the canPasteSelection(MidiTime, QDropEvent) method. Also, the pasteSelection(MidiTime, QDropEvent) isn't optimal, since it calls canPasteSelection twice (more details in the comments, but it's basically because the two canPasteSelection methods can return different values depending on the origin of QDropEvent). This could later be fixed by calling canPasteSelection before pasteSelection and removing it from inside the method altogether. Next step is to add the "tco_" key to the mimeData on the Copy/Cut operations and implementing the paste operation using those methods. * Adds the TCO type to the clipboard Adds the key with the TCO type ("tco_" + type number + ":" + TCO Data XML) to the clipboard, so it can be later used by the canPasteSelection and pasteSelection methods. * Apply changes to "src/tracks/SampleTrack.cpp" Change the SampleTCOView::contextMenuEvent() method so it behaves the same way as the TrackContentObjectView::contextMenuEvent() method. For that, I had to change the ContextMenuAction enum and the contextMenuAction methods to be protected instead of private, so SampleTCOView can access it. * Implement the paste action Now that the canPasteSelection and pasteSelection methods were overloaded, it was possible to implement the paste action using the same logic as the Drag&Drop copy/paste. Other small changes: - Removes the TCO views AFTER creating the TCO data file, instead of before (which probably resulted in an empty data file). - Uses the StringPairDrag::mimeType() instead of Clipboard::mimeType() since that's the one the methods from StringPairDrag.cpp recognizes. * Removes QDebug header Forgot to remove the QDebug header on the last commit. * Creates a Context Menu on the TCW for "paste" Now it's possible to paste a selection of copied TCOs anywhere in the TCW, as long as the rules to paste are met (same rules as Drag&Drop copy/paste). If the rules are not met the "Paste" menu action shows but is disabled. * Small code refactoring Saving a few lines of code. * Avoids double call to canPasteSelection This commit adds a third parameter to the pasteSelection overloaded method, which will define whether we whould skip the canPasteSelection check. The only situation where we will want to skip it is if we are calling pasteSelection from inside the other pasteSelection method, which will already have checked it. Because of that the default value is false. Organizes comments as well. * Separates methods for the actions on selections Now the remove, copy, cut, paste and toggle mute actions have a separate method for applying them to selections, which are then called from the contextMenuAction method. That made the code more organized and the contextMenuAction method smaller. Also, the mouse shortcuts for muting and removing (CTRL+middle button, middle button, CTRL+right button) now apply the action on selections as well. * Fixes small bug and reorganize code Fixes a small bug where using a mouse shortcut or choosing an action on a TCO that is not selected while other TCOs were selected would result in the selection being affected. Also reorganizes the code so the conditional for choosing between the selection action and the individual action stays inside the method. * Move logic to the action methods Since the methods that called the action+Selection() methods didn't need the list of selectableObjects, I moved it to the inside of the action+Selection() methods and removed the parameter from them. * Changes logic structure and labels As suggested by Spekular, the logic structure was changed. Now, the mousePressEvent and contextMenuAction methods determine whether the action should happen to a selection of TCOs or an individual TCO. The list of TCOs to be affected populate a QVector list called "active", which is parsed to the action method that will apply the action to each object in the list. I changed the method names to removeActive, cutActive, copyActive and toggleMuteActive, since I believe that better describe the behavior. The paste method is still called pasteSelection for now, because the paste behavior isn't related to the active TCOs but to the content of the clipboard. The contextMenuEvent method on both src/core/Track.cpp and src/tracks/SampleTrack.cpp were changed so they also check if the right-clicked TCO is part of a selection or an individual TCO, and the labels for the actions are changed to better describe the behavior (i.e.: "Delete" for individual TCO and "Delete selection" for multiple TCOs). * Make removeActive and toggleMuteActive static removeActive and toggleMuteActive methods are now static so they can be called from anywhere in the code since they don't require a TCO view instance to work. That makes it possible for them to be used in the future if any feature requires this type of action to be called from out of a TCO view instance. The same couldn't be done for the copyActive and cutActive methods because those require an instance of the TCO view to call them, since when copying to the clipboard some metadata is written using information from the object instance. * Renamed TCO View paste method I renamed the TCO View paste method from pasteSelection to just paste, since the TCO view doesn't currently have a paste method (only the TCO class). It's also a name that accurately describes the action: it will paste either a group of TCOVs or a single TCOV on the current TCOV. I also moved the logic for deciding between the multiple TCOV paste and single TCOV paste inside the paste method. * Moves repeated code to a new method This commit adds another method to TrackContentObjectView called getClickedTCOs, which will return a QVector with the TCO views that are supposed to be affected by a context menu action (either the individual right-clicked TCO or the selection of TCOs). Code was updated on the other methods to make use of this new method. Method names for actions that affect multiple TCOs were changed. Instead of calling them copyActive, cutActive, toggleMuteActive and removeActive, they are just called copy, cut, toggleMute and remove, hence they are overloaded methods for those actions that affect multiple TCOs (their differenciation is the arguments list, which is a QVector list for those). * Avoid unnecessary calls to getClickedTCOs() We use a ternary operator inside TrackContentObjectView::mousePressEvent to avoid unnecessary calls to getClickedTCOs when the list is not going to be used. The contextMenuEvent method on both Track.cpp and SampleTrack.cpp was reformated to use a more appropriate indentation and spacing between method calls. * Fix indenting in a ternary operator assignment --- include/Track.h | 33 +++++ src/core/Clipboard.cpp | 7 + src/core/Track.cpp | 282 ++++++++++++++++++++++++++++++++++--- src/tracks/SampleTrack.cpp | 52 +++++-- 4 files changed, 340 insertions(+), 34 deletions(-) diff --git a/include/Track.h b/include/Track.h index ff0e3ef88bf..9362a838019 100644 --- a/include/Track.h +++ b/include/Track.h @@ -250,6 +250,20 @@ class TrackContentObjectView : public selectableObject, public ModelView bool needsUpdate(); void setNeedsUpdate( bool b ); + // Method to get a QVector of TCOs to be affected by a context menu action + QVector getClickedTCOs(); + + // Methods to remove, copy, cut, paste and mute a QVector of TCO views + void copy( QVector tcovs ); + void cut( QVector tcovs ); + void paste(); + // remove and toggleMute are static because they don't depend + // being called from a particular TCO view, but can be called anywhere as long + // as a valid TCO view list is given, while copy/cut require an instance for + // some metadata to be written to the clipboard. + static void remove( QVector tcovs ); + static void toggleMute( QVector tcovs ); + public slots: virtual bool close(); void cut(); @@ -257,11 +271,21 @@ public slots: void update() override; protected: + enum ContextMenuAction + { + Remove, + Cut, + Copy, + Paste, + Mute + }; + virtual void constructContextMenu( QMenu * ) { } void contextMenuEvent( QContextMenuEvent * cme ) override; + void contextMenuAction( ContextMenuAction action ); void dragEnterEvent( QDragEnterEvent * dee ) override; void dropEvent( QDropEvent * de ) override; void leaveEvent( QEvent * e ) override; @@ -370,7 +394,9 @@ class TrackContentWidget : public QWidget, public JournallingObject } bool canPasteSelection( MidiTime tcoPos, const QDropEvent *de ); + bool canPasteSelection( MidiTime tcoPos, const QMimeData *md, bool allowSameBar = false ); bool pasteSelection( MidiTime tcoPos, QDropEvent * de ); + bool pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck = false ); MidiTime endPosition( const MidiTime & posStart ); @@ -391,6 +417,13 @@ public slots: void changePosition( const MidiTime & newPos = MidiTime( -1 ) ); protected: + enum ContextMenuAction + { + Paste + }; + + void contextMenuEvent( QContextMenuEvent * cme ) override; + void contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ); void dragEnterEvent( QDragEnterEvent * dee ) override; void dropEvent( QDropEvent * de ) override; void mousePressEvent( QMouseEvent * me ) override; diff --git a/src/core/Clipboard.cpp b/src/core/Clipboard.cpp index 0c4b972865b..9b1191cdc0d 100644 --- a/src/core/Clipboard.cpp +++ b/src/core/Clipboard.cpp @@ -22,6 +22,9 @@ * */ +#include +#include + #include "Clipboard.h" #include "JournallingObject.h" @@ -35,6 +38,10 @@ void Clipboard::copy( JournallingObject * _obj ) QDomElement parent = doc.createElement( "Clipboard" ); _obj->saveState( doc, parent ); content[_obj->nodeName()] = parent.firstChild().toElement(); + + // Clear the QApplication clipboard, so we don't have any conflicts when LMMS has to + // decide between the QApplication clipboard and the internal clipboard data + QApplication::clipboard()->clear( QClipboard::Clipboard ); } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 6505ffabda9..f79a34a15c9 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "AutomationPattern.h" @@ -734,6 +735,12 @@ void TrackContentObjectView::paintTextLabel(QString const & text, QPainter & pai */ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) { + // Right now, active is only used on right/mid clicks actions, so we use a ternary operator + // to avoid the overhead of calling getClickedTCOs when it's not used + auto active = me->button() == Qt::LeftButton + ? QVector() + : getClickedTCOs(); + setInitialPos( me->pos() ); setInitialOffsets(); if( !fixedTCOs() && me->button() == Qt::LeftButton ) @@ -826,22 +833,22 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) { if( me->modifiers() & Qt::ControlModifier ) { - m_tco->toggleMute(); + toggleMute( active ); } else if( me->modifiers() & Qt::ShiftModifier && !fixedTCOs() ) { - remove(); + remove( active ); } } else if( me->button() == Qt::MidButton ) { if( me->modifiers() & Qt::ControlModifier ) { - m_tco->toggleMute(); + toggleMute( active ); } else if( !fixedTCOs() ) { - remove(); + remove( active ); } } } @@ -1116,34 +1123,203 @@ void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me ) */ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) { + // Depending on whether we right-clicked a selection or an individual TCO we will have + // different labels for the actions. + bool individualTCO = getClickedTCOs().size() <= 1; + if( cme->modifiers() ) { return; } QMenu contextMenu( this ); + if( fixedTCOs() == false ) { - contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( "Delete (middle mousebutton)" ), - this, SLOT( remove() ) ); + contextMenu.addAction( + embed::getIconPixmap( "cancel" ), + tr( individualTCO + ? "Delete (middle mousebutton)" + : "Delete selection (middle mousebutton)" ), + [this](){ contextMenuAction( Remove ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( "Cut" ), this, SLOT( cut() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_cut" ), + tr( individualTCO + ? "Cut" + : "Cut selection" ), + [this](){ contextMenuAction( Cut ); } ); } - contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( "Copy" ), m_tco, SLOT( copy() ) ); - contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), m_tco, SLOT( paste() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_copy" ), + tr( individualTCO + ? "Copy" + : "Copy selection" ), + [this](){ contextMenuAction( Copy ); } ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), + [this](){ contextMenuAction( Paste ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( "Mute/unmute (<%1> + middle click)" ).arg(UI_CTRL_KEY), - m_tco, SLOT( toggleMute() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "muted" ), + tr( individualTCO + ? "Mute/unmute (<%1> + middle click)" + : "Mute/unmute selection (<%1> + middle click)" ).arg(UI_CTRL_KEY), + [this](){ contextMenuAction( Mute ); } ); + constructContextMenu( &contextMenu ); contextMenu.exec( QCursor::pos() ); } +// This method processes the actions from the context menu of the TCO View. +void TrackContentObjectView::contextMenuAction( ContextMenuAction action ) +{ + QVector active = getClickedTCOs(); + // active will be later used for the remove, copy, cut or toggleMute methods + + switch( action ) + { + case Remove: + remove( active ); + break; + case Cut: + cut( active ); + break; + case Copy: + copy( active ); + break; + case Paste: + paste(); + break; + case Mute: + toggleMute( active ); + break; + } +} + +QVector TrackContentObjectView::getClickedTCOs() +{ + // Get a list of selected selectableObjects + QVector sos = gui->songEditor()->m_editor->selectedObjects(); + + // Convert to a list of selected TCOVs + QVector selection; + selection.reserve( sos.size() ); + for( auto so: sos ) + { + TrackContentObjectView *tcov = dynamic_cast ( so ); + if( tcov != nullptr ) + { + selection.append( tcov ); + } + } + + // If we clicked part of the selection, affect all selected clips. Otherwise affect the clip we clicked + return selection.contains(this) + ? selection + : QVector( 1, this ); +} + +void TrackContentObjectView::remove( QVector tcovs ) +{ + for( auto tcov: tcovs ) + { + // No need to check if it's nullptr because we check when building the QVector + tcov->remove(); + } +} + +void TrackContentObjectView::copy( QVector tcovs ) +{ + // Checks if there are other selected TCOs and if so copy them as well + if( tcovs.size() > 1 ) + { + // Write the TCOs to a DataFile for copying + DataFile dataFile = createTCODataFiles( tcovs ); + + // Add the TCO type as a key to the final string + QString finalString = QString( "tco_%1:%2" ).arg( m_tco->getTrack()->type() ).arg( dataFile.toString() ); + + // Copy it to the clipboard + QMimeData *tco_content = new QMimeData; + tco_content->setData( StringPairDrag::mimeType(), finalString.toUtf8() ); + QApplication::clipboard()->setMimeData( tco_content, QClipboard::Clipboard ); + } + else + { + tcovs.at(0)->getTrackContentObject()->copy(); + } +} + +void TrackContentObjectView::cut( QVector tcovs ) +{ + // Checks if there are other selected TCOs and if so cut them as well + if( tcovs.size() > 1 ) + { + // Write the TCOs to a DataFile for copying + DataFile dataFile = createTCODataFiles( tcovs ); + + // Now that the dataFile is created we can delete the tracks, since we are cutting + // TODO: Is it safe to call tcov->remove(); on the current TCOV instance? + remove( tcovs ); + + // Add the TCO type as a key to the final string + QString finalString = QString( "tco_%1:%2" ).arg( m_tco->getTrack()->type() ).arg( dataFile.toString() ); + + // Copy it to the clipboard + QMimeData *tco_content = new QMimeData; + tco_content->setData( StringPairDrag::mimeType(), finalString.toUtf8() ); + QApplication::clipboard()->setMimeData( tco_content, QClipboard::Clipboard ); + } + else + { + tcovs.at(0)->cut(); + } +} + +void TrackContentObjectView::paste() +{ + // NOTE: Because we give preference to the QApplication clipboard over the LMMS Clipboard class, we need to + // clear the QApplication Clipboard during the LMMS Clipboard copy operations (Clipboard::copy does that) + + // If we have TCO data on the clipboard paste it. If not, do our regular TCO paste. + if( QApplication::clipboard()->mimeData( QClipboard::Clipboard )->hasFormat( StringPairDrag::mimeType() ) ) + { + // Paste the selection on the MidiTime of the selected Track + const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + + TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); + + if( tcw->pasteSelection( tcoPos, md ) == true ) + { + // If we succeed on the paste we delete the TCO we pasted on + remove(); + } + } + else + { + getTrackContentObject()->paste(); + } +} + +void TrackContentObjectView::toggleMute( QVector tcovs ) +{ + for( auto tcov: tcovs ) + { + // No need to check for nullptr because we check while building the tcovs QVector + tcov->getTrackContentObject()->toggleMute(); + } +} + @@ -1519,9 +1695,19 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d { const QMimeData * mimeData = de->mimeData(); + // If the source of the DropEvent is the current instance of LMMS we don't allow pasting in the same bar + // if it's another instance of LMMS we allow it + return de->source() + ? canPasteSelection( tcoPos, mimeData ) + : canPasteSelection( tcoPos, mimeData, true ); +} + +// Overloaded method to make it possible to call this method without a Drag&Drop event +bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData* md , bool allowSameBar ) +{ Track * t = getTrack(); - QString type = StringPairDrag::decodeMimeKey( mimeData ); - QString value = StringPairDrag::decodeMimeValue( mimeData ); + QString type = StringPairDrag::decodeMimeKey( md ); + QString value = StringPairDrag::decodeMimeValue( md ); // We can only paste into tracks of the same type if( type != ( "tco_" + QString::number( t->type() ) ) || @@ -1547,9 +1733,9 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); const int currentTrackIndex = tracks.indexOf( t ); - // Don't paste if we're on the same bar + // Don't paste if we're on the same bar and allowSameBar is false auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); - if( de->source() && sourceTrackContainerId == t->trackContainer()->id() && + if( !allowSameBar && sourceTrackContainerId == t->trackContainer()->id() && tcoPos == grabbedTCOBar && currentTrackIndex == initialTrackIndex ) { return false; @@ -1591,13 +1777,28 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d */ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) { + const QMimeData * mimeData = de->mimeData(); + if( canPasteSelection( tcoPos, de ) == false ) { return false; } - QString type = StringPairDrag::decodeKey( de ); - QString value = StringPairDrag::decodeValue( de ); + // We set skipSafetyCheck to true because we already called canPasteSelection + return pasteSelection( tcoPos, mimeData, true ); +} + +// Overloaded method so we can call it without a Drag&Drop event +bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck ) +{ + // When canPasteSelection was already called before, skipSafetyCheck will skip this + if( !skipSafetyCheck && canPasteSelection( tcoPos, md ) == false ) + { + return false; + } + + QString type = StringPairDrag::decodeMimeKey( md ); + QString value = StringPairDrag::decodeMimeValue( md ); getTrack()->addJournalCheckPoint(); @@ -1789,6 +1990,43 @@ MidiTime TrackContentWidget::endPosition( const MidiTime & posStart ) return posStart + static_cast( w * MidiTime::ticksPerBar() / ppb ); } +void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) +{ + if( cme->modifiers() ) + { + return; + } + + // If we don't have TCO data in the clipboard there's no need to create this menu + // since "paste" is the only action at the moment. + const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + if( !md->hasFormat( StringPairDrag::mimeType() ) ) + { + return; + } + + QMenu contextMenu( this ); + QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } ); + // If we can't paste in the current TCW for some reason, disable the action so the user knows + pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), md ) ? true : false ); + + contextMenu.exec( QCursor::pos() ); +} + +void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ) +{ + switch( action ) + { + case Paste: + // Paste the selection on the MidiTime of the context menu event + const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + MidiTime tcoPos = getPosition( cme->x() ); + + pasteSelection( tcoPos, md ); + break; + } +} diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 86e1861c691..0183684a180 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -343,29 +343,57 @@ void SampleTCOView::updateSample() void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) { + // Depending on whether we right-clicked a selection or an individual TCO we will have + // different labels for the actions. + bool individualTCO = getClickedTCOs().size() <= 1; + if( _cme->modifiers() ) { return; } QMenu contextMenu( this ); + if( fixedTCOs() == false ) { - contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( "Delete (middle mousebutton)" ), - this, SLOT( remove() ) ); + contextMenu.addAction( + embed::getIconPixmap( "cancel" ), + tr( individualTCO + ? "Delete (middle mousebutton)" + : "Delete selection (middle mousebutton)" ), + [this](){ contextMenuAction( Remove ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( "Cut" ), this, SLOT( cut() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_cut" ), + tr( individualTCO + ? "Cut" + : "Cut selection" ), + [this](){ contextMenuAction( Cut ); } ); } - contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( "Copy" ), m_tco, SLOT( copy() ) ); - contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), m_tco, SLOT( paste() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_copy" ), + tr( individualTCO + ? "Copy" + : "Copy selection" ), + [this](){ contextMenuAction( Copy ); } ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), + [this](){ contextMenuAction( Paste ); } ); + contextMenu.addSeparator(); - contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( "Mute/unmute (<%1> + middle click)" ).arg(UI_CTRL_KEY), - m_tco, SLOT( toggleMute() ) ); + + contextMenu.addAction( + embed::getIconPixmap( "muted" ), + tr( individualTCO + ? "Mute/unmute (<%1> + middle click)" + : "Mute/unmute selection (<%1> + middle click)" ).arg(UI_CTRL_KEY), + [this](){ contextMenuAction( Mute ); } ); + /*contextMenu.addAction( embed::getIconPixmap( "record" ), tr( "Set/clear record" ), m_tco, SLOT( toggleRecord() ) );*/ From 1bb8d12e9936bc9b5caab9b7f0d3c513da430fb2 Mon Sep 17 00:00:00 2001 From: Kumar Date: Tue, 11 Aug 2020 14:31:58 +0530 Subject: [PATCH 065/180] Enable mixer color-coding (#5589) * Enable mixer color-coding * Cleanup * Fix warnings * Improvements * Improvements * Use ColorChooser instead of QColorDialog * Fix default palette being out of range * Remove a redundant function * Rename and make stuff efficient * Comment on the code * Make things more efficient * Fix breaking builds * Improvements * Improvements pt. 2 * Improvements pt. 3 * Improvements pt. 4 * Improvements pt. 5 * Apply suggestions from code review Co-authored-by: Hyunjin Song Co-authored-by: Hyunjin Song --- include/ColorChooser.h | 22 +++++++- include/FxLine.h | 5 ++ include/FxMixer.h | 7 +++ src/core/FxMixer.cpp | 7 +++ src/gui/CMakeLists.txt | 1 + src/gui/dialogs/ColorChooser.cpp | 93 ++++++++++++++++++++++++++++++++ src/gui/widgets/FxLine.cpp | 58 ++++++++++++++++++-- 7 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 src/gui/dialogs/ColorChooser.cpp diff --git a/include/ColorChooser.h b/include/ColorChooser.h index fe5b7a22a4e..ac2a1b62d4a 100644 --- a/include/ColorChooser.h +++ b/include/ColorChooser.h @@ -21,21 +21,39 @@ * */ -#include #include +#include +#include #include +#include class ColorChooser: public QColorDialog { public: ColorChooser(const QColor &initial, QWidget *parent): QColorDialog(initial, parent) {}; ColorChooser(QWidget *parent): QColorDialog(parent) {}; + //! For getting a color without having to initialise a color dialog + ColorChooser() {}; + enum class Palette {Default, Track, Mixer}; + //! Set global palette via array, checking bounds + void setPalette (QVector); + //! Set global paletter via enum + void setPalette (Palette); + //! Set palette via enum, return self pointer for chaining + ColorChooser* withPalette (Palette); + //! Return a certain palette + static QVector getPalette (Palette); protected: - // Forward key events to the parent to prevent stuck notes when the dialog gets focus + //! Forward key events to the parent to prevent stuck notes when the dialog gets focus void keyReleaseEvent(QKeyEvent *event) override { QKeyEvent ke(*event); QApplication::sendEvent(parentWidget(), &ke); } +private: + //! Copy the current QColorDialog palette into an array + static QVector defaultPalette(); + //! Generate a nice palette, with adjustable value + static QVector nicePalette (int); }; diff --git a/include/FxLine.h b/include/FxLine.h index c16dcd5f598..e9ee248b811 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -26,10 +26,12 @@ #ifndef FX_LINE_H #define FX_LINE_H +#include #include #include #include +#include "ColorChooser.h" #include "Knob.h" #include "LcdWidget.h" #include "SendButtonIndicator.h" @@ -101,6 +103,9 @@ class FxLine : public QWidget public slots: void renameChannel(); + void resetColor(); + void changeColor(); + void randomColor(); private slots: void renameFinished(); diff --git a/include/FxMixer.h b/include/FxMixer.h index 68b69d9bc88..920d4e27bc3 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -32,6 +32,8 @@ #include +#include + class FxRoute; typedef QVector FxRouteVector; @@ -70,6 +72,11 @@ class FxChannel : public ThreadableJob bool requiresProcessing() const override { return true; } void unmuteForSolo(); + + // TODO C++17 and above: use std::optional insteads + QColor m_color; + bool m_hasColor; + std::atomic_int m_dependenciesMet; void incrementDeps(); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 4a68c3f3b05..7ddcbea623a 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -73,6 +73,7 @@ FxChannel::FxChannel( int idx, Model * _parent ) : m_lock(), m_channelIndex( idx ), m_queued( false ), + m_hasColor( false ), m_dependenciesMet(0) { BufferManager::clear( m_buffer, Engine::mixer()->framesPerPeriod() ); @@ -741,6 +742,7 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) ch->m_soloModel.saveSettings( _doc, fxch, "soloed" ); fxch.setAttribute( "num", i ); fxch.setAttribute( "name", ch->m_name ); + if( ch->m_hasColor ) fxch.setAttribute( "color", ch->m_color.name() ); // add the channel sends for( int si = 0; si < ch->m_sends.size(); ++si ) @@ -786,6 +788,11 @@ void FxMixer::loadSettings( const QDomElement & _this ) m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" ); m_fxChannels[num]->m_soloModel.loadSettings( fxch, "soloed" ); m_fxChannels[num]->m_name = fxch.attribute( "name" ); + if( fxch.hasAttribute( "color" ) ) + { + m_fxChannels[num]->m_hasColor = true; + m_fxChannels[num]->m_color.setNamedColor( fxch.attribute( "color" ) ); + } m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement( m_fxChannels[num]->m_fxChain.nodeName() ) ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3c6529e0b78..e1be929aab1 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -37,6 +37,7 @@ SET(LMMS_SRCS gui/dialogs/FileDialog.cpp gui/dialogs/VersionedSaveDialog.cpp + gui/dialogs/ColorChooser.cpp gui/editors/AutomationEditor.cpp gui/editors/BBEditor.cpp diff --git a/src/gui/dialogs/ColorChooser.cpp b/src/gui/dialogs/ColorChooser.cpp new file mode 100644 index 00000000000..b25aa97be6c --- /dev/null +++ b/src/gui/dialogs/ColorChooser.cpp @@ -0,0 +1,93 @@ +/* ColorChooser.cpp - definition of ColorChooser class. + * + * Copyright (c) 2020 russiankumar + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + + + + +//! Set global palette via array, checking bounds +void ColorChooser::setPalette (QVector colors) +{ + const int max = qMin (colors.size(), 48); + for (int i = 0; i < max; i++) + { + ColorChooser::setStandardColor (i, colors[i]); + } +} + + +//! Set global paletter via enum +void ColorChooser::setPalette (Palette palette) +{ + setPalette (getPalette (palette)); +} + + +//! Set palette via enum, return self pointer for chaining +ColorChooser* ColorChooser::withPalette (Palette palette) +{ + setPalette (palette); + return this; +} + + +//! Return a certain palette +QVector ColorChooser::getPalette (Palette palette) +{ + switch (palette) + { + case Palette::Mixer: return nicePalette(140); + case Palette::Track: return nicePalette(150); + default: return defaultPalette(); + } +} + + + + +//! Copy the current QColorDialog palette into an array +QVector ColorChooser::defaultPalette() +{ + QVector result (48); + for (int i = 0; i < 48; i++) + { + result[i] = (QColorDialog::standardColor(i)); + } + return result; +} + + +//! Generate a nice palette, with adjustable value +QVector ColorChooser::nicePalette (int base) +{ + QVector result (48); + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 6; y++) + { + result[6 * x + y].setHsl (qMax(0, 44 * x - 1), 150 - 20 * y, base - 10 * y); + } + } + return result; +} diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index 3314301fd77..476d8773b2b 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -25,6 +25,8 @@ #include "FxLine.h" +#include + #include #include "CaptionMenu.h" @@ -120,6 +122,8 @@ FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex ) : proxyWidget->setPos( 8, 145 ); connect( m_renameLineEdit, SIGNAL( editingFinished() ), this, SLOT( renameFinished() ) ); + connect( &Engine::fxMixer()->effectChannel( m_channelIndex )->m_muteModel, SIGNAL( dataChanged() ), this, SLOT( update() ) ); + } @@ -144,10 +148,11 @@ void FxLine::setChannelIndex( int index ) - void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool sendToThis, bool receiveFromThis ) { - QString name = Engine::fxMixer()->effectChannel( m_channelIndex )->m_name; + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + bool muted = channel->m_muteModel.value(); + QString name = channel->m_name; QString elidedName = elideName( name ); if( !m_inRename && m_renameLineEdit->text() != elidedName ) { @@ -156,8 +161,16 @@ void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool int width = fxLine->rect().width(); int height = fxLine->rect().height(); - - p->fillRect( fxLine->rect(), isActive ? fxLine->backgroundActive() : p->background() ); + + if( channel->m_hasColor && !muted ) + { + p->fillRect( fxLine->rect(), channel->m_color.darker( isActive ? 120 : 150 ) ); + } + else + { + p->fillRect( fxLine->rect(), + isActive ? fxLine->backgroundActive().color() : p->background().color() ); + } // inner border p->setPen( isActive ? fxLine->strokeInnerActive() : fxLine->strokeInnerInactive() ); @@ -224,7 +237,7 @@ void FxLine::mouseDoubleClickEvent( QMouseEvent * ) void FxLine::contextMenuEvent( QContextMenuEvent * ) { QPointer contextMenu = new CaptionMenu( Engine::fxMixer()->effectChannel( m_channelIndex )->m_name, this ); - if( m_channelIndex != 0 ) // no move-options in master + if( m_channelIndex != 0 ) // no move-options in master { contextMenu->addAction( tr( "Move &left" ), this, SLOT( moveChannelLeft() ) ); contextMenu->addAction( tr( "Move &right" ), this, SLOT( moveChannelRight() ) ); @@ -238,6 +251,10 @@ void FxLine::contextMenuEvent( QContextMenuEvent * ) contextMenu->addSeparator(); } contextMenu->addAction( embed::getIconPixmap( "cancel" ), tr( "Remove &unused channels" ), this, SLOT( removeUnusedChannels() ) ); + contextMenu->addSeparator(); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Set channel color" ), this, SLOT( changeColor() ) ); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Remove channel color" ), this, SLOT( resetColor() ) ); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Pick random channel color" ), this, SLOT( randomColor() ) ); contextMenu->exec( QCursor::pos() ); delete contextMenu; } @@ -395,3 +412,34 @@ void FxLine::setStrokeInnerInactive( const QColor & c ) { m_strokeInnerInactive = c; } + + +// Ask user for a color, and set it as the mixer line color +void FxLine::changeColor() +{ + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + auto new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Mixer )->getColor( channel->m_color ); + if( ! new_color.isValid() ) + { return; } + channel->m_color = new_color; + channel->m_hasColor = true; + update(); +} + + +// Disable the usage of color on this mixer line +void FxLine::resetColor() +{ + Engine::fxMixer()->effectChannel( m_channelIndex )->m_hasColor = false; + update(); +} + + +// Pick a random color from the mixer palette and set it as our color +void FxLine::randomColor() +{ + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + channel->m_color = ColorChooser::getPalette( ColorChooser::Palette::Mixer )[ rand() % 48 ]; + channel->m_hasColor = true; + update(); +} From 2fe06c8f3c849a0d8ce56910e66a1c9b85d3a460 Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 11 Aug 2020 14:57:27 +0200 Subject: [PATCH 066/180] Replace iterator where possible --- src/core/AutomationPattern.cpp | 259 ++++++++++++++------------------- 1 file changed, 107 insertions(+), 152 deletions(-) diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 2fd1cea125f..3098123aaaf 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -72,29 +72,30 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : -AutomationPattern::AutomationPattern( const AutomationPattern & _pat_to_copy ) : - TrackContentObject( _pat_to_copy.m_autoTrack ), - m_autoTrack( _pat_to_copy.m_autoTrack ), - m_objects( _pat_to_copy.m_objects ), - m_tension( _pat_to_copy.m_tension ), - m_progressionType( _pat_to_copy.m_progressionType ) -{ - for( timeMap::const_iterator it = _pat_to_copy.m_timeMap.begin(); - it != _pat_to_copy.m_timeMap.end(); ++it ) - { - m_timeMap[it.key()] = it.value(); - m_tangents[it.key()] = _pat_to_copy.m_tangents[it.key()]; - } - switch( getTrack()->trackContainer()->type() ) +AutomationPattern::AutomationPattern(const AutomationPattern& _pat_to_copy) : + TrackContentObject(_pat_to_copy.m_autoTrack), + m_autoTrack(_pat_to_copy.m_autoTrack), + m_objects(_pat_to_copy.m_objects), + m_tension(_pat_to_copy.m_tension), + m_progressionType(_pat_to_copy.m_progressionType) +{ + m_timeMap = timeMap(_pat_to_copy.m_timeMap); + m_tangents = timeMap(_pat_to_copy.m_tangents); + // Force a deep copy, because the previous implementation did this manually. + // TODO: Do we really need to do this? + m_timeMap.detach(); + m_tangents.detach(); + + switch (getTrack()->trackContainer()->type()) { case TrackContainer::BBContainer: - setAutoResize( true ); + setAutoResize(true); break; case TrackContainer::SongContainer: // move down default: - setAutoResize( false ); + setAutoResize(false); break; } } @@ -178,16 +179,10 @@ const AutomationPattern::objectVector& AutomationPattern::objects() const MidiTime AutomationPattern::timeMapLength() const { - MidiTime one_bar = MidiTime(1, 0); - if (m_timeMap.isEmpty()) { return one_bar; } - - timeMap::const_iterator it = m_timeMap.end(); - tick_t last_tick = static_cast((it-1).key()); - // if last_tick is 0 (single item at tick 0) - // return length as a whole bar to prevent disappearing TCO - if (last_tick == 0) { return one_bar; } - - return MidiTime(last_tick); + // Find the time of the last point in the timemap, defaulting to 0 if it's empty + tick_t lastTick = static_cast(m_timeMap.isEmpty() ? 0 : m_timeMap.lastKey()); + // Return one bar if the pattern is empty, or only contains a point at time zero + return (lastTick == 0) ? MidiTime(1, 0) : MidiTime(lastTick); } @@ -530,33 +525,30 @@ void AutomationPattern::flipX( int length ) -void AutomationPattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) +void AutomationPattern::saveSettings(QDomDocument & _doc, QDomElement & _this) { - _this.setAttribute( "pos", startPosition() ); - _this.setAttribute( "len", length() ); - _this.setAttribute( "name", name() ); - _this.setAttribute( "prog", QString::number( progressionType() ) ); - _this.setAttribute( "tens", QString::number( getTension() ) ); - _this.setAttribute( "mute", QString::number( isMuted() ) ); + _this.setAttribute("pos", startPosition()); + _this.setAttribute("len", length()); + _this.setAttribute("name", name()); + _this.setAttribute("prog", QString::number(progressionType())); + _this.setAttribute("tens", QString::number(getTension())); + _this.setAttribute("mute", QString::number(isMuted())); - for( timeMap::const_iterator it = m_timeMap.begin(); - it != m_timeMap.end(); ++it ) + for (timeMap::const_iterator it = m_timeMap.begin(); it != m_timeMap.end(); ++it) { - QDomElement element = _doc.createElement( "time" ); - element.setAttribute( "pos", it.key() ); - element.setAttribute( "value", it.value() ); - _this.appendChild( element ); + QDomElement element = _doc.createElement("time"); + element.setAttribute("pos", it.key()); + element.setAttribute("value", it.value()); + _this.appendChild(element); } - for( objectVector::const_iterator it = m_objects.begin(); - it != m_objects.end(); ++it ) + for (const auto model: m_objects) { - if( *it ) + if (!model.isNull()) { - QDomElement element = _doc.createElement( "object" ); - element.setAttribute( "id", - ProjectJournal::idToSave( ( *it )->id() ) ); - _this.appendChild( element ); + QDomElement element = _doc.createElement("object"); + element.setAttribute("id", ProjectJournal::idToSave(model->id())); + _this.appendChild(element); } } } @@ -635,30 +627,26 @@ TrackContentObjectView * AutomationPattern::createView( TrackView * _tv ) -bool AutomationPattern::isAutomated( const AutomatableModel * _m ) +bool AutomationPattern::isAutomated(const AutomatableModel* _m) { - TrackContainer::TrackList l; - l += Engine::getSong()->tracks(); - l += Engine::getBBTrackContainer()->tracks(); - l += Engine::getSong()->globalAutomationTrack(); + TrackContainer::TrackList trackList; + trackList += Engine::getSong()->tracks(); + trackList += Engine::getBBTrackContainer()->tracks(); + trackList += Engine::getSong()->globalAutomationTrack(); - for( TrackContainer::TrackList::ConstIterator it = l.begin(); it != l.end(); ++it ) + for (const Track* track: trackList) { - if( ( *it )->type() == Track::AutomationTrack || - ( *it )->type() == Track::HiddenAutomationTrack ) + if (track->type() == Track::AutomationTrack || + track->type() == Track::HiddenAutomationTrack) { - const Track::tcoVector & v = ( *it )->getTCOs(); - for( Track::tcoVector::ConstIterator j = v.begin(); j != v.end(); ++j ) + const Track::tcoVector& tcoVector = track->getTCOs(); + for (const TrackContentObject* tco: tcoVector) { - const AutomationPattern * a = dynamic_cast( *j ); - if( a && a->hasAutomation() ) + const AutomationPattern * a = dynamic_cast(tco); + if (a && a->hasAutomation()) { - for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k ) - { - if( *k == _m ) - { - return true; - } + for (const auto model: a->m_objects) { + if(model == _m) { return true; } } } } @@ -671,42 +659,39 @@ bool AutomationPattern::isAutomated( const AutomatableModel * _m ) /*! \brief returns a list of all the automation patterns everywhere that are connected to a specific model * \param _m the model we want to look for */ -QVector AutomationPattern::patternsForModel( const AutomatableModel * _m ) +QVector AutomationPattern::patternsForModel(const AutomatableModel* _m) { - QVector patterns; - TrackContainer::TrackList l; - l += Engine::getSong()->tracks(); - l += Engine::getBBTrackContainer()->tracks(); - l += Engine::getSong()->globalAutomationTrack(); + QVector patterns; + TrackContainer::TrackList trackList; + trackList += Engine::getSong()->tracks(); + trackList += Engine::getBBTrackContainer()->tracks(); + trackList += Engine::getSong()->globalAutomationTrack(); // go through all tracks... - for( TrackContainer::TrackList::ConstIterator it = l.begin(); it != l.end(); ++it ) + for (const Track* track: trackList) { // we want only automation tracks... - if( ( *it )->type() == Track::AutomationTrack || - ( *it )->type() == Track::HiddenAutomationTrack ) + if (track->type() == Track::AutomationTrack || + track->type() == Track::HiddenAutomationTrack ) { // get patterns in those tracks.... - const Track::tcoVector & v = ( *it )->getTCOs(); + const Track::tcoVector& tcoVector = track->getTCOs(); // go through all the patterns... - for( Track::tcoVector::ConstIterator j = v.begin(); j != v.end(); ++j ) + for (TrackContentObject* tco: tcoVector) { - AutomationPattern * a = dynamic_cast( *j ); + auto a = dynamic_cast(tco); // check that the pattern has automation - if( a && a->hasAutomation() ) + if (a && a->hasAutomation()) { // now check is the pattern is connected to the model we want by going through all the connections // of the pattern bool has_object = false; - for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k ) + for (const auto model: a->m_objects) { - if( *k == _m ) - { - has_object = true; - } + if (model == _m) { has_object = true; } } // if the patterns is connected to the model, add it to the list - if( has_object ) { patterns += a; } + if (has_object) { patterns += a; } } } } @@ -716,29 +701,24 @@ QVector AutomationPattern::patternsForModel( const Automata -AutomationPattern * AutomationPattern::globalAutomationPattern( - AutomatableModel * _m ) +AutomationPattern* AutomationPattern::globalAutomationPattern(AutomatableModel* _m) { - AutomationTrack * t = Engine::getSong()->globalAutomationTrack(); - Track::tcoVector v = t->getTCOs(); - for( Track::tcoVector::const_iterator j = v.begin(); j != v.end(); ++j ) + AutomationTrack* track = Engine::getSong()->globalAutomationTrack(); + Track::tcoVector tcoVector = track->getTCOs(); + for (TrackContentObject* tco: tcoVector) { - AutomationPattern * a = dynamic_cast( *j ); - if( a ) + auto a = dynamic_cast(tco); + if (a) { - for( objectVector::const_iterator k = a->m_objects.begin(); - k != a->m_objects.end(); ++k ) + for (const auto model: a->m_objects) { - if( *k == _m ) - { - return a; - } + if (model == _m) { return a; } } } } - AutomationPattern * a = new AutomationPattern( t ); - a->addObject( _m, false ); + AutomationPattern* a = new AutomationPattern(track); + a->addObject(_m, false); return a; } @@ -747,51 +727,37 @@ AutomationPattern * AutomationPattern::globalAutomationPattern( void AutomationPattern::resolveAllIDs() { - TrackContainer::TrackList l = Engine::getSong()->tracks() + - Engine::getBBTrackContainer()->tracks(); - l += Engine::getSong()->globalAutomationTrack(); - for( TrackContainer::TrackList::iterator it = l.begin(); - it != l.end(); ++it ) + TrackContainer::TrackList trackList = Engine::getSong()->tracks(); + trackList += Engine::getBBTrackContainer()->tracks(); + trackList += Engine::getSong()->globalAutomationTrack(); + + for (const Track* track: trackList) { - if( ( *it )->type() == Track::AutomationTrack || - ( *it )->type() == Track::HiddenAutomationTrack ) + if (track->type() == Track::AutomationTrack || + track->type() == Track::HiddenAutomationTrack) { - Track::tcoVector v = ( *it )->getTCOs(); - for( Track::tcoVector::iterator j = v.begin(); - j != v.end(); ++j ) + Track::tcoVector tcoVector = track->getTCOs(); + for (TrackContentObject* tco: tcoVector) { - AutomationPattern * a = dynamic_cast( *j ); - if( a ) + AutomationPattern* a = dynamic_cast(tco); + if (a) { - for( QVector::Iterator k = a->m_idsToResolve.begin(); - k != a->m_idsToResolve.end(); ++k ) + for (const jo_id_t id: a->m_idsToResolve) { - JournallingObject * o = Engine::projectJournal()-> - journallingObject( *k ); - if( o && dynamic_cast( o ) ) - { - a->addObject( dynamic_cast( o ), false ); - } - else - { - // FIXME: Remove this block once the automation system gets fixed - // This is a temporary fix for https://github.com/LMMS/lmms/issues/3781 - o = Engine::projectJournal()->journallingObject(ProjectJournal::idFromSave(*k)); - if( o && dynamic_cast( o ) ) - { - a->addObject( dynamic_cast( o ), false ); - } - else - { - // FIXME: Remove this block once the automation system gets fixed - // This is a temporary fix for https://github.com/LMMS/lmms/issues/4781 - o = Engine::projectJournal()->journallingObject(ProjectJournal::idToSave(*k)); - if( o && dynamic_cast( o ) ) - { - a->addObject( dynamic_cast( o ), false ); - } - } - } + JournallingObject* o = Engine::projectJournal()-> + journallingObject(id); + AutomatableModel* am = dynamic_cast(o); + if (o && am) { a->addObject(am, false); continue; } + + // FIXME: Remove this block once the automation system gets fixed + // This is a temporary fix for https://github.com/LMMS/lmms/issues/3781 + o = Engine::projectJournal()->journallingObject(ProjectJournal::idFromSave(id)); + if (o && am) { a->addObject(am, false); continue; } + + // FIXME: Remove this block once the automation system gets fixed + // This is a temporary fix for https://github.com/LMMS/lmms/issues/4781 + o = Engine::projectJournal()->journallingObject(ProjectJournal::idToSave(id)); + if (o && am) { a->addObject(am, false); } } a->m_idsToResolve.clear(); a->dataChanged(); @@ -843,16 +809,10 @@ void AutomationPattern::objectDestroyed( jo_id_t _id ) void AutomationPattern::cleanObjects() { - for( objectVector::iterator it = m_objects.begin(); it != m_objects.end(); ) + for (objectVector::iterator it = m_objects.begin(); it != m_objects.end();) { - if( *it ) - { - ++it; - } - else - { - it = m_objects.erase( it ); - } + if (*it) { ++it; } + else { it = m_objects.erase(it); } } } @@ -898,8 +858,3 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it, it++; } } - - - - - From 139e492b1786bf1214e54b029f3c99b8bf609df3 Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 11 Aug 2020 14:58:54 +0200 Subject: [PATCH 067/180] Revert "Replace iterator where possible" This reverts commit 2fe06c8f3c849a0d8ce56910e66a1c9b85d3a460. --- src/core/AutomationPattern.cpp | 259 +++++++++++++++++++-------------- 1 file changed, 152 insertions(+), 107 deletions(-) diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 3098123aaaf..2fd1cea125f 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -72,30 +72,29 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : -AutomationPattern::AutomationPattern(const AutomationPattern& _pat_to_copy) : - TrackContentObject(_pat_to_copy.m_autoTrack), - m_autoTrack(_pat_to_copy.m_autoTrack), - m_objects(_pat_to_copy.m_objects), - m_tension(_pat_to_copy.m_tension), - m_progressionType(_pat_to_copy.m_progressionType) -{ - m_timeMap = timeMap(_pat_to_copy.m_timeMap); - m_tangents = timeMap(_pat_to_copy.m_tangents); - // Force a deep copy, because the previous implementation did this manually. - // TODO: Do we really need to do this? - m_timeMap.detach(); - m_tangents.detach(); - - switch (getTrack()->trackContainer()->type()) +AutomationPattern::AutomationPattern( const AutomationPattern & _pat_to_copy ) : + TrackContentObject( _pat_to_copy.m_autoTrack ), + m_autoTrack( _pat_to_copy.m_autoTrack ), + m_objects( _pat_to_copy.m_objects ), + m_tension( _pat_to_copy.m_tension ), + m_progressionType( _pat_to_copy.m_progressionType ) +{ + for( timeMap::const_iterator it = _pat_to_copy.m_timeMap.begin(); + it != _pat_to_copy.m_timeMap.end(); ++it ) + { + m_timeMap[it.key()] = it.value(); + m_tangents[it.key()] = _pat_to_copy.m_tangents[it.key()]; + } + switch( getTrack()->trackContainer()->type() ) { case TrackContainer::BBContainer: - setAutoResize(true); + setAutoResize( true ); break; case TrackContainer::SongContainer: // move down default: - setAutoResize(false); + setAutoResize( false ); break; } } @@ -179,10 +178,16 @@ const AutomationPattern::objectVector& AutomationPattern::objects() const MidiTime AutomationPattern::timeMapLength() const { - // Find the time of the last point in the timemap, defaulting to 0 if it's empty - tick_t lastTick = static_cast(m_timeMap.isEmpty() ? 0 : m_timeMap.lastKey()); - // Return one bar if the pattern is empty, or only contains a point at time zero - return (lastTick == 0) ? MidiTime(1, 0) : MidiTime(lastTick); + MidiTime one_bar = MidiTime(1, 0); + if (m_timeMap.isEmpty()) { return one_bar; } + + timeMap::const_iterator it = m_timeMap.end(); + tick_t last_tick = static_cast((it-1).key()); + // if last_tick is 0 (single item at tick 0) + // return length as a whole bar to prevent disappearing TCO + if (last_tick == 0) { return one_bar; } + + return MidiTime(last_tick); } @@ -525,30 +530,33 @@ void AutomationPattern::flipX( int length ) -void AutomationPattern::saveSettings(QDomDocument & _doc, QDomElement & _this) +void AutomationPattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - _this.setAttribute("pos", startPosition()); - _this.setAttribute("len", length()); - _this.setAttribute("name", name()); - _this.setAttribute("prog", QString::number(progressionType())); - _this.setAttribute("tens", QString::number(getTension())); - _this.setAttribute("mute", QString::number(isMuted())); + _this.setAttribute( "pos", startPosition() ); + _this.setAttribute( "len", length() ); + _this.setAttribute( "name", name() ); + _this.setAttribute( "prog", QString::number( progressionType() ) ); + _this.setAttribute( "tens", QString::number( getTension() ) ); + _this.setAttribute( "mute", QString::number( isMuted() ) ); - for (timeMap::const_iterator it = m_timeMap.begin(); it != m_timeMap.end(); ++it) + for( timeMap::const_iterator it = m_timeMap.begin(); + it != m_timeMap.end(); ++it ) { - QDomElement element = _doc.createElement("time"); - element.setAttribute("pos", it.key()); - element.setAttribute("value", it.value()); - _this.appendChild(element); + QDomElement element = _doc.createElement( "time" ); + element.setAttribute( "pos", it.key() ); + element.setAttribute( "value", it.value() ); + _this.appendChild( element ); } - for (const auto model: m_objects) + for( objectVector::const_iterator it = m_objects.begin(); + it != m_objects.end(); ++it ) { - if (!model.isNull()) + if( *it ) { - QDomElement element = _doc.createElement("object"); - element.setAttribute("id", ProjectJournal::idToSave(model->id())); - _this.appendChild(element); + QDomElement element = _doc.createElement( "object" ); + element.setAttribute( "id", + ProjectJournal::idToSave( ( *it )->id() ) ); + _this.appendChild( element ); } } } @@ -627,26 +635,30 @@ TrackContentObjectView * AutomationPattern::createView( TrackView * _tv ) -bool AutomationPattern::isAutomated(const AutomatableModel* _m) +bool AutomationPattern::isAutomated( const AutomatableModel * _m ) { - TrackContainer::TrackList trackList; - trackList += Engine::getSong()->tracks(); - trackList += Engine::getBBTrackContainer()->tracks(); - trackList += Engine::getSong()->globalAutomationTrack(); + TrackContainer::TrackList l; + l += Engine::getSong()->tracks(); + l += Engine::getBBTrackContainer()->tracks(); + l += Engine::getSong()->globalAutomationTrack(); - for (const Track* track: trackList) + for( TrackContainer::TrackList::ConstIterator it = l.begin(); it != l.end(); ++it ) { - if (track->type() == Track::AutomationTrack || - track->type() == Track::HiddenAutomationTrack) + if( ( *it )->type() == Track::AutomationTrack || + ( *it )->type() == Track::HiddenAutomationTrack ) { - const Track::tcoVector& tcoVector = track->getTCOs(); - for (const TrackContentObject* tco: tcoVector) + const Track::tcoVector & v = ( *it )->getTCOs(); + for( Track::tcoVector::ConstIterator j = v.begin(); j != v.end(); ++j ) { - const AutomationPattern * a = dynamic_cast(tco); - if (a && a->hasAutomation()) + const AutomationPattern * a = dynamic_cast( *j ); + if( a && a->hasAutomation() ) { - for (const auto model: a->m_objects) { - if(model == _m) { return true; } + for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k ) + { + if( *k == _m ) + { + return true; + } } } } @@ -659,39 +671,42 @@ bool AutomationPattern::isAutomated(const AutomatableModel* _m) /*! \brief returns a list of all the automation patterns everywhere that are connected to a specific model * \param _m the model we want to look for */ -QVector AutomationPattern::patternsForModel(const AutomatableModel* _m) +QVector AutomationPattern::patternsForModel( const AutomatableModel * _m ) { - QVector patterns; - TrackContainer::TrackList trackList; - trackList += Engine::getSong()->tracks(); - trackList += Engine::getBBTrackContainer()->tracks(); - trackList += Engine::getSong()->globalAutomationTrack(); + QVector patterns; + TrackContainer::TrackList l; + l += Engine::getSong()->tracks(); + l += Engine::getBBTrackContainer()->tracks(); + l += Engine::getSong()->globalAutomationTrack(); // go through all tracks... - for (const Track* track: trackList) + for( TrackContainer::TrackList::ConstIterator it = l.begin(); it != l.end(); ++it ) { // we want only automation tracks... - if (track->type() == Track::AutomationTrack || - track->type() == Track::HiddenAutomationTrack ) + if( ( *it )->type() == Track::AutomationTrack || + ( *it )->type() == Track::HiddenAutomationTrack ) { // get patterns in those tracks.... - const Track::tcoVector& tcoVector = track->getTCOs(); + const Track::tcoVector & v = ( *it )->getTCOs(); // go through all the patterns... - for (TrackContentObject* tco: tcoVector) + for( Track::tcoVector::ConstIterator j = v.begin(); j != v.end(); ++j ) { - auto a = dynamic_cast(tco); + AutomationPattern * a = dynamic_cast( *j ); // check that the pattern has automation - if (a && a->hasAutomation()) + if( a && a->hasAutomation() ) { // now check is the pattern is connected to the model we want by going through all the connections // of the pattern bool has_object = false; - for (const auto model: a->m_objects) + for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k ) { - if (model == _m) { has_object = true; } + if( *k == _m ) + { + has_object = true; + } } // if the patterns is connected to the model, add it to the list - if (has_object) { patterns += a; } + if( has_object ) { patterns += a; } } } } @@ -701,24 +716,29 @@ QVector AutomationPattern::patternsForModel(const Automatab -AutomationPattern* AutomationPattern::globalAutomationPattern(AutomatableModel* _m) +AutomationPattern * AutomationPattern::globalAutomationPattern( + AutomatableModel * _m ) { - AutomationTrack* track = Engine::getSong()->globalAutomationTrack(); - Track::tcoVector tcoVector = track->getTCOs(); - for (TrackContentObject* tco: tcoVector) + AutomationTrack * t = Engine::getSong()->globalAutomationTrack(); + Track::tcoVector v = t->getTCOs(); + for( Track::tcoVector::const_iterator j = v.begin(); j != v.end(); ++j ) { - auto a = dynamic_cast(tco); - if (a) + AutomationPattern * a = dynamic_cast( *j ); + if( a ) { - for (const auto model: a->m_objects) + for( objectVector::const_iterator k = a->m_objects.begin(); + k != a->m_objects.end(); ++k ) { - if (model == _m) { return a; } + if( *k == _m ) + { + return a; + } } } } - AutomationPattern* a = new AutomationPattern(track); - a->addObject(_m, false); + AutomationPattern * a = new AutomationPattern( t ); + a->addObject( _m, false ); return a; } @@ -727,37 +747,51 @@ AutomationPattern* AutomationPattern::globalAutomationPattern(AutomatableModel* void AutomationPattern::resolveAllIDs() { - TrackContainer::TrackList trackList = Engine::getSong()->tracks(); - trackList += Engine::getBBTrackContainer()->tracks(); - trackList += Engine::getSong()->globalAutomationTrack(); - - for (const Track* track: trackList) + TrackContainer::TrackList l = Engine::getSong()->tracks() + + Engine::getBBTrackContainer()->tracks(); + l += Engine::getSong()->globalAutomationTrack(); + for( TrackContainer::TrackList::iterator it = l.begin(); + it != l.end(); ++it ) { - if (track->type() == Track::AutomationTrack || - track->type() == Track::HiddenAutomationTrack) + if( ( *it )->type() == Track::AutomationTrack || + ( *it )->type() == Track::HiddenAutomationTrack ) { - Track::tcoVector tcoVector = track->getTCOs(); - for (TrackContentObject* tco: tcoVector) + Track::tcoVector v = ( *it )->getTCOs(); + for( Track::tcoVector::iterator j = v.begin(); + j != v.end(); ++j ) { - AutomationPattern* a = dynamic_cast(tco); - if (a) + AutomationPattern * a = dynamic_cast( *j ); + if( a ) { - for (const jo_id_t id: a->m_idsToResolve) + for( QVector::Iterator k = a->m_idsToResolve.begin(); + k != a->m_idsToResolve.end(); ++k ) { - JournallingObject* o = Engine::projectJournal()-> - journallingObject(id); - AutomatableModel* am = dynamic_cast(o); - if (o && am) { a->addObject(am, false); continue; } - - // FIXME: Remove this block once the automation system gets fixed - // This is a temporary fix for https://github.com/LMMS/lmms/issues/3781 - o = Engine::projectJournal()->journallingObject(ProjectJournal::idFromSave(id)); - if (o && am) { a->addObject(am, false); continue; } - - // FIXME: Remove this block once the automation system gets fixed - // This is a temporary fix for https://github.com/LMMS/lmms/issues/4781 - o = Engine::projectJournal()->journallingObject(ProjectJournal::idToSave(id)); - if (o && am) { a->addObject(am, false); } + JournallingObject * o = Engine::projectJournal()-> + journallingObject( *k ); + if( o && dynamic_cast( o ) ) + { + a->addObject( dynamic_cast( o ), false ); + } + else + { + // FIXME: Remove this block once the automation system gets fixed + // This is a temporary fix for https://github.com/LMMS/lmms/issues/3781 + o = Engine::projectJournal()->journallingObject(ProjectJournal::idFromSave(*k)); + if( o && dynamic_cast( o ) ) + { + a->addObject( dynamic_cast( o ), false ); + } + else + { + // FIXME: Remove this block once the automation system gets fixed + // This is a temporary fix for https://github.com/LMMS/lmms/issues/4781 + o = Engine::projectJournal()->journallingObject(ProjectJournal::idToSave(*k)); + if( o && dynamic_cast( o ) ) + { + a->addObject( dynamic_cast( o ), false ); + } + } + } } a->m_idsToResolve.clear(); a->dataChanged(); @@ -809,10 +843,16 @@ void AutomationPattern::objectDestroyed( jo_id_t _id ) void AutomationPattern::cleanObjects() { - for (objectVector::iterator it = m_objects.begin(); it != m_objects.end();) + for( objectVector::iterator it = m_objects.begin(); it != m_objects.end(); ) { - if (*it) { ++it; } - else { it = m_objects.erase(it); } + if( *it ) + { + ++it; + } + else + { + it = m_objects.erase( it ); + } } } @@ -858,3 +898,8 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it, it++; } } + + + + + From 966f18423f388448363101ea99692b8ba05dedb5 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Tue, 11 Aug 2020 10:18:34 -0500 Subject: [PATCH 068/180] Center vertical scroll position when opening the Automation Editor (#5123) * Center vertical scroll position when opening the Automation Editor --- include/AutomationEditor.h | 1 + src/gui/editors/AutomationEditor.cpp | 31 ++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 0a288f8afcf..4813759697f 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -209,6 +209,7 @@ protected slots: float m_bottomLevel; float m_topLevel; + void centerTopBottomScroll(); void updateTopBottomLevels(); QScrollBar * m_leftRightScroll; diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 8c281bf3e1f..221b01aca85 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -320,7 +320,7 @@ void AutomationEditor::updateAfterPatternChange() m_minLevel = m_pattern->firstObject()->minValue(); m_maxLevel = m_pattern->firstObject()->maxValue(); m_step = m_pattern->firstObject()->step(); - m_scrollLevel = ( m_minLevel + m_maxLevel ) / 2; + centerTopBottomScroll(); m_tensionModel->setValue( m_pattern->getTension() ); @@ -1595,12 +1595,36 @@ void AutomationEditor::drawLevelTick(QPainter & p, int tick, float value) p.fillRect( x, y_start, rect_width, rect_height, currentColor ); } - +#ifdef LMMS_DEBUG else { printf("not in range\n"); } +#endif +} + + + +// center the vertical scroll position on the first object's value +void AutomationEditor::centerTopBottomScroll() +{ + // default to the m_scrollLevel position + int pos = static_cast(m_scrollLevel); + // If a pattern exists... + if (m_pattern) + { + // get time map of current pattern + timeMap & time_map = m_pattern->getTimeMap(); + // If time_map is not empty... + if (!time_map.empty()) + { + // set the position to the inverted value ((max + min) - value) + // If we set just (max - value), we're off by m_pattern's minimum + pos = m_pattern->getMax() + m_pattern->getMin() - static_cast(time_map.begin().value()); + } + } + m_topBottomScroll->setValue(pos); } @@ -1632,8 +1656,7 @@ void AutomationEditor::resizeEvent(QResizeEvent * re) m_topBottomScroll->setRange( (int) m_scrollLevel, (int) m_scrollLevel ); } - - m_topBottomScroll->setValue( (int) m_scrollLevel ); + centerTopBottomScroll(); if( Engine::getSong() ) { From 1c1575cc86b24e8fbbf2b5a4894863a8203647b9 Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Thu, 13 Aug 2020 17:18:21 +0200 Subject: [PATCH 069/180] Change the background color of the selected text in a text box. (#5628) * Change the background color of the selected text in a text box. The new background color matches the green background of selected items in a tree view. * Add selection-background-color for QLineEdit widgets in the classic theme, but keep the color as it was. --- data/themes/classic/style.css | 1 + data/themes/default/style.css | 1 + 2 files changed, 2 insertions(+) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 3e64c9da5a9..65b496617f1 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -35,6 +35,7 @@ QLineEdit { border: 2px inset rgba(91,101,113,128); background: #49515b; color: #e0e0e0; + selection-background-color: #202020; } diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 4dee86788ec..832da176f28 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -62,6 +62,7 @@ QLineEdit { border: 1px; background: #101213; color: #d1d8e4; + selection-background-color: #17793b; } QLineEdit:read-only { From 9ed41c4927ca4ba66a0c367f8ee6da4f0b3a1d01 Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Mon, 17 Aug 2020 16:12:49 +0200 Subject: [PATCH 070/180] Fix for Icons and comboboxes mismatch in arpeggiator in Instrument Editor #5494 (#5623) * Fix for Icons and comboboxes mismatch in arpeggiator in Instrument Editor #5494 (https://github.com/LMMS/lmms/issues/5494) Introduce a static const int variable for the default height of a ComboBox. Set this height already in the constructor of the ComboBox object. Update all modules setting the height of a ComboBox object to make use of the new constant. * Replace 'const int' by 'constexpr int' after review. --- include/ComboBox.h | 4 ++-- plugins/DualFilter/DualFilterControlDialog.cpp | 4 ++-- plugins/SpectrumAnalyzer/SaControlsDialog.cpp | 12 ++++++------ .../audio_file_processor/audio_file_processor.cpp | 2 +- plugins/monstro/Monstro.cpp | 10 +++++----- plugins/stk/mallets/mallets.cpp | 2 +- src/core/audio/AudioPortAudio.cpp | 4 ++-- src/gui/ControllerConnectionDialog.cpp | 2 +- src/gui/editors/AutomationEditor.cpp | 6 +++--- src/gui/editors/BBEditor.cpp | 2 +- src/gui/editors/PianoRoll.cpp | 12 ++++++------ src/gui/editors/SongEditor.cpp | 4 ++-- src/gui/widgets/ComboBox.cpp | 2 ++ src/gui/widgets/Controls.cpp | 2 +- src/gui/widgets/InstrumentSoundShapingView.cpp | 2 +- 15 files changed, 36 insertions(+), 34 deletions(-) diff --git a/include/ComboBox.h b/include/ComboBox.h index d530c9d92f7..1ab1c240d80 100644 --- a/include/ComboBox.h +++ b/include/ComboBox.h @@ -32,8 +32,6 @@ #include "ComboBoxModel.h" #include "AutomatableModelView.h" - - class LMMS_EXPORT ComboBox : public QWidget, public IntModelView { Q_OBJECT @@ -51,6 +49,8 @@ class LMMS_EXPORT ComboBox : public QWidget, public IntModelView return castModel(); } + static constexpr int DEFAULT_HEIGHT = 22; + public slots: void selectNext(); void selectPrevious(); diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp index 73755bef38f..0c7732bc6c2 100755 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -75,12 +75,12 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) ToolTip::add( enabled2Toggle, tr( "Enable/disable filter 2" ) ); ComboBox * m_filter1ComboBox = new ComboBox( this ); - m_filter1ComboBox->setGeometry( 19, 70, 137, 22 ); + m_filter1ComboBox->setGeometry( 19, 70, 137, ComboBox::DEFAULT_HEIGHT ); m_filter1ComboBox->setFont( pointSize<8>( m_filter1ComboBox->font() ) ); m_filter1ComboBox->setModel( &controls->m_filter1Model ); ComboBox * m_filter2ComboBox = new ComboBox( this ); - m_filter2ComboBox->setGeometry( 217, 70, 137, 22 ); + m_filter2ComboBox->setGeometry( 217, 70, 137, ComboBox::DEFAULT_HEIGHT ); m_filter2ComboBox->setFont( pointSize<8>( m_filter2ComboBox->font() ) ); m_filter2ComboBox->setModel( &controls->m_filter2Model ); } diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index f1aad2a01b5..ddd1489e85d 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -151,8 +151,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *freqRangeCombo = new ComboBox(this, tr("Frequency range")); freqRangeCombo->setToolTip(tr("Frequency range")); - freqRangeCombo->setMinimumSize(100, 22); - freqRangeCombo->setMaximumSize(200, 22); + freqRangeCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + freqRangeCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); freqRangeCombo->setModel(&controls->m_freqRangeModel); config_layout->addWidget(freqRangeCombo, 0, 3, 2, 1); @@ -171,8 +171,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *ampRangeCombo = new ComboBox(this, tr("Amplitude range")); ampRangeCombo->setToolTip(tr("Amplitude range")); - ampRangeCombo->setMinimumSize(100, 22); - ampRangeCombo->setMaximumSize(200, 22); + ampRangeCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + ampRangeCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); ampRangeCombo->setModel(&controls->m_ampRangeModel); config_layout->addWidget(ampRangeCombo, 2, 3, 2, 1); @@ -201,8 +201,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) ComboBox *windowCombo = new ComboBox(this, tr("FFT window type")); windowCombo->setToolTip(tr("FFT window type")); - windowCombo->setMinimumSize(100, 22); - windowCombo->setMaximumSize(200, 22); + windowCombo->setMinimumSize(100, ComboBox::DEFAULT_HEIGHT); + windowCombo->setMaximumSize(200, ComboBox::DEFAULT_HEIGHT); windowCombo->setModel(&controls->m_windowModel); config_layout->addWidget(windowCombo, 2, 5, 2, 1); processor->rebuildWindow(); diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 51af71f55eb..95c4e60a875 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -541,7 +541,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, // interpolation selector m_interpBox = new ComboBox( this ); - m_interpBox->setGeometry( 142, 62, 82, 22 ); + m_interpBox->setGeometry( 142, 62, 82, ComboBox::DEFAULT_HEIGHT ); m_interpBox->setFont( pointSize<8>( m_interpBox->font() ) ); // wavegraph diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index efb351fc839..d4fb372a4a5 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1664,7 +1664,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc2VolKnob -> setVolumeKnob( true ); m_osc2WaveBox = new ComboBox( view ); - m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, 22 ); + m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc2WaveBox->setFont( pointSize<8>( m_osc2WaveBox->font() ) ); maketinyled( m_osc2SyncHButton, 212, O2ROW - 3, tr( "Hard sync oscillator 2" ) ) @@ -1679,18 +1679,18 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc3VolKnob -> setVolumeKnob( true ); m_osc3Wave1Box = new ComboBox( view ); - m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); + m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); m_osc3Wave2Box = new ComboBox( view ); - m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, 22 ); + m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_osc3Wave2Box->setFont( pointSize<8>( m_osc3Wave2Box->font() ) ); maketinyled( m_osc3SyncHButton, 212, O3ROW - 3, tr( "Hard sync oscillator 3" ) ) maketinyled( m_osc3SyncRButton, 191, O3ROW - 3, tr( "Reverse sync oscillator 3" ) ) m_lfo1WaveBox = new ComboBox( view ); - m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, 22 ); + m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_lfo1WaveBox->setFont( pointSize<8>( m_lfo1WaveBox->font() ) ); maketsknob( m_lfo1AttKnob, LFOCOL1, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) @@ -1698,7 +1698,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) makeknob( m_lfo1PhsKnob, LFOCOL3, LFOROW, tr( "Phase" ), tr( " deg" ), "lfoKnob" ) m_lfo2WaveBox = new ComboBox( view ); - m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, 22 ); + m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); m_lfo2WaveBox->setFont( pointSize<8>( m_lfo2WaveBox->font() ) ); maketsknob( m_lfo2AttKnob, LFOCOL4, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index f9e2e7ede56..96bc5f63e9b 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -403,7 +403,7 @@ malletsInstrumentView::malletsInstrumentView( malletsInstrument * _instrument, changePreset(); // Show widget m_presetsCombo = new ComboBox( this, tr( "Instrument" ) ); - m_presetsCombo->setGeometry( 140, 50, 99, 22 ); + m_presetsCombo->setGeometry( 140, 50, 99, ComboBox::DEFAULT_HEIGHT ); m_presetsCombo->setFont( pointSize<8>( m_presetsCombo->font() ) ); connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ), diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index ad67277ab35..865eeca8d5a 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -410,14 +410,14 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioPortAudio::name(), _parent ) { m_backend = new ComboBox( this, "BACKEND" ); - m_backend->setGeometry( 64, 15, 260, 20 ); + m_backend->setGeometry( 64, 15, 260, ComboBox::DEFAULT_HEIGHT ); QLabel * backend_lbl = new QLabel( tr( "Backend" ), this ); backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); backend_lbl->move( 8, 18 ); m_device = new ComboBox( this, "DEVICE" ); - m_device->setGeometry( 64, 35, 260, 20 ); + m_device->setGeometry( 64, 35, 260, ComboBox::DEFAULT_HEIGHT ); QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); diff --git a/src/gui/ControllerConnectionDialog.cpp b/src/gui/ControllerConnectionDialog.cpp index f0d3d10e9c2..6b6527b3342 100644 --- a/src/gui/ControllerConnectionDialog.cpp +++ b/src/gui/ControllerConnectionDialog.cpp @@ -187,7 +187,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, this, SLOT( userToggled() ) ); m_userController = new ComboBox( m_userGroupBox, "Controller" ); - m_userController->setGeometry( 10, 24, 200, 22 ); + m_userController->setGeometry( 10, 24, 200, ComboBox::DEFAULT_HEIGHT ); for (Controller * c : Engine::getSong()->controllers()) { m_userController->model()->addItem( c->name() ); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 221b01aca85..af9ea3b08a4 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -2349,7 +2349,7 @@ AutomationEditorWindow::AutomationEditorWindow() : zoom_x_label->setPixmap( embed::getIconPixmap( "zoom_x" ) ); m_zoomingXComboBox = new ComboBox( zoomToolBar ); - m_zoomingXComboBox->setFixedSize( 80, 22 ); + m_zoomingXComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingXComboBox->setToolTip( tr( "Horizontal zooming" ) ); for( float const & zoomLevel : m_editor->m_zoomXLevels ) @@ -2368,7 +2368,7 @@ AutomationEditorWindow::AutomationEditorWindow() : zoom_y_label->setPixmap( embed::getIconPixmap( "zoom_y" ) ); m_zoomingYComboBox = new ComboBox( zoomToolBar ); - m_zoomingYComboBox->setFixedSize( 80, 22 ); + m_zoomingYComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingYComboBox->setToolTip( tr( "Vertical zooming" ) ); m_editor->m_zoomingYModel.addItem( "Auto" ); @@ -2398,7 +2398,7 @@ AutomationEditorWindow::AutomationEditorWindow() : quantize_lbl->setPixmap( embed::getIconPixmap( "quantize" ) ); m_quantizeComboBox = new ComboBox( m_toolBar ); - m_quantizeComboBox->setFixedSize( 60, 22 ); + m_quantizeComboBox->setFixedSize( 60, ComboBox::DEFAULT_HEIGHT ); m_quantizeComboBox->setToolTip( tr( "Quantization" ) ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index bfc16df5be5..ffafc4577d8 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -75,7 +75,7 @@ BBEditor::BBEditor( BBTrackContainer* tc ) : DropToolBar *beatSelectionToolBar = addDropToolBarToTop(tr("Beat selector")); m_bbComboBox = new ComboBox( m_toolBar ); - m_bbComboBox->setFixedSize( 200, 22 ); + m_bbComboBox->setFixedSize( 200, ComboBox::DEFAULT_HEIGHT ); m_bbComboBox->setModel( &tc->m_bbComboBoxModel ); beatSelectionToolBar->addWidget( m_bbComboBox ); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 3d35c9c5133..0dc659dceb8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -4344,7 +4344,7 @@ PianoRollWindow::PianoRollWindow() : m_zoomingComboBox = new ComboBox( m_toolBar ); m_zoomingComboBox->setModel( &m_editor->m_zoomingModel ); - m_zoomingComboBox->setFixedSize( 64, 22 ); + m_zoomingComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); m_zoomingComboBox->setToolTip( tr( "Horizontal zooming") ); QLabel * zoom_y_lbl = new QLabel(m_toolBar); @@ -4352,7 +4352,7 @@ PianoRollWindow::PianoRollWindow() : m_zoomingYComboBox = new ComboBox(m_toolBar); m_zoomingYComboBox->setModel(&m_editor->m_zoomingYModel); - m_zoomingYComboBox->setFixedSize(64, 22); + m_zoomingYComboBox->setFixedSize(64, ComboBox::DEFAULT_HEIGHT); m_zoomingYComboBox->setToolTip(tr("Vertical zooming")); // setup quantize-stuff @@ -4361,7 +4361,7 @@ PianoRollWindow::PianoRollWindow() : m_quantizeComboBox = new ComboBox( m_toolBar ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); - m_quantizeComboBox->setFixedSize( 64, 22 ); + m_quantizeComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); m_quantizeComboBox->setToolTip( tr( "Quantization") ); // setup note-len-stuff @@ -4370,7 +4370,7 @@ PianoRollWindow::PianoRollWindow() : m_noteLenComboBox = new ComboBox( m_toolBar ); m_noteLenComboBox->setModel( &m_editor->m_noteLenModel ); - m_noteLenComboBox->setFixedSize( 105, 22 ); + m_noteLenComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_noteLenComboBox->setToolTip( tr( "Note length") ); // setup scale-stuff @@ -4379,7 +4379,7 @@ PianoRollWindow::PianoRollWindow() : m_scaleComboBox = new ComboBox( m_toolBar ); m_scaleComboBox->setModel( &m_editor->m_scaleModel ); - m_scaleComboBox->setFixedSize( 105, 22 ); + m_scaleComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_scaleComboBox->setToolTip( tr( "Scale") ); // setup chord-stuff @@ -4388,7 +4388,7 @@ PianoRollWindow::PianoRollWindow() : m_chordComboBox = new ComboBox( m_toolBar ); m_chordComboBox->setModel( &m_editor->m_chordModel ); - m_chordComboBox->setFixedSize( 105, 22 ); + m_chordComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_chordComboBox->setToolTip( tr( "Chord" ) ); // -- Clear ghost pattern button diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index d60b986d898..e738708ac09 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -955,7 +955,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : //Set up zooming-stuff m_zoomingComboBox = new ComboBox( m_toolBar ); - m_zoomingComboBox->setFixedSize( 80, 22 ); + m_zoomingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_zoomingComboBox->move( 580, 4 ); m_zoomingComboBox->setModel(m_editor->m_zoomingModel); m_zoomingComboBox->setToolTip(tr("Horizontal zooming")); @@ -970,7 +970,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : //Set up quantization/snapping selector m_snappingComboBox = new ComboBox( m_toolBar ); - m_snappingComboBox->setFixedSize( 80, 22 ); + m_snappingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); m_snappingComboBox->setModel(m_editor->m_snappingModel); m_snappingComboBox->setToolTip(tr("Clip snapping size")); connect(m_editor->snappingModel(), SIGNAL(dataChanged()), this, SLOT(updateSnapLabel())); diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index c796bfa74c8..69955501d61 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -51,6 +51,8 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : m_menu( this ), m_pressed( false ) { + setFixedHeight( ComboBox::DEFAULT_HEIGHT ); + if( s_background == NULL ) { s_background = new QPixmap( embed::getIconPixmap( "combobox_bg" ) ); diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index 15b4e0d282a..98aaf0e0eee 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -78,7 +78,7 @@ ComboControl::ComboControl(QWidget *parent) : m_combo(new ComboBox(nullptr)), m_label(new QLabel(m_widget)) { - m_combo->setFixedSize(64, 22); + m_combo->setFixedSize(64, ComboBox::DEFAULT_HEIGHT); QVBoxLayout* vbox = new QVBoxLayout(m_widget); vbox->addWidget(m_combo); vbox->addWidget(m_label); diff --git a/src/gui/widgets/InstrumentSoundShapingView.cpp b/src/gui/widgets/InstrumentSoundShapingView.cpp index a6c6fbd56cf..0eabab54536 100644 --- a/src/gui/widgets/InstrumentSoundShapingView.cpp +++ b/src/gui/widgets/InstrumentSoundShapingView.cpp @@ -74,7 +74,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : m_filterComboBox = new ComboBox( m_filterGroupBox ); - m_filterComboBox->setGeometry( 14, 22, 120, 22 ); + m_filterComboBox->setGeometry( 14, 22, 120, ComboBox::DEFAULT_HEIGHT ); m_filterComboBox->setFont( pointSize<8>( m_filterComboBox->font() ) ); From 5efb3a19cb13e4f3ad23c5d75dcd07a140a7ee86 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 30 Aug 2020 19:27:17 +0100 Subject: [PATCH 071/180] Fix use of translation functions (#5629) --- include/AudioAlsa.h | 2 +- include/AudioDummy.h | 2 +- include/AudioJack.h | 2 +- include/AudioOss.h | 2 +- include/AudioPortAudio.h | 2 +- include/AudioPulseAudio.h | 2 +- include/AudioSdl.h | 2 +- include/AudioSndio.h | 2 +- include/AudioSoundIo.h | 2 +- include/InstrumentSoundShaping.h | 2 +- plugins/Amplifier/Amplifier.cpp | 2 +- plugins/BassBooster/BassBooster.cpp | 2 +- plugins/Bitcrush/Bitcrush.cpp | 2 +- plugins/CrossoverEQ/CrossoverEQ.cpp | 2 +- plugins/Delay/DelayEffect.cpp | 2 +- plugins/DualFilter/DualFilter.cpp | 2 +- plugins/Eq/EqEffect.cpp | 2 +- plugins/Flanger/FlangerEffect.cpp | 2 +- plugins/FreeBoy/FreeBoy.cpp | 2 +- plugins/GigPlayer/GigPlayer.cpp | 2 +- plugins/HydrogenImport/HydrogenImport.cpp | 2 +- plugins/LadspaEffect/LadspaEffect.cpp | 2 +- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 2 +- plugins/MidiExport/MidiExport.cpp | 2 +- plugins/MidiImport/MidiImport.cpp | 2 +- plugins/MultitapEcho/MultitapEcho.cpp | 2 +- plugins/OpulenZ/OpulenZ.cpp | 2 +- plugins/ReverbSC/ReverbSC.cpp | 2 +- plugins/SpectrumAnalyzer/Analyzer.cpp | 2 +- plugins/Vectorscope/Vectorscope.cpp | 2 +- plugins/VstEffect/VstEffect.cpp | 2 +- plugins/VstEffect/VstEffectControls.cpp | 2 +- plugins/Xpressive/Xpressive.cpp | 2 +- .../audio_file_processor.cpp | 2 +- plugins/bit_invader/bit_invader.cpp | 2 +- plugins/carlapatchbay/carlapatchbay.cpp | 2 +- plugins/carlarack/carlarack.cpp | 2 +- .../dynamics_processor/dynamics_processor.cpp | 2 +- plugins/kicker/kicker.cpp | 2 +- plugins/ladspa_browser/ladspa_browser.cpp | 2 +- plugins/lb302/lb302.cpp | 2 +- plugins/monstro/Monstro.cpp | 2 +- plugins/nes/Nes.cpp | 2 +- plugins/organic/organic.cpp | 2 +- plugins/patman/patman.cpp | 2 +- .../peak_controller_effect.cpp | 2 +- plugins/sf2_player/sf2_player.cpp | 2 +- plugins/sfxr/sfxr.cpp | 2 +- plugins/sid/sid_instrument.cpp | 2 +- plugins/stereo_enhancer/stereo_enhancer.cpp | 2 +- plugins/stereo_matrix/stereo_matrix.cpp | 2 +- plugins/stk/mallets/mallets.cpp | 2 +- .../triple_oscillator/TripleOscillator.cpp | 2 +- plugins/vestige/vestige.cpp | 4 +- plugins/vibed/vibed.cpp | 2 +- plugins/vst_base/VstPlugin.cpp | 2 +- plugins/watsyn/Watsyn.cpp | 2 +- plugins/waveshaper/waveshaper.cpp | 2 +- plugins/zynaddsubfx/ZynAddSubFx.cpp | 2 +- src/core/InstrumentSoundShaping.cpp | 18 +++--- src/core/Plugin.cpp | 2 +- src/core/Track.cpp | 24 ++++---- src/gui/AudioDeviceSetupWidget.cpp | 2 +- src/gui/EffectSelectDialog.cpp | 2 +- src/gui/MidiSetupWidget.cpp | 2 +- src/gui/PluginBrowser.cpp | 4 +- src/gui/SetupDialog.cpp | 60 ++++++++++--------- .../widgets/InstrumentSoundShapingView.cpp | 2 +- src/tracks/InstrumentTrack.cpp | 5 -- src/tracks/SampleTrack.cpp | 24 ++++---- 71 files changed, 132 insertions(+), 135 deletions(-) diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index b1aa9647a61..4bfd0217e31 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -71,7 +71,7 @@ class AudioAlsa : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "ALSA (Advanced Linux Sound Architecture)" ); } diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 0772c69eb36..cf77491bc3f 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -48,7 +48,7 @@ class AudioDummy : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "Dummy (no sound output)" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "Dummy (no sound output)" ); } diff --git a/include/AudioJack.h b/include/AudioJack.h index a80e8855201..844aa886d87 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -63,7 +63,7 @@ class AudioJack : public QObject, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "JACK (JACK Audio Connection Kit)" ); } diff --git a/include/AudioOss.h b/include/AudioOss.h index 9e4787ff202..c631bcedddc 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -48,7 +48,7 @@ class AudioOss : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "OSS (Open Sound System)" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "OSS (Open Sound System)" ); } static QString probeDevice(); diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index e1288c3a45d..5279492cee7 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -72,7 +72,7 @@ class AudioPortAudio : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "PortAudio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" ); } diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index e65180a74a3..b92a386b99f 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -50,7 +50,7 @@ class AudioPulseAudio : public QThread, public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "PulseAudio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PulseAudio" ); } static QString probeDevice(); diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 93f23abed22..1bda446b290 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -51,7 +51,7 @@ class AudioSdl : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "SDL (Simple DirectMedia Layer)" ); } diff --git a/include/AudioSndio.h b/include/AudioSndio.h index f8cf56848a6..0cc88facfa4 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -49,7 +49,7 @@ class AudioSndio : public QThread, public AudioDevice inline static QString name( void ) { - return QT_TRANSLATE_NOOP( "setupWidget", "sndio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "sndio" ); } class setupWidget : public AudioDeviceSetupWidget diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h index f743ad67629..79b586f0645 100644 --- a/include/AudioSoundIo.h +++ b/include/AudioSoundIo.h @@ -61,7 +61,7 @@ class AudioSoundIo : public AudioDevice inline static QString name() { - return QT_TRANSLATE_NOOP( "setupWidget", "soundio" ); + return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "soundio" ); } class setupWidget : public AudioDeviceSetupWidget diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index 1b8df38d3f1..b037f615a77 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -74,7 +74,7 @@ class InstrumentSoundShaping : public Model, public JournallingObject FloatModel m_filterCutModel; FloatModel m_filterResModel; - static const QString targetNames[InstrumentSoundShaping::NumTargets][3]; + static const char *const targetNames[InstrumentSoundShaping::NumTargets][3]; friend class InstrumentSoundShapingView; diff --git a/plugins/Amplifier/Amplifier.cpp b/plugins/Amplifier/Amplifier.cpp index cc2a63304ff..e7250872f53 100644 --- a/plugins/Amplifier/Amplifier.cpp +++ b/plugins/Amplifier/Amplifier.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT amplifier_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Amplifier", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native amplifier plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native amplifier plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/BassBooster/BassBooster.cpp b/plugins/BassBooster/BassBooster.cpp index 535834aae77..1ed1f246712 100644 --- a/plugins/BassBooster/BassBooster.cpp +++ b/plugins/BassBooster/BassBooster.cpp @@ -34,7 +34,7 @@ Plugin::Descriptor PLUGIN_EXPORT bassbooster_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "BassBooster", - QT_TRANSLATE_NOOP( "pluginBrowser", "Boost your bass the fast and simple way" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Boost your bass the fast and simple way" ), "Tobias Doerffel ", 0x0100, Plugin::Effect, diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index 0580d47cd5e..fa19b195fc4 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Bitcrush", - QT_TRANSLATE_NOOP( "pluginBrowser", "An oversampling bitcrusher" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "An oversampling bitcrusher" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index e2090980a06..78029134ebc 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -36,7 +36,7 @@ Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Crossover Equalizer", - QT_TRANSLATE_NOOP( "pluginBrowser", "A 4-band Crossover Equalizer" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A 4-band Crossover Equalizer" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/Delay/DelayEffect.cpp b/plugins/Delay/DelayEffect.cpp index 0daad0d9d8c..69ee924e8b0 100644 --- a/plugins/Delay/DelayEffect.cpp +++ b/plugins/Delay/DelayEffect.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT delay_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Delay", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native delay plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native delay plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index b4e11ccb396..d4e5d4b5ef0 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -36,7 +36,7 @@ Plugin::Descriptor PLUGIN_EXPORT dualfilter_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Dual Filter", - QT_TRANSLATE_NOOP( "pluginBrowser", "A Dual filter plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A Dual filter plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 6b7c61bcc49..a295374931c 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -39,7 +39,7 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Equalizer", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native eq plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native eq plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp index 7e441cf62a3..65b58ce49ed 100644 --- a/plugins/Flanger/FlangerEffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Flanger", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A native flanger plugin" ), "Dave French ", 0x0100, Plugin::Effect, diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index 2f787044f25..95ba5a215b5 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT freeboy_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "FreeBoy", - QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of GameBoy (TM) APU" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Emulation of GameBoy (TM) APU" ), "Attila Herman " "Csaba Hruska ", diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 334e2bd7737..1e077f5d4e3 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -63,7 +63,7 @@ Plugin::Descriptor PLUGIN_EXPORT gigplayer_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "GIG Player", - QT_TRANSLATE_NOOP( "pluginBrowser", "Player for GIG files" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Player for GIG files" ), "Garrett Wilson ", 0x0100, Plugin::Instrument, diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp index 4ed0a89a3f1..4a69bc4511c 100644 --- a/plugins/HydrogenImport/HydrogenImport.cpp +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -29,7 +29,7 @@ Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Hydrogen Import", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for importing Hydrogen files into LMMS" ), "frank mather", 0x0100, diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index d7e66bf47aa..117e1e0e10e 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspaeffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LADSPA", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for using arbitrary LADSPA-effects " "inside LMMS." ), "Danny McRae ", diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 2ee5ceabbe5..36e9df46ba7 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -39,7 +39,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("pluginBrowser", + QT_TRANSLATE_NOOP("PluginBrowser", "plugin for using arbitrary LV2-effects inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 974aaf416b4..0f7534ac8c9 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("pluginBrowser", + QT_TRANSLATE_NOOP("PluginBrowser", "plugin for using arbitrary LV2 instruments inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index 1860527c10e..466664b167d 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -47,7 +47,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiexport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "MIDI Export", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for exporting MIDI-files from LMMS" ), "Mohamed Abdel Maksoud and " "Hyunjin Song ", diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 3646975a29a..f183fb418f2 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -64,7 +64,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "MIDI Import", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Filter for importing MIDI-files into LMMS" ), "Tobias Doerffel ", 0x0100, diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index bff032deed9..eb6c51cba82 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -34,7 +34,7 @@ Plugin::Descriptor PLUGIN_EXPORT multitapecho_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Multitap Echo", - QT_TRANSLATE_NOOP( "pluginBrowser", "A multitap echo delay plugin" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "A multitap echo delay plugin" ), "Vesa Kivimäki ", 0x0100, Plugin::Effect, diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 4dde05b0f3b..8ab635b4f92 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -68,7 +68,7 @@ Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "OpulenZ", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "2-operator FM Synth" ), "Raine M. Ekman ", 0x0100, diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp index 3d56fd0d0ae..f54002199d5 100644 --- a/plugins/ReverbSC/ReverbSC.cpp +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT reverbsc_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "ReverbSC", - QT_TRANSLATE_NOOP( "pluginBrowser", "Reverb algorithm by Sean Costello" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Reverb algorithm by Sean Costello" ), "Paul Batchelor", 0x0123, Plugin::Effect, diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index 656d18bd4d6..87a8542e913 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -42,7 +42,7 @@ extern "C" { { "spectrumanalyzer", "Spectrum Analyzer", - QT_TRANSLATE_NOOP("pluginBrowser", "A graphical spectrum analyzer."), + QT_TRANSLATE_NOOP("PluginBrowser", "A graphical spectrum analyzer."), "Martin Pavelek ", 0x0112, Plugin::Effect, diff --git a/plugins/Vectorscope/Vectorscope.cpp b/plugins/Vectorscope/Vectorscope.cpp index f8bc30c40df..30d33aa2d8f 100644 --- a/plugins/Vectorscope/Vectorscope.cpp +++ b/plugins/Vectorscope/Vectorscope.cpp @@ -33,7 +33,7 @@ extern "C" { { STRINGIFY(PLUGIN_NAME), "Vectorscope", - QT_TRANSLATE_NOOP("pluginBrowser", "A stereo field visualizer."), + QT_TRANSLATE_NOOP("PluginBrowser", "A stereo field visualizer."), "Martin Pavelek ", 0x0100, Plugin::Effect, diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 80f209a7b1c..fb805aed3c3 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT vsteffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "VST", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for using arbitrary VST effects inside LMMS." ), "Tobias Doerffel ", 0x0200, diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index ab740838855..e9133ff325e 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -374,7 +374,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * { sprintf( paramStr, "%d", i); m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)), - 0.0f, 1.0f, 0.01f, _eff, tr( paramStr ) ); + 0.0f, 1.0f, 0.01f, _eff, paramStr ); } FloatModel * model = m_vi->knobFModel[i]; diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 6e648edd481..e4eff06bdc8 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -53,7 +53,7 @@ extern "C" { Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY( - PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("pluginBrowser", + PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("PluginBrowser", "Mathematical expression parser"), "Orr Dvori", 0x0100, Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL }; diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 95c4e60a875..dbde6e8c45c 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -55,7 +55,7 @@ Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "AudioFileProcessor", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Simple sampler with various settings for " "using samples (e.g. drums) in an " "instrument-track" ), diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index caa272fa7f0..3bc5785ef64 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitinvader_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "BitInvader", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Customizable wavetable synthesizer" ), "Andreas Brandmaier ", 0x0100, diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index ad0c1f6aef5..0090b3fcccf 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Carla Patchbay", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Carla Patchbay Instrument" ), "falkTX ", CARLA_VERSION_HEX, diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index ee2a788354f..3b3f443abb4 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Carla Rack", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Carla Rack Instrument" ), "falkTX ", CARLA_VERSION_HEX, diff --git a/plugins/dynamics_processor/dynamics_processor.cpp b/plugins/dynamics_processor/dynamics_processor.cpp index 9bf7d9b3c90..38c74cf19de 100644 --- a/plugins/dynamics_processor/dynamics_processor.cpp +++ b/plugins/dynamics_processor/dynamics_processor.cpp @@ -38,7 +38,7 @@ Plugin::Descriptor PLUGIN_EXPORT dynamicsprocessor_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Dynamics Processor", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for processing dynamics in a flexible way" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 5f36aae93f1..fe770c1ac1f 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Kicker", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Versatile drum synthesizer" ), "Tobias Doerffel ", 0x0100, diff --git a/plugins/ladspa_browser/ladspa_browser.cpp b/plugins/ladspa_browser/ladspa_browser.cpp index d265bc0e3e2..b363336346f 100644 --- a/plugins/ladspa_browser/ladspa_browser.cpp +++ b/plugins/ladspa_browser/ladspa_browser.cpp @@ -49,7 +49,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspabrowser_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LADSPA Plugin Browser", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "List installed LADSPA plugins" ), "Danny McRae ", 0x0100, diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index ad3d33b0a04..72bd5a4946e 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -83,7 +83,7 @@ Plugin::Descriptor PLUGIN_EXPORT lb302_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "LB302", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Incomplete monophonic imitation tb303" ), "Paul Giblock ", 0x0100, diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index d4fb372a4a5..8e0342377c3 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT monstro_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Monstro", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Monstrous 3-oscillator synth with modulation matrix" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index d4bcc6881d1..d7c97fd160e 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT nes_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Nescaline", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "A NES-like synthesizer" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index 6eb933afad3..c78d5c056b1 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT organic_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Organic", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Additive Synthesizer for organ-like sounds" ), "Andreas Brandmaier ", 0x0100, diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index e5170383438..d65850edcd0 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -54,7 +54,7 @@ Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "PatMan", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "GUS-compatible patch instrument" ), "Javier Serrano Polo ", 0x0100, diff --git a/plugins/peak_controller_effect/peak_controller_effect.cpp b/plugins/peak_controller_effect/peak_controller_effect.cpp index 9d1e6ccf465..01d24600e4e 100644 --- a/plugins/peak_controller_effect/peak_controller_effect.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT peakcontrollereffect_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Peak Controller", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for controlling knobs with sound peaks" ), "Paul Giblock ", 0x0100, diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 7065a08002c..7a07b0ec697 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -57,7 +57,7 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Sf2 Player", - QT_TRANSLATE_NOOP( "pluginBrowser", "Player for SoundFont files" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "Player for SoundFont files" ), "Paul Giblock ", 0x0100, Plugin::Instrument, diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 1aacdc12c3b..08d418836c2 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -62,7 +62,7 @@ Plugin::Descriptor PLUGIN_EXPORT sfxr_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "sfxr", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "LMMS port of sfxr" ), "Wong Cho Ching", 0x0100, diff --git a/plugins/sid/sid_instrument.cpp b/plugins/sid/sid_instrument.cpp index 27f874e12dc..485fdfffb58 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/sid/sid_instrument.cpp @@ -75,7 +75,7 @@ Plugin::Descriptor PLUGIN_EXPORT sid_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "SID", - QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of the MOS6581 and MOS8580 " + QT_TRANSLATE_NOOP( "PluginBrowser", "Emulation of the MOS6581 and MOS8580 " "SID.\nThis chip was used in the Commodore 64 computer." ), "Csaba Hruska " diff --git a/plugins/stereo_enhancer/stereo_enhancer.cpp b/plugins/stereo_enhancer/stereo_enhancer.cpp index 2faa5846d2f..3f5a9a38c29 100644 --- a/plugins/stereo_enhancer/stereo_enhancer.cpp +++ b/plugins/stereo_enhancer/stereo_enhancer.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereoenhancer_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "StereoEnhancer Effect", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for enhancing stereo separation of a stereo input file" ), "Lou Herard ", 0x0100, diff --git a/plugins/stereo_matrix/stereo_matrix.cpp b/plugins/stereo_matrix/stereo_matrix.cpp index a03a615ba37..2ec9b49509e 100644 --- a/plugins/stereo_matrix/stereo_matrix.cpp +++ b/plugins/stereo_matrix/stereo_matrix.cpp @@ -35,7 +35,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereomatrix_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Stereo Matrix", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Plugin for freely manipulating stereo output" ), "Paul Giblock ", 0x0100, diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index 96bc5f63e9b..6f968985ced 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -50,7 +50,7 @@ Plugin::Descriptor PLUGIN_EXPORT malletsstk_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Mallets", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Tuneful things to bang on" ), "Danny McRae ", 0x0100, diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 8093d218c04..9fdae48337f 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -49,7 +49,7 @@ Plugin::Descriptor PLUGIN_EXPORT tripleoscillator_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "TripleOscillator", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Three powerful oscillators you can modulate " "in several ways" ), "Tobias Doerffel ", diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index b17a1684541..dc1723db472 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -71,7 +71,7 @@ Plugin::Descriptor Q_DECL_EXPORT vestige_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "VeSTige", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "VST-host for using VST(i)-plugins within LMMS" ), "Tobias Doerffel ", 0x0100, @@ -991,7 +991,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume { sprintf( paramStr, "%d", i); m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)), - 0.0f, 1.0f, 0.01f, castModel(), tr( paramStr ) ); + 0.0f, 1.0f, 0.01f, castModel(), paramStr ); } FloatModel * model = m_vi->knobFModel[i]; diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp index c663660826e..6610bab0e43 100644 --- a/plugins/vibed/vibed.cpp +++ b/plugins/vibed/vibed.cpp @@ -48,7 +48,7 @@ Plugin::Descriptor PLUGIN_EXPORT vibedstrings_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Vibed", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Vibrating string modeler" ), "Danny McRae ", 0x0100, diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 7d6a45940d2..278ab8b2d71 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -560,7 +560,7 @@ void VstPlugin::loadParameterDisplays() void VstPlugin::savePreset( ) { QString presName = currentProgramName().isEmpty() ? tr(": default") : currentProgramName(); - presName.replace(tr("\""), tr("'")); // QFileDialog unable to handle double quotes properly + presName.replace("\"", "'"); // QFileDialog unable to handle double quotes properly FileDialog sfd( NULL, tr( "Save Preset" ), presName.section(": ", 1, 1) + tr(".fxp"), tr( "Vst Plugin Preset (*.fxp *.fxb)" ) ); diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index a5af401ccf7..b60dd5a99c2 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT watsyn_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Watsyn", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "4-oscillator modulatable wavetable synth" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/waveshaper/waveshaper.cpp b/plugins/waveshaper/waveshaper.cpp index a3bf2ddfb12..5327d931d9c 100644 --- a/plugins/waveshaper/waveshaper.cpp +++ b/plugins/waveshaper/waveshaper.cpp @@ -38,7 +38,7 @@ Plugin::Descriptor PLUGIN_EXPORT waveshaper_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "Waveshaper Effect", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "plugin for waveshaping" ), "Vesa Kivimäki ", 0x0100, diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 429948e7508..04f7bda0ff3 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -58,7 +58,7 @@ Plugin::Descriptor PLUGIN_EXPORT zynaddsubfx_plugin_descriptor = { STRINGIFY( PLUGIN_NAME ), "ZynAddSubFX", - QT_TRANSLATE_NOOP( "pluginBrowser", + QT_TRANSLATE_NOOP( "PluginBrowser", "Embedded ZynAddSubFX" ), "Tobias Doerffel ", 0x0100, diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 46026b96f8c..2c221cdcc01 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -43,16 +43,14 @@ const float RES_PRECISION = 1000.0f; // names for env- and lfo-targets - first is name being displayed to user // and second one is used internally, e.g. for saving/restoring settings -const QString InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] = +const char *const InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] = { - { InstrumentSoundShaping::tr( "VOLUME" ), "vol", - InstrumentSoundShaping::tr( "Volume" ) }, -/* InstrumentSoundShaping::tr( "Pan" ), - InstrumentSoundShaping::tr( "Pitch" ),*/ - { InstrumentSoundShaping::tr( "CUTOFF" ), "cut", - InstrumentSoundShaping::tr( "Cutoff frequency" ) }, - { InstrumentSoundShaping::tr( "RESO" ), "res", - InstrumentSoundShaping::tr( "Resonance" ) } + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "VOLUME"), "vol", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Volume") }, + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "CUTOFF"), "cut", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Cutoff frequency") }, + { QT_TRANSLATE_NOOP("InstrumentSoundShaping", "RESO"), "res", + QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Resonance") } } ; @@ -77,7 +75,7 @@ InstrumentSoundShaping::InstrumentSoundShaping( value_for_zero_amount, this ); m_envLfoParameters[i]->setDisplayName( - tr( targetNames[i][2].toUtf8().constData() ) ); + tr( targetNames[i][2] ) ); } m_filterModel.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 411f6fe5899..f1256654970 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -44,7 +44,7 @@ static Plugin::Descriptor dummyPluginDescriptor = { "dummy", "dummy", - QT_TRANSLATE_NOOP( "pluginBrowser", "no description" ), + QT_TRANSLATE_NOOP( "PluginBrowser", "no description" ), "Tobias Doerffel ", 0x0100, Plugin::Undefined, diff --git a/src/core/Track.cpp b/src/core/Track.cpp index f79a34a15c9..e52270fdd83 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1138,26 +1138,26 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) { contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( individualTCO - ? "Delete (middle mousebutton)" - : "Delete selection (middle mousebutton)" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), [this](){ contextMenuAction( Remove ); } ); contextMenu.addSeparator(); contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( individualTCO - ? "Cut" - : "Cut selection" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), [this](){ contextMenuAction( Cut ); } ); } contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( individualTCO - ? "Copy" - : "Copy selection" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), [this](){ contextMenuAction( Copy ); } ); contextMenu.addAction( @@ -1169,9 +1169,9 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( individualTCO - ? "Mute/unmute (<%1> + middle click)" - : "Mute/unmute selection (<%1> + middle click)" ).arg(UI_CTRL_KEY), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), [this](){ contextMenuAction( Mute ); } ); constructContextMenu( &contextMenu ); diff --git a/src/gui/AudioDeviceSetupWidget.cpp b/src/gui/AudioDeviceSetupWidget.cpp index fbec38c7682..feba3f1b0fb 100644 --- a/src/gui/AudioDeviceSetupWidget.cpp +++ b/src/gui/AudioDeviceSetupWidget.cpp @@ -26,7 +26,7 @@ AudioDeviceSetupWidget::AudioDeviceSetupWidget(const QString & caption, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(TabWidget::tr(caption.toLatin1())), parent) + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) { } diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index 79e40427b28..4423ea707d7 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -234,7 +234,7 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx, { QLabel *label = new QLabel(m_descriptionWidget); QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; - labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; + labelText += "

" + tr("Description") + ": " + qApp->translate( "PluginBrowser", descriptor.description ) + "

"; labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; label->setText(labelText); diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index 0c34544d6bf..8342070571b 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -31,7 +31,7 @@ MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & configSection, const QString & devName, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toLatin1())), parent), + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent), m_configSection(configSection), m_device(nullptr) { diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 671b58381ea..7fd1a6301a0 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -205,7 +205,9 @@ PluginDescWidget::PluginDescWidget(const PluginKey &_pk, setFixedHeight( DEFAULT_HEIGHT ); setMouseTracking( true ); setCursor( Qt::PointingHandCursor ); - setToolTip(_pk.description()); + setToolTip(_pk.desc->subPluginFeatures + ? _pk.description() + : tr(_pk.desc->description)); } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index e542039c50e..f06eea9e05c 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -31,12 +31,14 @@ #include #include +#include "AudioDeviceSetupWidget.h" #include "debug.h" #include "embed.h" #include "Engine.h" #include "FileDialog.h" #include "gui_templates.h" #include "MainWindow.h" +#include "MidiSetupWidget.h" #include "Mixer.h" #include "ProjectJournal.h" #include "SetupDialog.h" @@ -195,14 +197,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : auto addLedCheckBox = [&XDelta, &YDelta, this]( - const char* ledText, + const QString &ledText, TabWidget* tw, int& counter, bool initialState, const char* toggledSlot, bool showRestartWarning ){ - LedCheckBox * checkBox = new LedCheckBox(tr(ledText), tw); + LedCheckBox * checkBox = new LedCheckBox(ledText, tw); counter++; checkBox->move(XDelta, YDelta * counter); checkBox->setChecked(initialState); @@ -221,21 +223,21 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Graphical user interface (GUI)"), general_w); - addLedCheckBox("Display volume as dBFS ", gui_tw, counter, + addLedCheckBox(tr("Display volume as dBFS "), gui_tw, counter, m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true); - addLedCheckBox("Enable tooltips", gui_tw, counter, + addLedCheckBox(tr("Enable tooltips"), gui_tw, counter, m_tooltips, SLOT(toggleTooltips(bool)), true); - addLedCheckBox("Enable master oscilloscope by default", gui_tw, counter, + addLedCheckBox(tr("Enable master oscilloscope by default"), gui_tw, counter, m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true); - addLedCheckBox("Enable all note labels in piano roll", gui_tw, counter, + addLedCheckBox(tr("Enable all note labels in piano roll"), gui_tw, counter, m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false); - addLedCheckBox("Enable compact track buttons", gui_tw, counter, + addLedCheckBox(tr("Enable compact track buttons"), gui_tw, counter, m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true); - addLedCheckBox("Enable one instrument-track-window mode", gui_tw, counter, + addLedCheckBox(tr("Enable one instrument-track-window mode"), gui_tw, counter, m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); - addLedCheckBox("Show sidebar on the right-hand side", gui_tw, counter, + addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter, m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true); - addLedCheckBox("Mute automation tracks during solo", gui_tw, counter, + addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter, m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false); gui_tw->setFixedHeight(YDelta + YDelta * counter); @@ -248,11 +250,11 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Projects"), general_w); - addLedCheckBox("Compress project files by default", projects_tw, counter, + addLedCheckBox(tr("Compress project files by default"), projects_tw, counter, m_MMPZ, SLOT(toggleMMPZ(bool)), true); - addLedCheckBox("Create a backup file when saving a project", projects_tw, counter, + addLedCheckBox(tr("Create a backup file when saving a project"), projects_tw, counter, m_disableBackup, SLOT(toggleDisableBackup(bool)), false); - addLedCheckBox("Reopen last project on startup", projects_tw, counter, + addLedCheckBox(tr("Reopen last project on startup"), projects_tw, counter, m_openLastProject, SLOT(toggleOpenLastProject(bool)), false); projects_tw->setFixedHeight(YDelta + YDelta * counter); @@ -372,9 +374,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : TabWidget * ui_fx_tw = new TabWidget( tr("User interface (UI) effects vs. performance"), performance_w); - addLedCheckBox("Smooth scroll in song editor", ui_fx_tw, counter, + addLedCheckBox(tr("Smooth scroll in song editor"), ui_fx_tw, counter, m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false); - addLedCheckBox("Display playback cursor in AudioFileProcessor", ui_fx_tw, counter, + addLedCheckBox(tr("Display playback cursor in AudioFileProcessor"), ui_fx_tw, counter, m_animateAFP, SLOT(toggleAnimateAFP(bool)), false); ui_fx_tw->setFixedHeight(YDelta + YDelta * counter); @@ -421,10 +423,10 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : connect(m_vstAlwaysOnTopCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleVSTAlwaysOnTop(bool))); - addLedCheckBox("Sync VST plugins to host playback", plugins_tw, counter, + addLedCheckBox(tr("Sync VST plugins to host playback"), plugins_tw, counter, m_syncVSTPlugins, SLOT(toggleSyncVSTPlugins(bool)), false); - addLedCheckBox("Keep effects running even without input", plugins_tw, counter, + addLedCheckBox(tr("Keep effects running even without input"), plugins_tw, counter, m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false); plugins_tw->setFixedHeight(YDelta + YDelta * counter); @@ -511,7 +513,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_audioIfaceSetupWidgets.end(); ++it) { m_audioIfaceNames[ - tr(it.key().toLatin1())] = it.key(); + AudioDeviceSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_audioIfaceNames.begin(); it != m_audioIfaceNames.end(); ++it) @@ -657,7 +659,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_midiIfaceSetupWidgets.end(); ++it) { m_midiIfaceNames[ - tr(it.key().toLatin1())] = it.key(); + MidiSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_midiIfaceNames.begin(); it != m_midiIfaceNames.end(); ++it) @@ -736,14 +738,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : QVBoxLayout * pathSelectorsLayout = new QVBoxLayout; pathSelectorsLayout->setSpacing(10); - auto addPathEntry = [&](const char* caption, + auto addPathEntry = [&](const QString &caption, const QString& content, const char* setSlot, const char* openSlot, QLineEdit*& lineEdit, const char* pixmap = "project_open") { - TabWidget * newTw = new TabWidget(tr(caption), + TabWidget * newTw = new TabWidget(caption, pathSelectors); newTw->setFixedHeight(48); @@ -763,37 +765,37 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : pathSelectorsLayout->addSpacing(10); }; - addPathEntry("LMMS working directory", m_workingDir, + addPathEntry(tr("LMMS working directory"), m_workingDir, SLOT(setWorkingDir(const QString &)), SLOT(openWorkingDir()), m_workingDirLineEdit); - addPathEntry("VST plugins directory", m_vstDir, + addPathEntry(tr("VST plugins directory"), m_vstDir, SLOT(setVSTDir(const QString &)), SLOT(openVSTDir()), m_vstDirLineEdit); - addPathEntry("LADSPA plugins directories", m_ladspaDir, + addPathEntry(tr("LADSPA plugins directories"), m_ladspaDir, SLOT(setLADSPADir(const QString &)), SLOT(openLADSPADir()), m_ladspaDirLineEdit, "add_folder"); - addPathEntry("SF2 directory", m_sf2Dir, + addPathEntry(tr("SF2 directory"), m_sf2Dir, SLOT(setSF2Dir(const QString &)), SLOT(openSF2Dir()), m_sf2DirLineEdit); #ifdef LMMS_HAVE_FLUIDSYNTH - addPathEntry("Default SF2", m_sf2File, + addPathEntry(tr("Default SF2"), m_sf2File, SLOT(setSF2File(const QString &)), SLOT(openSF2File()), m_sf2FileLineEdit); #endif - addPathEntry("GIG directory", m_gigDir, + addPathEntry(tr("GIG directory"), m_gigDir, SLOT(setGIGDir(const QString &)), SLOT(openGIGDir()), m_gigDirLineEdit); - addPathEntry("Theme directory", m_themeDir, + addPathEntry(tr("Theme directory"), m_themeDir, SLOT(setThemeDir(const QString &)), SLOT(openThemeDir()), m_themeDirLineEdit); - addPathEntry("Background artwork", m_backgroundPicFile, + addPathEntry(tr("Background artwork"), m_backgroundPicFile, SLOT(setBackgroundPicFile(const QString &)), SLOT(openBackgroundPicFile()), m_backgroundPicFileLineEdit); diff --git a/src/gui/widgets/InstrumentSoundShapingView.cpp b/src/gui/widgets/InstrumentSoundShapingView.cpp index 0eabab54536..24a52fad235 100644 --- a/src/gui/widgets/InstrumentSoundShapingView.cpp +++ b/src/gui/widgets/InstrumentSoundShapingView.cpp @@ -62,7 +62,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : { m_envLfoViews[i] = new EnvelopeAndLfoView( m_targetsTabWidget ); m_targetsTabWidget->addTab( m_envLfoViews[i], - tr( InstrumentSoundShaping::targetNames[i][0].toUtf8().constData() ), + tr( InstrumentSoundShaping::targetNames[i][0] ), NULL ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 222c604dfb9..8d0cb24316e 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -76,11 +76,6 @@ #include "TrackLabelButton.h" -const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack", - "With this knob you can set " - "the volume of the opened " - "channel."); - const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; const int PIANO_HEIGHT = 80; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 0183684a180..d73b62cbaff 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -358,26 +358,26 @@ void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) { contextMenu.addAction( embed::getIconPixmap( "cancel" ), - tr( individualTCO - ? "Delete (middle mousebutton)" - : "Delete selection (middle mousebutton)" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), [this](){ contextMenuAction( Remove ); } ); contextMenu.addSeparator(); contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), - tr( individualTCO - ? "Cut" - : "Cut selection" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), [this](){ contextMenuAction( Cut ); } ); } contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), - tr( individualTCO - ? "Copy" - : "Copy selection" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), [this](){ contextMenuAction( Copy ); } ); contextMenu.addAction( @@ -389,9 +389,9 @@ void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) contextMenu.addAction( embed::getIconPixmap( "muted" ), - tr( individualTCO - ? "Mute/unmute (<%1> + middle click)" - : "Mute/unmute selection (<%1> + middle click)" ).arg(UI_CTRL_KEY), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), [this](){ contextMenuAction( Mute ); } ); /*contextMenu.addAction( embed::getIconPixmap( "record" ), From 5f4d0cab9b44e4b8df9d67909ee4bed78b794c7d Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Thu, 10 Sep 2020 22:47:06 +0200 Subject: [PATCH 072/180] Fix for issue #3816 - FM or heavy PM in TripleOscillator makes outputs odd for some target waveforms. (#5651) The internal waveforms of the class Oscillator produces the wrong amplitude when the input is a negative phase. When doing PM or FM, negative phases may occur. When a negative phase is e.g. passed to the the saw sample, it produces values less than -1.0, hence going out of range. Converted all fraction calls to absFraction calls. Removed the +2 in the function Oscillator::recalcPhase. The comment here was that it was needed to avoid negative phases in case of PM. But by converting fraction to absFraction in the waveforms, negative phases are not an issue anymore. On top of that the m_phase variable gains about 2 extra bits in precision. As side effect of that, it improves the behaviour of the issue #2047 - TripleOscillator: Oscillators are getting out of sync. Though I did not investigate it thoroughly over different notes and samplerates. Add documentation to the fraction and absFraction functions in lmms_math.h as it was not immediately clear by the name what the functions do. Correct the implementation of the functions in case the flag __INTEL_COMPILER is set. (floorf rounds always down). --- include/Oscillator.h | 10 +++++----- include/lmms_math.h | 23 +++++++++++++++++++++-- src/core/Oscillator.cpp | 3 +-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/include/Oscillator.h b/include/Oscillator.h index 408e69dbc6c..912bcdc0977 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -97,7 +97,7 @@ class LMMS_EXPORT Oscillator static inline sample_t triangleSample( const float _sample ) { - const float ph = fraction( _sample ); + const float ph = absFraction( _sample ); if( ph <= 0.25f ) { return ph * 4.0f; @@ -111,17 +111,17 @@ class LMMS_EXPORT Oscillator static inline sample_t sawSample( const float _sample ) { - return -1.0f + fraction( _sample ) * 2.0f; + return -1.0f + absFraction( _sample ) * 2.0f; } static inline sample_t squareSample( const float _sample ) { - return ( fraction( _sample ) > 0.5f ) ? -1.0f : 1.0f; + return ( absFraction( _sample ) > 0.5f ) ? -1.0f : 1.0f; } static inline sample_t moogSawSample( const float _sample ) { - const float ph = fraction( _sample ); + const float ph = absFraction( _sample ); if( ph < 0.5f ) { return -1.0f + ph * 4.0f; @@ -131,7 +131,7 @@ class LMMS_EXPORT Oscillator static inline sample_t expSample( const float _sample ) { - float ph = fraction( _sample ); + float ph = absFraction( _sample ); if( ph > 0.5f ) { ph = 1.0f - ph; diff --git a/include/lmms_math.h b/include/lmms_math.h index 7c68c6bfbf5..3fd76896e18 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -42,22 +42,41 @@ using namespace std; static inline float absFraction( const float _x ) { - return( _x - ( _x >= 0.0f ? floorf( _x ) : floorf( _x ) - 1 ) ); + return( _x - floorf( _x ) ); } static inline float fraction( const float _x ) { - return( _x - floorf( _x ) ); + return( _x - floorf( _x ) - ( _x >= 0.0f ? 0.0 : 1.0 ) ); } #else +/*! + * @brief Returns the wrapped fractional part of a float, a value between 0.0f and 1.0f. + * + * absFraction( 2.3) => 0.3 + * absFraction(-2.3) => 0.7 + * + * Note that this not the same as the absolute value of the fraction (as the function name suggests). + * If the result is interpreted as a phase of an oscillator, it makes that negative phases are + * converted to positive phases. + */ static inline float absFraction( const float _x ) { return( _x - ( _x >= 0.0f ? static_cast( _x ) : static_cast( _x ) - 1 ) ); } +/*! + * @brief Returns the fractional part of a float, a value between -1.0f and 1.0f. + * + * fraction( 2.3) => 0.3 + * fraction(-2.3) => -0.3 + * + * Note that if the return value is used as a phase of an oscillator, that the oscillator must support + * negative phases. + */ static inline float fraction( const float _x ) { return( _x - static_cast( _x ) ); diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index b7dc5bff535..ecd1d6ea791 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -316,8 +316,7 @@ inline void Oscillator::recalcPhase() m_phaseOffset = m_ext_phaseOffset; m_phase += m_phaseOffset; } - m_phase = absFraction( m_phase )+2; // make sure we're not running - // negative when doing PM + m_phase = absFraction( m_phase ); } From 8a19c2d25e8b91640de87557ecd5793ba4aebefe Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 5 Sep 2020 22:33:51 +0200 Subject: [PATCH 073/180] Fix spelling in Lv2 classes --- include/LinkedModelGroupViews.h | 2 +- include/LinkedModelGroups.h | 2 +- include/Lv2ControlBase.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 390d2556890..46ba4c3ab55 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -98,7 +98,7 @@ class LinkedModelGroupsView void modelChanged(class LinkedModelGroups* ctrlBase); private: - //! The base class must return the adressed group view, + //! The base class must return the addressed group view, //! which has the same value as "this" virtual LinkedModelGroupView* getGroupView() = 0; }; diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 355290d9aee..686f09cba36 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -42,7 +42,7 @@ /** Base class for a group of linked models - See the LinkedModelGroup class for explenations + See the LinkedModelGroup class for explanations Features: * Models are stored by their QObject::objectName diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 9f1b54992cd..d6591a50d7b 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -52,7 +52,7 @@ class PluginIssue; This class would usually be a Model subclass. However, Qt doesn't allow this: - * inhertiting only from Model will cause diamond inheritance for QObject, + * inheriting only from Model will cause diamond inheritance for QObject, which will cause errors with Q_OBJECT * making this a direct subclass of Instrument resp. EffectControls would require CRTP, which would make this class a template class, which would From d29066fa83adbd1e5ecec6573c0583cc66d67ea9 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 5 Sep 2020 23:27:13 +0200 Subject: [PATCH 074/180] Add helpful comment --- src/core/lv2/Lv2Proc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 86235f145b2..ceb9b17123e 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -483,6 +483,8 @@ struct ConnectPortVisitor : public Lv2Ports::Visitor ConnectPortVisitor::~ConnectPortVisitor() {} +// !This function must be realtime safe! +// use createPort to create any port before connecting void Lv2Proc::connectPort(std::size_t num) { ConnectPortVisitor connect; From 3d8b31039f0fec7800b14215b47cc20ed648ab16 Mon Sep 17 00:00:00 2001 From: Dat Ng Date: Sun, 13 Sep 2020 04:09:46 +0200 Subject: [PATCH 075/180] Qt deprecation fix (#5619) Qt6 TODO: Orientation check by comparing angleDelta().x() and y() won't make sense because the direction is arbitrary in Qt 6. --- include/DeprecationHelper.h | 63 +++++++++++++++++++ include/FadeButton.h | 8 +-- include/Fader.h | 11 ++-- include/FileDialog.h | 3 +- include/MainWindow.h | 2 +- include/StepRecorder.h | 6 +- include/SubWindow.h | 2 +- plugins/GigPlayer/PatchesDialog.h | 2 +- .../audio_file_processor.cpp | 2 +- plugins/ladspa_browser/ladspa_port_dialog.cpp | 2 +- plugins/lb302/lb302.cpp | 9 ++- plugins/sf2_player/patches_dialog.h | 2 +- src/core/PluginFactory.cpp | 5 ++ src/core/Song.cpp | 4 ++ src/core/audio/AudioFileOgg.cpp | 11 +++- src/gui/SetupDialog.cpp | 6 +- src/gui/dialogs/FileDialog.cpp | 4 +- src/gui/dialogs/VersionedSaveDialog.cpp | 5 +- src/gui/editors/AutomationEditor.cpp | 51 ++++++++------- src/gui/editors/PianoRoll.cpp | 49 ++++++++------- src/gui/editors/SongEditor.cpp | 30 +++++---- src/gui/widgets/ComboBox.cpp | 4 +- src/gui/widgets/FadeButton.cpp | 8 +-- src/gui/widgets/Fader.cpp | 12 ++-- src/gui/widgets/Knob.cpp | 19 +++--- src/gui/widgets/LcdSpinBox.cpp | 7 +-- src/gui/widgets/LcdWidget.cpp | 19 +++--- src/gui/widgets/LedCheckbox.cpp | 5 +- src/gui/widgets/TabWidget.cpp | 13 ++-- src/tracks/Pattern.cpp | 32 +++++----- 30 files changed, 253 insertions(+), 143 deletions(-) create mode 100644 include/DeprecationHelper.h diff --git a/include/DeprecationHelper.h b/include/DeprecationHelper.h new file mode 100644 index 00000000000..bef4ea9b954 --- /dev/null +++ b/include/DeprecationHelper.h @@ -0,0 +1,63 @@ +/* + * DeprecationHelper.h - This file contains the declarations of helper functions + * which helps centralize the #ifdefs preprocessors regarding deprecation based on Qt versions. + * The functions are defined differently based on the callers' Qt versions. + * + * Copyright (c) 2020 Tien Dat Nguyen + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef DEPRECATIONHELPER_H +#define DEPRECATIONHELPER_H + +#include +#include + +/** + * @brief horizontalAdvance is a backwards-compatible adapter for + * QFontMetrics::horizontalAdvance and width functions. + * @param metrics + * @param text + * @return text's horizontal advance based on metrics. + */ +inline int horizontalAdvance(const QFontMetrics& metrics, const QString& text) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return metrics.horizontalAdvance(text); +#else + return metrics.width(text); +#endif +} + +/** + * @brief position is a backwards-compatible adapter for + * QWheelEvent::position and pos functions. + * @param wheelEvent + * @return the position of wheelEvent + */ +inline QPoint position(QWheelEvent *wheelEvent) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + return wheelEvent->position().toPoint(); +#else + return wheelEvent->pos(); +#endif +} +#endif // DEPRECATIONHELPER_H diff --git a/include/FadeButton.h b/include/FadeButton.h index 54703d19476..dfffe93a284 100644 --- a/include/FadeButton.h +++ b/include/FadeButton.h @@ -26,9 +26,9 @@ #ifndef FADE_BUTTON_H #define FADE_BUTTON_H -#include #include #include +#include class FadeButton : public QAbstractButton @@ -55,8 +55,8 @@ public slots: private: - QTime m_stateTimer; - QTime m_releaseTimer; + QElapsedTimer m_stateTimer; + QElapsedTimer m_releaseTimer; // the default color of the widget QColor m_normalColor; @@ -66,7 +66,7 @@ public slots: QColor m_holdColor; int activeNotes; - QColor fadeToColor(QColor, QColor, QTime, float); + QColor fadeToColor(QColor, QColor, QElapsedTimer, float); } ; diff --git a/include/Fader.h b/include/Fader.h index 2072154459d..bf1e7215e35 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -48,9 +48,10 @@ #ifndef FADER_H #define FADER_H -#include -#include +#include #include +#include + #include "AutomatableModelView.h" @@ -130,7 +131,7 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) ); } - void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTime &lastPeakTime ); + void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer ); int calculateDisplayPeak( float fPeak ); void updateTextFloat(); @@ -144,8 +145,8 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView float m_fMinPeak; float m_fMaxPeak; - QTime m_lastPeakTime_L; - QTime m_lastPeakTime_R; + QElapsedTimer m_lastPeakTimer_L; + QElapsedTimer m_lastPeakTimer_R; static QPixmap * s_back; static QPixmap * s_leds; diff --git a/include/FileDialog.h b/include/FileDialog.h index c3db2393d45..6e29703b8a1 100644 --- a/include/FileDialog.h +++ b/include/FileDialog.h @@ -46,8 +46,7 @@ class LMMS_EXPORT FileDialog : public QFileDialog const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString(), - QString *selectedFilter = 0, - QFileDialog::Options options = 0); + QString *selectedFilter = 0); void clearSelection(); }; diff --git a/include/MainWindow.h b/include/MainWindow.h index 15112456853..a179e651e49 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -61,7 +61,7 @@ class MainWindow : public QMainWindow void addSpacingToToolBar( int _size ); // wrap the widget with a window decoration and add it to the workspace - LMMS_EXPORT SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags=0); + LMMS_EXPORT SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags = QFlag(0)); /// diff --git a/include/StepRecorder.h b/include/StepRecorder.h index b9653b1bbea..8240cc41a61 100644 --- a/include/StepRecorder.h +++ b/include/StepRecorder.h @@ -21,7 +21,7 @@ #ifndef STEP_RECORDER_H #define STEP_RECORDER_H -#include +#include #include #include #include @@ -130,7 +130,7 @@ class StepRecorder : public QObject private: bool m_pressed; - QTime releasedTimer; + QElapsedTimer releasedTimer; } ; QVector m_curStepNotes; // contains the current recorded step notes (i.e. while user still press the notes; before they are applied to the pattern) @@ -140,4 +140,4 @@ class StepRecorder : public QObject bool m_isStepInProgress = false; }; -#endif //STEP_RECORDER_H \ No newline at end of file +#endif //STEP_RECORDER_H diff --git a/include/SubWindow.h b/include/SubWindow.h index 148cf2c9997..55d05425ab8 100644 --- a/include/SubWindow.h +++ b/include/SubWindow.h @@ -55,7 +55,7 @@ class LMMS_EXPORT SubWindow : public QMdiSubWindow Q_PROPERTY( QColor borderColor READ borderColor WRITE setBorderColor ) public: - SubWindow( QWidget *parent = NULL, Qt::WindowFlags windowFlags = 0 ); + SubWindow( QWidget *parent = NULL, Qt::WindowFlags windowFlags = QFlag(0) ); // same as QWidet::normalGeometry, but works properly under X11 (see https://bugreports.qt.io/browse/QTBUG-256) QRect getTrueNormalGeometry() const; QBrush activeColor() const; diff --git a/plugins/GigPlayer/PatchesDialog.h b/plugins/GigPlayer/PatchesDialog.h index 0836631acad..ae00f660aaf 100644 --- a/plugins/GigPlayer/PatchesDialog.h +++ b/plugins/GigPlayer/PatchesDialog.h @@ -43,7 +43,7 @@ class PatchesDialog : public QDialog, private Ui::PatchesDialog public: // Constructor. - PatchesDialog( QWidget * pParent = 0, Qt::WindowFlags wflags = 0 ); + PatchesDialog(QWidget * pParent = 0, Qt::WindowFlags wflags = QFlag(0)); // Destructor. virtual ~PatchesDialog(); diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index dbde6e8c45c..da0f17a3c9e 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -867,7 +867,7 @@ void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we ) { - zoom( _we->delta() > 0 ); + zoom( _we->angleDelta().y() > 0 ); update(); } diff --git a/plugins/ladspa_browser/ladspa_port_dialog.cpp b/plugins/ladspa_browser/ladspa_port_dialog.cpp index d7b124b3fa1..c213c6f0235 100644 --- a/plugins/ladspa_browser/ladspa_port_dialog.cpp +++ b/plugins/ladspa_browser/ladspa_port_dialog.cpp @@ -65,7 +65,7 @@ ladspaPortDialog::ladspaPortDialog( const ladspa_key_t & _key ) for( int col = 0; col < 7; ++col ) { QTableWidgetItem * item = new QTableWidgetItem; - item->setFlags( 0 ); + item->setFlags(QFlag(0)); settings->setItem( row, col, item ); } diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 72bd5a4946e..cc671749db1 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -433,8 +433,11 @@ QString lb302Synth::nodeName() const // OBSOLETE. Break apart once we get Q_OBJECT to work. >:[ void lb302Synth::recalcFilter() { +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + vcf.loadRelaxed()->recalc(); +#else vcf.load()->recalc(); - +#endif // THIS IS OLD 3pole/24dB code, I may reintegrate it. Don't need it // right now. Should be toggled by LB_24_RES_TRICK at the moment. @@ -683,7 +686,11 @@ void lb302Synth::initNote( lb302Note *n) if(n->dead ==0){ // Swap next two blocks?? +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + vcf.loadRelaxed()->playNote(); +#else vcf.load()->playNote(); +#endif // Ensure envelope is recalculated vcf_envpos = ENVINC; diff --git a/plugins/sf2_player/patches_dialog.h b/plugins/sf2_player/patches_dialog.h index a2c88a79d1e..76387e830c4 100644 --- a/plugins/sf2_player/patches_dialog.h +++ b/plugins/sf2_player/patches_dialog.h @@ -43,7 +43,7 @@ class patchesDialog : public QDialog, private Ui::patchesDialog public: // Constructor. - patchesDialog(QWidget *pParent = 0, Qt::WindowFlags wflags = 0); + patchesDialog(QWidget *pParent = 0, Qt::WindowFlags wflags = QFlag(0)); // Destructor. virtual ~patchesDialog(); diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index abf6421229e..16f86a17a4e 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -144,7 +144,12 @@ void PluginFactory::discoverPlugins() QSet files; for (const QString& searchPath : QDir::searchPaths("plugins")) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + auto discoveredPluginList = QDir(searchPath).entryInfoList(nameFilters); + files.unite(QSet(discoveredPluginList.begin(), discoveredPluginList.end())); +#else files.unite(QDir(searchPath).entryInfoList(nameFilters).toSet()); +#endif } // Cheap dependency handling: zynaddsubfx needs ZynAddSubFxCore. By loading diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 5f63e6ee859..07f28821bfc 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1203,7 +1203,11 @@ void Song::loadProject( const QString & fileName ) } else { +#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0)) + QTextStream(stderr) << Engine::getSong()->errorSummary() << Qt::endl; +#else QTextStream(stderr) << Engine::getSong()->errorSummary() << endl; +#endif } } diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index 86f265b1270..ce506f2e17c 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -30,7 +30,9 @@ #ifdef LMMS_HAVE_OGGVORBIS - +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) +#include +#endif #include #include @@ -136,8 +138,13 @@ bool AudioFileOgg::startEncoding() // We give our ogg file a random serial number and avoid // 0 and UINT32_MAX which can get you into trouble. - qsrand( time( 0 ) ); +#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) + QRandomGenerator::global()->seed(time(0)); + m_serialNo = 0xD0000000 + QRandomGenerator::global()->generate() % 0x0FFFFFFF; +#else + qsrand(time(0)); m_serialNo = 0xD0000000 + qrand() % 0x0FFFFFFF; +#endif ogg_stream_init( &m_os, m_serialNo ); // Now, build the three header packets and send through to the stream diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index f06eea9e05c..86ba61eb108 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -1303,7 +1303,7 @@ void SetupDialog::openGIGDir() { QString new_dir = FileDialog::getExistingDirectory(this, tr("Choose your GIG directory"), m_gigDir); - if(new_dir != QString::null) + if(!new_dir.isEmpty()) { m_gigDirLineEdit->setText(new_dir); } @@ -1320,7 +1320,7 @@ void SetupDialog::openThemeDir() { QString new_dir = FileDialog::getExistingDirectory(this, tr("Choose your theme directory"), m_themeDir); - if(new_dir != QString::null) + if(!new_dir.isEmpty()) { m_themeDirLineEdit->setText(new_dir); } @@ -1355,7 +1355,7 @@ void SetupDialog::openBackgroundPicFile() QString new_file = FileDialog::getOpenFileName(this, tr("Choose your background picture"), dir, "Picture files (" + fileTypes + ")"); - if(new_file != QString::null) + if(!new_file.isEmpty()) { m_backgroundPicFileLineEdit->setText(new_file); } diff --git a/src/gui/dialogs/FileDialog.cpp b/src/gui/dialogs/FileDialog.cpp index 54cc9d6e4d7..848a7f52a7c 100644 --- a/src/gui/dialogs/FileDialog.cpp +++ b/src/gui/dialogs/FileDialog.cpp @@ -84,11 +84,9 @@ QString FileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &directory, const QString &filter, - QString *selectedFilter, - QFileDialog::Options options) + QString *selectedFilter) { FileDialog dialog(parent, caption, directory, filter); - dialog.setOptions(dialog.options() | options); if (selectedFilter && !selectedFilter->isEmpty()) dialog.selectNameFilter(*selectedFilter); if (dialog.exec() == QDialog::Accepted) { diff --git a/src/gui/dialogs/VersionedSaveDialog.cpp b/src/gui/dialogs/VersionedSaveDialog.cpp index 18993c23bf4..d26f198915a 100644 --- a/src/gui/dialogs/VersionedSaveDialog.cpp +++ b/src/gui/dialogs/VersionedSaveDialog.cpp @@ -31,6 +31,7 @@ #include #include +#include "DeprecationHelper.h" #include "VersionedSaveDialog.h" #include "LedCheckbox.h" @@ -50,8 +51,8 @@ VersionedSaveDialog::VersionedSaveDialog( QWidget *parent, plusButton->setToolTip( tr( "Increment version number" ) ); QPushButton *minusButton( new QPushButton( "-", this ) ); minusButton->setToolTip( tr( "Decrement version number" ) ); - plusButton->setFixedWidth( plusButton->fontMetrics().width( "+" ) + 30 ); - minusButton->setFixedWidth( minusButton->fontMetrics().width( "+" ) + 30 ); + plusButton->setFixedWidth(horizontalAdvance(plusButton->fontMetrics(), "+") + 30); + minusButton->setFixedWidth(horizontalAdvance(minusButton->fontMetrics(), "+") + 30); // Add buttons to grid layout. For doing this, remove the lineEdit and // replace it with a HBox containing lineEdit and the buttons. diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index af9ea3b08a4..b64cea0f5f2 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -45,21 +45,22 @@ #endif #include "ActionGroup.h" -#include "SongEditor.h" -#include "MainWindow.h" +#include "BBTrackContainer.h" +#include "ComboBox.h" +#include "debug.h" +#include "DeprecationHelper.h" #include "GuiApplication.h" +#include "MainWindow.h" #include "embed.h" #include "Engine.h" #include "gui_templates.h" -#include "TimeLineWidget.h" -#include "ToolTip.h" -#include "TextFloat.h" -#include "ComboBox.h" -#include "BBTrackContainer.h" #include "PianoRoll.h" -#include "debug.h" -#include "StringPairDrag.h" #include "ProjectJournal.h" +#include "SongEditor.h" +#include "StringPairDrag.h" +#include "TextFloat.h" +#include "TimeLineWidget.h" +#include "ToolTip.h" QPixmap * AutomationEditor::s_toolDraw = NULL; @@ -1677,11 +1678,11 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::ShiftModifier ) { int y = m_zoomingYModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { y++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { y--; } @@ -1691,11 +1692,11 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::AltModifier ) { int q = m_quantizeModel.value(); - if( we->delta() > 0 ) + if((we->angleDelta().x() + we->angleDelta().y()) > 0) // alt + scroll becomes horizontal scroll on KDE { q--; } - else if( we->delta() < 0 ) + else if((we->angleDelta().x() + we->angleDelta().y()) < 0) // alt + scroll becomes horizontal scroll on KDE { q++; } @@ -1706,17 +1707,17 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier ) { int x = m_zoomingXModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { x++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { x--; } x = qBound( 0, x, m_zoomingXModel.size() - 1 ); - int mouseX = (we->x() - VALUES_WIDTH)* MidiTime::ticksPerBar(); + int mouseX = (position( we ).x() - VALUES_WIDTH)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = mouseX / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -1728,16 +1729,22 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) m_zoomingXModel.setValue( x ); } - else if( we->modifiers() & Qt::ShiftModifier - || we->orientation() == Qt::Horizontal ) + + // FIXME: Reconsider if determining orientation is necessary in Qt6. + else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + { + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().x() * 2 / 15); + } + else if(we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue( m_leftRightScroll->value() - - we->delta() * 2 / 15 ); + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().y() * 2 / 15); } else { - m_topBottomScroll->setValue( m_topBottomScroll->value() - - we->delta() / 30 ); + m_topBottomScroll->setValue(m_topBottomScroll->value() - + (we->angleDelta().x() + we->angleDelta().y()) / 30); } } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 0dc659dceb8..c63225440dc 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -47,11 +47,12 @@ #include "AutomationEditor.h" #include "ActionGroup.h" -#include "ConfigManager.h" #include "BBTrackContainer.h" #include "Clipboard.h" #include "ComboBox.h" +#include "ConfigManager.h" #include "debug.h" +#include "DeprecationHelper.h" #include "DetuningHelper.h" #include "embed.h" #include "GuiApplication.h" @@ -61,9 +62,9 @@ #include "Pattern.h" #include "SongEditor.h" #include "stdshims.h" +#include "StepRecorderWidget.h" #include "TextFloat.h" #include "TimeLineWidget.h" -#include "StepRecorderWidget.h" using std::move; @@ -3321,13 +3322,13 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { we->accept(); // handle wheel events for note edit area - for editing note vol/pan with mousewheel - if( we->x() > noteEditLeft() && we->x() < noteEditRight() - && we->y() > noteEditTop() && we->y() < noteEditBottom() ) + if(position(we).x() > noteEditLeft() && position(we).x() < noteEditRight() + && position(we).y() > noteEditTop() && position(we).y() < noteEditBottom()) { if (!hasValidPattern()) {return;} // get values for going through notes int pixel_range = 8; - int x = we->x() - m_whiteKeyWidth; + int x = position(we).x() - m_whiteKeyWidth; int ticks_start = ( x - pixel_range / 2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * @@ -3346,7 +3347,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } if( nv.size() > 0 ) { - const int step = we->delta() > 0 ? 1 : -1; + const int step = we->angleDelta().y() > 0 ? 1 : -1; if( m_noteEditMode == NoteEditVolume ) { for ( Note * n : nv ) @@ -3363,7 +3364,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { // show the volume hover-text only if all notes have the // same volume - showVolTextFloat( nv[0]->getVolume(), we->pos(), 1000 ); + showVolTextFloat(nv[0]->getVolume(), position(we), 1000); } } else if( m_noteEditMode == NoteEditPanning ) @@ -3382,7 +3383,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { // show the pan hover-text only if all notes have the same // panning - showPanTextFloat( nv[0]->getPanning(), we->pos(), 1000 ); + showPanTextFloat( nv[0]->getPanning(), position( we ), 1000 ); } } update(); @@ -3394,11 +3395,11 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::AltModifier ) { int q = m_quantizeModel.value(); - if( we->delta() > 0 ) + if((we->angleDelta().x() + we->angleDelta().y()) > 0) // alt + scroll becomes horizontal scroll on KDE { q--; } - else if( we->delta() < 0 ) + else if((we->angleDelta().x() + we->angleDelta().y()) < 0) // alt + scroll becomes horizontal scroll on KDE { q++; } @@ -3408,11 +3409,11 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::ShiftModifier ) { int l = m_noteLenModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { l--; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { l++; } @@ -3422,17 +3423,17 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) else if( we->modifiers() & Qt::ControlModifier ) { int z = m_zoomingModel.value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { z++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { z--; } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (we->x() - m_whiteKeyWidth)* MidiTime::ticksPerBar(); + int x = (position(we).x() - m_whiteKeyWidth) * MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -3442,16 +3443,22 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) // update combobox with zooming-factor m_zoomingModel.setValue( z ); } - else if( we->modifiers() & Qt::ShiftModifier - || we->orientation() == Qt::Horizontal ) + + // FIXME: Reconsider if determining orientation is necessary in Qt6. + else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + { + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().x() * 2 / 15); + } + else if(we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue( m_leftRightScroll->value() - - we->delta() * 2 / 15 ); + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().y() * 2 / 15); } else { - m_topBottomScroll->setValue( m_topBottomScroll->value() - - we->delta() / 30 ); + m_topBottomScroll->setValue(m_topBottomScroll->value() - + we->angleDelta().y() / 30); } } diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index e738708ac09..492de0e0092 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -24,7 +24,6 @@ #include "SongEditor.h" -#include #include #include #include @@ -32,24 +31,26 @@ #include #include #include +#include +#include "AudioDevice.h" #include "AutomatableSlider.h" #include "ComboBox.h" #include "ConfigManager.h" #include "CPULoadWidget.h" +#include "DeprecationHelper.h" #include "embed.h" #include "GuiApplication.h" #include "LcdSpinBox.h" #include "MainWindow.h" #include "MeterDialog.h" #include "Mixer.h" +#include "Oscilloscope.h" +#include "PianoRoll.h" #include "TextFloat.h" +#include "TimeDisplayWidget.h" #include "TimeLineWidget.h" #include "ToolTip.h" -#include "Oscilloscope.h" -#include "TimeDisplayWidget.h" -#include "AudioDevice.h" -#include "PianoRoll.h" #include "Track.h" const QVector SongEditor::m_zoomLevels = @@ -527,18 +528,18 @@ void SongEditor::wheelEvent( QWheelEvent * we ) { int z = m_zoomingModel->value(); - if( we->delta() > 0 ) + if(we->angleDelta().y() > 0) { z++; } - else if( we->delta() < 0 ) + else if(we->angleDelta().y() < 0) { z--; } z = qBound( 0, z, m_zoomingModel->size() - 1 ); - int x = we->x() - m_trackHeadWidth; + int x = position(we).x() - m_trackHeadWidth; // bar based on the mouse x-position where the scroll wheel was used int bar = x / pixelsPerBar(); // what would be the bar in the new zoom level on the very same mouse x @@ -555,10 +556,17 @@ void SongEditor::wheelEvent( QWheelEvent * we ) // and make sure, all TCO's are resized and relocated realignTracks(); } - else if( we->modifiers() & Qt::ShiftModifier || we->orientation() == Qt::Horizontal ) + + // FIXME: Reconsider if determining orientation is necessary in Qt6. + else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + { + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().x() /30); + } + else if(we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue( m_leftRightScroll->value() - - we->delta() / 30 ); + m_leftRightScroll->setValue(m_leftRightScroll->value() - + we->angleDelta().y() / 30); } else { diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index 69955501d61..4ef43bee74e 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -194,7 +194,7 @@ void ComboBox::paintEvent( QPaintEvent * _pe ) // Border QStyleOptionFrame opt; opt.initFrom( this ); - opt.state = 0; + opt.state = QStyle::StateFlag::State_None; style()->drawPrimitive( QStyle::PE_Frame, &opt, &p, this ); @@ -232,7 +232,7 @@ void ComboBox::wheelEvent( QWheelEvent* event ) { if( model() ) { - model()->setInitValue( model()->value() + ( ( event->delta() < 0 ) ? 1 : -1 ) ); + model()->setInitValue(model()->value() + ((event->angleDelta().y() < 0) ? 1 : -1)); update(); event->accept(); } diff --git a/src/gui/widgets/FadeButton.cpp b/src/gui/widgets/FadeButton.cpp index 43f8061441a..c40569c2181 100644 --- a/src/gui/widgets/FadeButton.cpp +++ b/src/gui/widgets/FadeButton.cpp @@ -110,20 +110,20 @@ void FadeButton::paintEvent(QPaintEvent * _pe) { QColor col = m_normalColor; - if(!m_stateTimer.isNull() && m_stateTimer.elapsed() < FadeDuration) + if(m_stateTimer.isValid() && m_stateTimer.elapsed() < FadeDuration) { // The first part of the fade, when a note is triggered. col = fadeToColor(m_activatedColor, m_holdColor, m_stateTimer, FadeDuration); QTimer::singleShot(20, this, SLOT(update())); } - else if (!m_stateTimer.isNull() + else if (m_stateTimer.isValid() && m_stateTimer.elapsed() >= FadeDuration && activeNotes > 0) { // The fade is done, but at least one note is still held. col = m_holdColor; } - else if (!m_releaseTimer.isNull() && m_releaseTimer.elapsed() < FadeDuration) + else if (m_releaseTimer.isValid() && m_releaseTimer.elapsed() < FadeDuration) { // Last note just ended. Fade to default color. col = fadeToColor(m_holdColor, m_normalColor, m_releaseTimer, FadeDuration); @@ -149,7 +149,7 @@ void FadeButton::paintEvent(QPaintEvent * _pe) } -QColor FadeButton::fadeToColor(QColor startCol, QColor endCol, QTime timer, float duration) +QColor FadeButton::fadeToColor(QColor startCol, QColor endCol, QElapsedTimer timer, float duration) { QColor col; diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 4317066ab65..1f0a13ec173 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -265,7 +265,7 @@ void Fader::wheelEvent ( QWheelEvent *ev ) { ev->accept(); - if ( ev->delta() > 0 ) + if (ev->angleDelta().y() > 0) { model()->incValue( 1 ); } @@ -282,7 +282,7 @@ void Fader::wheelEvent ( QWheelEvent *ev ) /// /// Set peak value (0.0 .. 1.0) /// -void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTime &lastPeakTime ) +void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer ) { if( fPeak < m_fMinPeak ) { @@ -299,12 +299,12 @@ void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTim if( targetPeak >= persistentPeak ) { persistentPeak = targetPeak; - lastPeakTime.restart(); + lastPeakTimer.restart(); } update(); } - if( persistentPeak > 0 && lastPeakTime.elapsed() > 1500 ) + if( persistentPeak > 0 && lastPeakTimer.elapsed() > 1500 ) { persistentPeak = qMax( 0, persistentPeak-0.05 ); update(); @@ -315,14 +315,14 @@ void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTim void Fader::setPeak_L( float fPeak ) { - setPeak( fPeak, m_fPeakValue_L, m_persistentPeak_L, m_lastPeakTime_L ); + setPeak( fPeak, m_fPeakValue_L, m_persistentPeak_L, m_lastPeakTimer_L ); } void Fader::setPeak_R( float fPeak ) { - setPeak( fPeak, m_fPeakValue_R, m_persistentPeak_R, m_lastPeakTime_R ); + setPeak( fPeak, m_fPeakValue_R, m_persistentPeak_R, m_lastPeakTimer_R ); } diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 920c9765ddb..e3d22f69ab4 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -38,6 +38,7 @@ #include "CaptionMenu.h" #include "ConfigManager.h" #include "ControllerConnection.h" +#include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" #include "GuiApplication.h" @@ -168,9 +169,9 @@ void Knob::setLabel( const QString & txt ) m_label = txt; if( m_knobPixmap ) { - setFixedSize( qMax( m_knobPixmap->width(), - QFontMetrics( pointSizeF( font(), 6.5) ).width( m_label ) ), - m_knobPixmap->height() + 10 ); + setFixedSize(qMax( m_knobPixmap->width(), + horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)), + m_knobPixmap->height() + 10); } update(); } @@ -682,20 +683,20 @@ void Knob::paintEvent( QPaintEvent * _me ) p.fontMetrics().width( m_label ) / 2 + 1, height() - 1, m_label );*/ p.setPen( textColor() ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2, - height() - 2, m_label ); + p.drawText(width() / 2 - + horizontalAdvance(p.fontMetrics(), m_label) / 2, + height() - 2, m_label); } } -void Knob::wheelEvent( QWheelEvent * _we ) +void Knob::wheelEvent(QWheelEvent * we) { - _we->accept(); + we->accept(); const float stepMult = model()->range() / 2000 / model()->step(); - const int inc = ( ( _we->delta() > 0 ) ? 1 : -1 ) * ( ( stepMult < 1 ) ? 1 : stepMult ); + const int inc = ((we->angleDelta().y() > 0 ) ? 1 : -1) * ((stepMult < 1 ) ? 1 : stepMult); model()->incValue( inc ); diff --git a/src/gui/widgets/LcdSpinBox.cpp b/src/gui/widgets/LcdSpinBox.cpp index 446639090f6..6aacc01dd5a 100644 --- a/src/gui/widgets/LcdSpinBox.cpp +++ b/src/gui/widgets/LcdSpinBox.cpp @@ -149,11 +149,10 @@ void LcdSpinBox::mouseReleaseEvent( QMouseEvent* ) -void LcdSpinBox::wheelEvent( QWheelEvent * _we ) +void LcdSpinBox::wheelEvent(QWheelEvent * we) { - _we->accept(); - model()->setInitValue( model()->value() + - ( ( _we->delta() > 0 ) ? 1 : -1 ) * model()->step() ); + we->accept(); + model()->setInitValue(model()->value() + ((we->angleDelta().y() > 0) ? 1 : -1) * model()->step()); emit manualChange(); } diff --git a/src/gui/widgets/LcdWidget.cpp b/src/gui/widgets/LcdWidget.cpp index e34e1eb47b3..63e3b6ac0c5 100644 --- a/src/gui/widgets/LcdWidget.cpp +++ b/src/gui/widgets/LcdWidget.cpp @@ -32,6 +32,7 @@ #include #include "LcdWidget.h" +#include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" #include "MainWindow.h" @@ -199,13 +200,13 @@ void LcdWidget::paintEvent( QPaintEvent* ) { p.setFont( pointSizeF( p.font(), 6.5 ) ); p.setPen( textShadowColor() ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2 + 1, - height(), m_label ); + p.drawText(width() / 2 - + horizontalAdvance(p.fontMetrics(), m_label) / 2 + 1, + height(), m_label); p.setPen( textColor() ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2, - height() - 1, m_label ); + p.drawText(width() / 2 - + horizontalAdvance(p.fontMetrics(), m_label) / 2, + height() - 1, m_label); } } @@ -240,10 +241,10 @@ void LcdWidget::updateSize() m_cellHeight + (2*margin) ); } else { - setFixedSize( qMax( + setFixedSize(qMax( m_cellWidth * m_numDigits + 2*(margin+m_marginWidth), - QFontMetrics( pointSizeF( font(), 6.5 ) ).width( m_label ) ), - m_cellHeight + (2*margin) + 9 ); + horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)), + m_cellHeight + (2*margin) + 9); } update(); diff --git a/src/gui/widgets/LedCheckbox.cpp b/src/gui/widgets/LedCheckbox.cpp index bdb537744f7..1ce64f1a85e 100644 --- a/src/gui/widgets/LedCheckbox.cpp +++ b/src/gui/widgets/LedCheckbox.cpp @@ -27,6 +27,7 @@ #include #include "LedCheckbox.h" +#include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" @@ -120,7 +121,9 @@ void LedCheckBox::initUi( LedColors _color ) void LedCheckBox::onTextUpdated() { - setFixedSize( m_ledOffPixmap->width() + 5 + QFontMetrics( font() ).width( text() ), m_ledOffPixmap->height() ); + setFixedSize(m_ledOffPixmap->width() + 5 + horizontalAdvance(QFontMetrics(font()), + text()), + m_ledOffPixmap->height()); } diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 22d32261210..07889c331f5 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -31,8 +31,9 @@ #include #include -#include "gui_templates.h" +#include "DeprecationHelper.h" #include "embed.h" +#include "gui_templates.h" TabWidget::TabWidget(const QString & caption, QWidget * parent, bool usePixmap, bool resizable) : @@ -76,7 +77,7 @@ void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, i } // Tab's width when it is a text tab. This isn't correct for artwork tabs, but it's fixed later during the PaintEvent - int tab_width = fontMetrics().width( name ) + 10; + int tab_width = horizontalAdvance(fontMetrics(), name) + 10; // Register new tab widgetDesc d = { w, pixmap, name, tab_width }; @@ -125,7 +126,7 @@ int TabWidget::findTabAtPos( const QPoint *pos ) if( pos->y() > 1 && pos->y() < m_tabbarHeight - 1 ) { - int cx = ( ( m_caption == "" ) ? 4 : 14 ) + fontMetrics().width( m_caption ); + int cx = ((m_caption == "") ? 4 : 14) + horizontalAdvance(fontMetrics(), m_caption); for( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it ) { @@ -232,7 +233,7 @@ void TabWidget::paintEvent( QPaintEvent * pe ) } // Calculate the tabs' x (tabs are painted next to the caption) - int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + fontMetrics().width( m_caption ); + int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + horizontalAdvance(fontMetrics(), m_caption); // Compute tabs' width depending on the number of tabs (only applicable for artwork tabs) widgetStack::iterator first = m_widgets.begin(); @@ -288,13 +289,13 @@ void TabWidget::paintEvent( QPaintEvent * pe ) // Switch between tabs with mouse wheel void TabWidget::wheelEvent( QWheelEvent * we ) { - if( we->y() > m_tabheight ) + if(position(we).y() > m_tabheight) { return; } we->accept(); - int dir = ( we->delta() < 0 ) ? 1 : -1; + int dir = (we->angleDelta().y() < 0) ? 1 : -1; int tab = m_activeTab; while( tab > -1 && static_cast( tab ) < m_widgets.count() ) { diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 40f11b3cb30..52259c70733 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -24,23 +24,21 @@ */ #include "Pattern.h" +#include #include #include #include #include #include -#include "InstrumentTrack.h" -#include "gui_templates.h" +#include "AudioSampleRecorder.h" +#include "BBTrackContainer.h" +#include "DeprecationHelper.h" #include "embed.h" #include "GuiApplication.h" +#include "InstrumentTrack.h" #include "PianoRoll.h" #include "RenameDialog.h" -#include "SampleBuffer.h" -#include "AudioSampleRecorder.h" -#include "BBTrackContainer.h" -#include "StringPairDrag.h" -#include "MainWindow.h" #include @@ -782,16 +780,16 @@ void PatternView::mouseDoubleClickEvent(QMouseEvent *_me) -void PatternView::wheelEvent( QWheelEvent * _we ) +void PatternView::wheelEvent(QWheelEvent * we) { - if( m_pat->m_patternType == Pattern::BeatPattern && - ( fixedTCOs() || pixelsPerBar() >= 96 ) && - _we->y() > height() - s_stepBtnOff->height() ) + if(m_pat->m_patternType == Pattern::BeatPattern && + (fixedTCOs() || pixelsPerBar() >= 96) && + position(we).y() > height() - s_stepBtnOff->height()) { // get the step number that was wheeled on and // do calculations in floats to prevent rounding errors... - float tmp = ( ( float(_we->x()) - TCO_BORDER_WIDTH ) * - float( m_pat -> m_steps ) ) / float(width() - TCO_BORDER_WIDTH*2); + float tmp = ((float(position(we).x()) - TCO_BORDER_WIDTH) * + float(m_pat -> m_steps)) / float(width() - TCO_BORDER_WIDTH*2); int step = int( tmp ); @@ -801,7 +799,7 @@ void PatternView::wheelEvent( QWheelEvent * _we ) } Note * n = m_pat->noteAtStep( step ); - if( !n && _we->delta() > 0 ) + if(!n && we->angleDelta().y() > 0) { n = m_pat->addStepNote( step ); n->setVolume( 0 ); @@ -810,7 +808,7 @@ void PatternView::wheelEvent( QWheelEvent * _we ) { int vol = n->getVolume(); - if( _we->delta() > 0 ) + if(we->angleDelta().y() > 0) { n->setVolume( qMin( 100, vol + 5 ) ); } @@ -826,11 +824,11 @@ void PatternView::wheelEvent( QWheelEvent * _we ) gui->pianoRoll()->update(); } } - _we->accept(); + we->accept(); } else { - TrackContentObjectView::wheelEvent( _we ); + TrackContentObjectView::wheelEvent(we); } } From 76a182bb951d21d32d7672992acb7ac722f39a2c Mon Sep 17 00:00:00 2001 From: Veratil Date: Tue, 18 Aug 2020 19:45:20 -0500 Subject: [PATCH 076/180] Update portsmf to latest r234 commit --- plugins/MidiImport/portsmf/algrd_internal.h | 9 +- plugins/MidiImport/portsmf/allegro.cpp | 1254 +++++++++++---- plugins/MidiImport/portsmf/allegro.h | 351 +++-- plugins/MidiImport/portsmf/allegrord.cpp | 1527 ++++++++++--------- plugins/MidiImport/portsmf/allegrosmfrd.cpp | 900 +++++------ plugins/MidiImport/portsmf/allegrosmfwr.cpp | 62 +- plugins/MidiImport/portsmf/allegrowr.cpp | 28 +- plugins/MidiImport/portsmf/mfmidi.cpp | 33 +- plugins/MidiImport/portsmf/mfmidi.h | 20 +- plugins/MidiImport/portsmf/strparse.cpp | 12 +- plugins/MidiImport/portsmf/strparse.h | 2 +- plugins/MidiImport/portsmf/trace.cpp | 2 +- 12 files changed, 2530 insertions(+), 1670 deletions(-) diff --git a/plugins/MidiImport/portsmf/algrd_internal.h b/plugins/MidiImport/portsmf/algrd_internal.h index 3b77adc4cd2..7e3ac88522b 100644 --- a/plugins/MidiImport/portsmf/algrd_internal.h +++ b/plugins/MidiImport/portsmf/algrd_internal.h @@ -1,4 +1,5 @@ -/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */ - -Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq); - +/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */ + +Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq, + double *offset_ptr = NULL); + diff --git a/plugins/MidiImport/portsmf/allegro.cpp b/plugins/MidiImport/portsmf/allegro.cpp index 3f7b84073a5..3c5a2a5a575 100644 --- a/plugins/MidiImport/portsmf/allegro.cpp +++ b/plugins/MidiImport/portsmf/allegro.cpp @@ -9,7 +9,7 @@ 04 apr 03 -- fixed bug in add_track that caused infinite loop */ -#include "debug.h" +#include "assert.h" #include "stdlib.h" #include "stdio.h" #include "string.h" @@ -25,13 +25,15 @@ using namespace std; #define STREQL(x, y) (strcmp(x, y) == 0) #define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ROUND(x) ((int) ((x) + 0.5)) // 4311 is type cast ponter to long warning // 4996 is warning against strcpy // 4267 is size_t to long warning -//#pragma warning(disable: 4311 4996 4267) +#pragma warning(disable: 4311 4996 4267) Alg_atoms symbol_table; -Serial_buffer Alg_track::ser_buf; // declare the static variable +Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables +Serial_write_buffer Alg_track::ser_write_buf; bool within(double d1, double d2, double epsilon) { @@ -52,12 +54,10 @@ void Alg_atoms::expand() { maxlen = (maxlen + 5); // extra growth for small sizes maxlen += (maxlen >> 2); // add 25% - char **new_atoms = new Alg_attribute[maxlen]; + Alg_attribute *new_atoms = new Alg_attribute[maxlen]; // now do copy - if (atoms) { - memcpy(new_atoms, atoms, len * sizeof(Alg_attribute)); - delete[] atoms; - } + memcpy(new_atoms, atoms, len * sizeof(Alg_attribute)); + if (atoms) delete[] atoms; atoms = new_atoms; } @@ -81,6 +81,7 @@ Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type) Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr) { + // should use hash algorithm for (int i = 0; i < len; i++) { if (STREQL(attr, atoms[i])) { return atoms[i]; @@ -123,7 +124,7 @@ void Alg_parameter::show() printf("%s:%s", attr_name(), s); break; case 'i': - printf("%s:%d", attr_name(), (int) i); + printf("%s:%ld", attr_name(), i); break; case 'l': printf("%s:%s", attr_name(), (l ? "t" : "f")); @@ -143,7 +144,8 @@ Alg_parameter::~Alg_parameter() } -void Alg_parameters::insert_real(Alg_parameters **list, char *name, double r) +void Alg_parameters::insert_real(Alg_parameters **list, const char *name, + double r) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -153,7 +155,8 @@ void Alg_parameters::insert_real(Alg_parameters **list, char *name, double r) } -void Alg_parameters::insert_string(Alg_parameters **list, char *name, char *s) +void Alg_parameters::insert_string(Alg_parameters **list, const char *name, + const char *s) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -164,7 +167,8 @@ void Alg_parameters::insert_string(Alg_parameters **list, char *name, char *s) } -void Alg_parameters::insert_integer(Alg_parameters **list, char *name, long i) +void Alg_parameters::insert_integer(Alg_parameters **list, const char *name, + long i) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -174,7 +178,8 @@ void Alg_parameters::insert_integer(Alg_parameters **list, char *name, long i) } -void Alg_parameters::insert_logical(Alg_parameters **list, char *name, bool l) +void Alg_parameters::insert_logical(Alg_parameters **list, const char *name, + bool l) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -184,7 +189,8 @@ void Alg_parameters::insert_logical(Alg_parameters **list, char *name, bool l) } -void Alg_parameters::insert_atom(Alg_parameters **list, char *name, char *s) +void Alg_parameters::insert_atom(Alg_parameters **list, const char *name, + const char *s) { Alg_parameters_ptr a = new Alg_parameters(*list); *list = a; @@ -194,7 +200,8 @@ void Alg_parameters::insert_atom(Alg_parameters **list, char *name, char *s) } -Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, const char *name) +Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, + const char *name) { while (*list) { if (STREQL((*list)->parm.attr_name(), name)) { @@ -209,12 +216,12 @@ Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, const char *na } -Alg_parameter_ptr Alg_parameters::find(Alg_attribute *attr) +Alg_parameter_ptr Alg_parameters::find(Alg_attribute attr) { assert(attr); Alg_parameters_ptr temp = this; while (temp) { - if (temp->parm.attr == *attr) { + if (temp->parm.attr == attr) { return &(temp->parm); } } @@ -226,9 +233,9 @@ int Alg_event::get_type_code() { if (!is_note()) { const char* attr = get_attribute(); - if (STREQL(attr, "gate")) // volume change + if (STREQL(attr, "gater")) // volume change return ALG_GATE; - if (STREQL(attr, "bend")) // pitch bend + if (STREQL(attr, "bendr")) // pitch bend return ALG_BEND; if (strncmp(attr, "control", 7) == 0) // control change // note that midi control changes have attributes of the form @@ -237,15 +244,15 @@ int Alg_event::get_type_code() // We don't check for decimal numbers in the range 0-127, so any // attribute that begins with "control" is an ALG_CONTROL: return ALG_CONTROL; - if (STREQL(attr, "program")) // program change + if (STREQL(attr, "programi")) // program change return ALG_PROGRAM; - if (STREQL(attr, "pressure")) // pressure change + if (STREQL(attr, "pressurer")) // pressure change return ALG_PRESSURE; - if (STREQL(attr, "keysig")) // key signature + if (STREQL(attr, "keysigi")) // key signature return ALG_KEYSIG; - if (STREQL(attr, "timesig_num")) // time signature numerator + if (STREQL(attr, "timesig_numi")) // time signature numerator return ALG_TIMESIG_NUM; - if (STREQL(attr, "timesig_den")) // time signature denominator + if (STREQL(attr, "timesig_deni")) // time signature denominator return ALG_TIMESIG_DEN; return ALG_OTHER; } @@ -258,7 +265,7 @@ void Alg_event::set_parameter(Alg_parameter_ptr new_parameter) Alg_parameter_ptr parm; if (is_note()) { Alg_note_ptr note = (Alg_note_ptr) this; - parm = note->parameters->find(&(new_parameter->attr)); + parm = note->parameters->find(new_parameter->attr); if (!parm) { note->parameters = new Alg_parameters(note->parameters); parm = &(note->parameters->parm); @@ -271,7 +278,7 @@ void Alg_event::set_parameter(Alg_parameter_ptr new_parameter) } -void Alg_event::set_string_value(char *a, char *value) +void Alg_event::set_string_value(const char *a, const char *value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -284,7 +291,7 @@ void Alg_event::set_string_value(char *a, char *value) } -void Alg_event::set_real_value(char *a, double value) +void Alg_event::set_real_value(const char *a, double value) { assert(a); // must be non-null // attr is like a, but it has the type code prefixed for @@ -300,7 +307,7 @@ void Alg_event::set_real_value(char *a, double value) } -void Alg_event::set_logical_value(char *a, bool value) +void Alg_event::set_logical_value(const char *a, bool value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -313,7 +320,7 @@ void Alg_event::set_logical_value(char *a, bool value) } -void Alg_event::set_integer_value(char *a, long value) +void Alg_event::set_integer_value(const char *a, long value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -326,7 +333,7 @@ void Alg_event::set_integer_value(char *a, long value) } -void Alg_event::set_atom_value(char *a, char *value) +void Alg_event::set_atom_value(const char *a, const char *value) { assert(a); // must be non-null Alg_attribute attr = symbol_table.insert_string(a); @@ -402,18 +409,18 @@ void Alg_event::set_duration(double d) } -bool Alg_event::has_attribute(char *a) +bool Alg_event::has_attribute(const char *a) { assert(is_note()); assert(a); // must be non-null Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); return parm != NULL; } -char Alg_event::get_attribute_type(char *a) +char Alg_event::get_attribute_type(const char *a) { assert(is_note()); assert(a); @@ -421,66 +428,66 @@ char Alg_event::get_attribute_type(char *a) } -char *Alg_event::get_string_value(char *a, char *value) +const char *Alg_event::get_string_value(const char *a, const char *value) { assert(is_note()); assert(a); // must be non-null Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 's'); // must be of type string - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->s; return value; } -double Alg_event::get_real_value(char *a, double value) +double Alg_event::get_real_value(const char *a, double value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'r'); // must be of type real - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->r; return value; } -bool Alg_event::get_logical_value(char *a, bool value) +bool Alg_event::get_logical_value(const char *a, bool value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'l'); // must be of type logical - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->l; return value; } -long Alg_event::get_integer_value(char *a, long value) +long Alg_event::get_integer_value(const char *a, long value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'i'); // must be of type integer - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->i; return value; } -char *Alg_event::get_atom_value(char *a, char *value) +const char *Alg_event::get_atom_value(const char *a, const char *value) { assert(is_note()); assert(a); Alg_note* note = (Alg_note *) this; Alg_attribute attr = symbol_table.insert_string(a); assert(a[0] == 'a'); // must be of type atom - Alg_parameter_ptr parm = note->parameters->find(&attr); + Alg_parameter_ptr parm = note->parameters->find(attr); if (parm) return parm->a; // if default is a string, convert to an atom (unique // string in symbol table) and return it @@ -489,7 +496,7 @@ char *Alg_event::get_atom_value(char *a, char *value) } -void Alg_event::delete_attribute(char *a) +void Alg_event::delete_attribute(const char *a) { assert(is_note()); Alg_note* note = (Alg_note *) this; @@ -514,12 +521,12 @@ char Alg_event::get_update_type() } -char *Alg_event::get_string_value() +const char *Alg_event::get_string_value() { assert(is_update()); Alg_update* update = (Alg_update *) this; assert(get_update_type() == 's'); - return update->parameter.a; + return update->parameter.s; } @@ -550,7 +557,7 @@ long Alg_event::get_integer_value() } -char *Alg_event::get_atom_value() +const char *Alg_event::get_atom_value() { assert(is_update()); Alg_update* update = (Alg_update *) this; @@ -566,7 +573,7 @@ bool Alg_event::overlap(double t, double len, bool all) return true; if (all && is_note()) { double dur = ((Alg_note_ptr) this)->dur; - // note ends within region + // note overlaps with region if (time < t && time + dur - ALG_EPS > t) return true; } @@ -601,9 +608,9 @@ Alg_note::~Alg_note() void Alg_note::show() { - printf("Alg_note: time %g, chan %d, dur %g, key %d, " + printf("Alg_note: time %g, chan %ld, dur %g, key %ld, " "pitch %g, loud %g, attributes ", - time, (int) chan, dur, (int) key, pitch, loud); + time, chan, dur, key, pitch, loud); Alg_parameters_ptr parms = parameters; while (parms) { parms->parm.show(); @@ -670,6 +677,8 @@ Alg_event_ptr Alg_events::uninsert(long index) { assert(0 <= index && index < len); Alg_event_ptr event = events[index]; + //printf("memmove: %x from %x (%d)\n", events + index, events + index + 1, + // sizeof(Alg_event_ptr) * (len - index - 1)); memmove(events + index, events + index + 1, sizeof(Alg_event_ptr) * (len - index - 1)); len--; @@ -695,6 +704,7 @@ void Alg_events::append(Alg_event_ptr event) Alg_events::~Alg_events() { + assert(!in_use); // individual events are not deleted, only the array if (events) { delete[] events; @@ -729,8 +739,8 @@ void Alg_event_list::set_start_time(Alg_event *event, double t) // For Alg_track, change the time and move the event to the right place // For Alg_seq, find the track and do the update there - long index = 0, i; - Alg_track_ptr track_ptr = Alg_track_ptr(); + long index, i; + Alg_track_ptr track_ptr; if (type == 'e') { // this is an Alg_event_list // make sure the owner has not changed its event set assert(events_owner && @@ -847,7 +857,12 @@ double Alg_time_map::beat_to_time(double beat) return beat; } int i = locate_beat(beat); - if (i == beats.len) { + // case 1: beat is between two time/beat pairs + if (0 < i && i < beats.len) { + mbi = &beats[i - 1]; + mbi1 = &beats[i]; + // case 2: beat is beyond last time/beat pair + } else if (i == beats.len) { if (last_tempo_flag) { return beats[i - 1].time + (beat - beats[i - 1].beat) / last_tempo; @@ -858,11 +873,11 @@ double Alg_time_map::beat_to_time(double beat) mbi = &beats[i - 2]; mbi1 = &beats[i - 1]; } - } else { - mbi = &beats[i - 1]; - mbi1 = &beats[i]; + // case 3: beat is at time 0 + } else /* if (i == 0) */ { + return beats[0].time; } - // whether w extrapolate or interpolate, the math is the same + // whether we extrapolate or interpolate, the math is the same double time_dif = mbi1->time - mbi->time; double beat_dif = mbi1->beat - mbi->beat; return mbi->time + (beat - mbi->beat) * time_dif / beat_dif; @@ -945,6 +960,7 @@ bool Alg_time_map::insert_tempo(double tempo, double beat) // compute difference too diff = diff - old_diff; // apply new_diff to score and beats + i++; while (i < beats.len) { beats[i].time = beats[i].time + diff; i++; @@ -954,6 +970,38 @@ bool Alg_time_map::insert_tempo(double tempo, double beat) } +double Alg_time_map::get_tempo(double beat) +{ + Alg_beat_ptr mbi; + Alg_beat_ptr mbi1; + // if beat < 0, there is probably an error; return something nice anyway + if (beat < 0) return ALG_DEFAULT_BPM / 60.0; + long i = locate_beat(beat); + // this code is similar to beat_to_time() so far, but we want to get + // beyond beat if possible because we want the tempo FOLLOWING beat + // (Consider the case beat == 0.0) + if (i < beats.len && beat >= beats[i].beat) i++; + // case 1: beat is between two time/beat pairs + if (i < beats.len) { + mbi = &beats[i - 1]; + mbi1 = &beats[i]; + // case 2: beat is beyond last time/beat pair + } else /* if (i == beats.len) */ { + if (last_tempo_flag) { + return last_tempo; + } else if (i == 1) { + return ALG_DEFAULT_BPM / 60.0; + } else { + mbi = &beats[i - 2]; + mbi1 = &beats[i - 1]; + } + } + double time_dif = mbi1->time - mbi->time; + double beat_dif = mbi1->beat - mbi->beat; + return beat_dif / time_dif; +} + + bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat) { if (start_beat >= end_beat) return false; @@ -975,6 +1023,34 @@ bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat) } +bool Alg_time_map::stretch_region(double b0, double b1, double dur) +{ + // find current duration + double t0 = beat_to_time(b0); + double t1 = beat_to_time(b1); + double old_dur = t1 - t0; + if (old_dur <= 0 || dur <= 0) return false; + double scale = dur / old_dur; // larger scale => slower + // insert a beat if necessary at b0 and b1 + insert_beat(t0, b0); + insert_beat(t1, b1); + long start_x = locate_beat(b0); + long stop_x = locate_beat(b1); + double orig_time = beats[start_x].time; + double prev_time = orig_time; + for (int i = start_x + 1; i < beats.len; i++) { + double delta = beats[i].time - orig_time; + if (i <= stop_x) { // change tempo to next Alg_beat + delta *= scale; + } + orig_time = beats[i].time; + prev_time += delta; + beats[i].time = prev_time; + } + return true; +} + + void Alg_time_map::trim(double start, double end, bool units_are_seconds) { // extract the time map from start to end and shift to time zero @@ -1277,22 +1353,22 @@ void Alg_track::serialize(void **buffer, long *bytes) // // The format for a track is given within the Seq format above assert(get_type() == 't'); - ser_buf.init_for_write(); + ser_write_buf.init_for_write(); serialize_track(); - *buffer = ser_buf.to_heap(bytes); + *buffer = ser_write_buf.to_heap(bytes); } void Alg_seq::serialize(void **buffer, long *bytes) { assert(get_type() == 's'); - ser_buf.init_for_write(); + ser_write_buf.init_for_write(); serialize_seq(); - *buffer = ser_buf.to_heap(bytes); + *buffer = ser_write_buf.to_heap(bytes); } -void Serial_buffer::check_buffer(long needed) +void Serial_write_buffer::check_buffer(long needed) { if (len < (ptr - buffer) + needed) { // do we need more space? long new_len = len * 2; // exponential growth is important @@ -1301,9 +1377,11 @@ void Serial_buffer::check_buffer(long needed) // make sure new_len is as big as needed if (needed > new_len) new_len = needed; char *new_buffer = new char[new_len]; // allocate space - memcpy(new_buffer, buffer, len); // copy from old buffer ptr = new_buffer + (ptr - buffer); // relocate ptr to new buffer - delete buffer; // free old buffer + if (len > 0) { // we had a buffer already + memcpy(new_buffer, buffer, len); // copy from old buffer + delete buffer; // free old buffer + } buffer = new_buffer; // update buffer information len = new_len; } @@ -1315,37 +1393,39 @@ void Alg_seq::serialize_seq() int i; // loop counters // we can easily compute how much buffer space we need until we // get to tracks, so expand at least that much - long needed = 48 + 16 * time_map->beats.len + 24 * time_sig.length(); - ser_buf.check_buffer(needed); - ser_buf.set_char('A'); - ser_buf.set_char('L'); - ser_buf.set_char('G'); - ser_buf.set_char('S'); - long length_offset = ser_buf.get_posn(); - ser_buf.set_int32(0); // leave room to come back and write length - ser_buf.set_int32(channel_offset_per_track); - ser_buf.set_int32(units_are_seconds); - ser_buf.set_double(time_map->last_tempo); - ser_buf.set_int32(time_map->last_tempo_flag); - ser_buf.set_int32(time_map->beats.len); + long needed = 64 + 16 * time_map->beats.len + 24 * time_sig.length(); + ser_write_buf.check_buffer(needed); + ser_write_buf.set_char('A'); + ser_write_buf.set_char('L'); + ser_write_buf.set_char('G'); + ser_write_buf.set_char('S'); + long length_offset = ser_write_buf.get_posn(); + ser_write_buf.set_int32(0); // leave room to come back and write length + ser_write_buf.set_int32(channel_offset_per_track); + ser_write_buf.set_int32(units_are_seconds); + ser_write_buf.set_double(beat_dur); + ser_write_buf.set_double(real_dur); + ser_write_buf.set_double(time_map->last_tempo); + ser_write_buf.set_int32(time_map->last_tempo_flag); + ser_write_buf.set_int32(time_map->beats.len); for (i = 0; i < time_map->beats.len; i++) { - ser_buf.set_double(time_map->beats[i].time); - ser_buf.set_double(time_map->beats[i].beat); + ser_write_buf.set_double(time_map->beats[i].time); + ser_write_buf.set_double(time_map->beats[i].beat); } - ser_buf.set_int32(time_sig.length()); - ser_buf.pad(); + ser_write_buf.set_int32(time_sig.length()); + ser_write_buf.pad(); for (i = 0; i < time_sig.length(); i++) { - ser_buf.set_double(time_sig[i].beat); - ser_buf.set_double(time_sig[i].num); - ser_buf.set_double(time_sig[i].den); + ser_write_buf.set_double(time_sig[i].beat); + ser_write_buf.set_double(time_sig[i].num); + ser_write_buf.set_double(time_sig[i].den); } - ser_buf.set_int32(tracks()); - ser_buf.pad(); + ser_write_buf.set_int32(tracks()); + ser_write_buf.pad(); for (i = 0; i < tracks(); i++) { track(i)->serialize_track(); } // do not include ALGS, include padding at end - ser_buf.store_long(length_offset, ser_buf.get_posn() - length_offset); + ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset); } @@ -1353,51 +1433,51 @@ void Alg_track::serialize_track() { // to simplify the code, copy from parameter addresses to locals int j; - ser_buf.check_buffer(32); - ser_buf.set_char('A'); - ser_buf.set_char('L'); - ser_buf.set_char('G'); - ser_buf.set_char('T'); - long length_offset = ser_buf.get_posn(); // save location for track length - ser_buf.set_int32(0); // room to write track length - ser_buf.set_int32(units_are_seconds); - ser_buf.set_double(beat_dur); - ser_buf.set_double(real_dur); - ser_buf.set_int32(len); + ser_write_buf.check_buffer(32); + ser_write_buf.set_char('A'); + ser_write_buf.set_char('L'); + ser_write_buf.set_char('G'); + ser_write_buf.set_char('T'); + long length_offset = ser_write_buf.get_posn(); // save location for track length + ser_write_buf.set_int32(0); // room to write track length + ser_write_buf.set_int32(units_are_seconds); + ser_write_buf.set_double(beat_dur); + ser_write_buf.set_double(real_dur); + ser_write_buf.set_int32(len); for (j = 0; j < len; j++) { - ser_buf.check_buffer(24); + ser_write_buf.check_buffer(24); Alg_event *event = (*this)[j]; - ser_buf.set_int32(event->get_selected()); - ser_buf.set_int32(event->get_type()); - ser_buf.set_int32(event->get_identifier()); - ser_buf.set_int32(event->chan); - ser_buf.set_double(event->time); + ser_write_buf.set_int32(event->get_selected()); + ser_write_buf.set_int32(event->get_type()); + ser_write_buf.set_int32(event->get_identifier()); + ser_write_buf.set_int32(event->chan); + ser_write_buf.set_double(event->time); if (event->is_note()) { - ser_buf.check_buffer(20); + ser_write_buf.check_buffer(20); Alg_note *note = (Alg_note *) event; - ser_buf.set_float(note->pitch); - ser_buf.set_float(note->loud); - ser_buf.set_double(note->dur); - long parm_num_offset = ser_buf.get_posn(); + ser_write_buf.set_float(note->pitch); + ser_write_buf.set_float(note->loud); + ser_write_buf.set_double(note->dur); + long parm_num_offset = ser_write_buf.get_posn(); long parm_num = 0; - ser_buf.set_int32(0); // placeholder for no. parameters + ser_write_buf.set_int32(0); // placeholder for no. parameters Alg_parameters_ptr parms = note->parameters; while (parms) { serialize_parameter(&(parms->parm)); parms = parms->next; parm_num++; } - ser_buf.store_long(parm_num_offset, parm_num); + ser_write_buf.store_long(parm_num_offset, parm_num); } else { assert(event->is_update()); Alg_update *update = (Alg_update *) event; serialize_parameter(&(update->parameter)); } - ser_buf.check_buffer(7); // maximum padding possible - ser_buf.pad(); + ser_write_buf.check_buffer(7); // maximum padding possible + ser_write_buf.pad(); } // write length, not including ALGT, including padding at end - ser_buf.store_long(length_offset, ser_buf.get_posn() - length_offset); + ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset); } @@ -1406,29 +1486,29 @@ void Alg_track::serialize_parameter(Alg_parameter *parm) // add eight to account for name + zero end-of-string and the // possibility of adding 7 padding bytes long len = strlen(parm->attr_name()) + 8; - ser_buf.check_buffer(len); - ser_buf.set_string(parm->attr_name()); - ser_buf.pad(); + ser_write_buf.check_buffer(len); + ser_write_buf.set_string(parm->attr_name()); + ser_write_buf.pad(); switch (parm->attr_type()) { case 'r': - ser_buf.check_buffer(8); - ser_buf.set_double(parm->r); + ser_write_buf.check_buffer(8); + ser_write_buf.set_double(parm->r); break; case 's': - ser_buf.check_buffer(strlen(parm->s) + 1); - ser_buf.set_string(parm->s); + ser_write_buf.check_buffer(strlen(parm->s) + 1); + ser_write_buf.set_string(parm->s); break; case 'i': - ser_buf.check_buffer(4); - ser_buf.set_int32(parm->i); + ser_write_buf.check_buffer(4); + ser_write_buf.set_int32(parm->i); break; case 'l': - ser_buf.check_buffer(4); - ser_buf.set_int32(parm->l); + ser_write_buf.check_buffer(4); + ser_write_buf.set_int32(parm->l); break; case 'a': - ser_buf.check_buffer(strlen(parm->a) + 1); - ser_buf.set_string(parm->a); + ser_write_buf.check_buffer(strlen(parm->a) + 1); + ser_write_buf.set_string(parm->a); break; } } @@ -1438,55 +1518,69 @@ void Alg_track::serialize_parameter(Alg_parameter *parm) Alg_track *Alg_track::unserialize(void *buffer, long len) { assert(len > 8); - ser_buf.init_for_read(buffer, len); - bool alg = ser_buf.get_char() == 'A' && - ser_buf.get_char() == 'L' && - ser_buf.get_char() == 'G'; + ser_read_buf.init_for_read(buffer, len); + bool alg = ser_read_buf.get_char() == 'A' && + ser_read_buf.get_char() == 'L' && + ser_read_buf.get_char() == 'G'; assert(alg); - char c = ser_buf.get_char(); + char c = ser_read_buf.get_char(); if (c == 'S') { Alg_seq *seq = new Alg_seq; + ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,S seq->unserialize_seq(); return seq; } else { assert(c == 'T'); Alg_track *track = new Alg_track; + ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,T track->unserialize_track(); return track; } } +#pragma warning(disable: 4800) // long to bool performance warning + +/* Note: this Alg_seq must have a default initialized Alg_time_map. + * It will be filled in with data from the ser_read_buf buffer. + */ void Alg_seq::unserialize_seq() { - ser_buf.check_input_buffer(28); - long len = ser_buf.get_int32(); - assert(ser_buf.get_len() >= len); - channel_offset_per_track = ser_buf.get_int32(); - units_are_seconds = (bool) ser_buf.get_int32(); - time_map = new Alg_time_map(); - time_map->last_tempo = ser_buf.get_double(); - time_map->last_tempo_flag = (bool) ser_buf.get_int32(); - long beats = ser_buf.get_int32(); - ser_buf.check_input_buffer(beats * 16 + 4); + ser_read_buf.check_input_buffer(48); + bool algs = (ser_read_buf.get_char() == 'A') && + (ser_read_buf.get_char() == 'L') && + (ser_read_buf.get_char() == 'G') && + (ser_read_buf.get_char() == 'S'); + assert(algs); + long len = ser_read_buf.get_int32(); + assert(ser_read_buf.get_len() >= len); + channel_offset_per_track = ser_read_buf.get_int32(); + units_are_seconds = ser_read_buf.get_int32() != 0; + beat_dur = ser_read_buf.get_double(); + real_dur = ser_read_buf.get_double(); + // no need to allocate an Alg_time_map since it's done during initialization + time_map->last_tempo = ser_read_buf.get_double(); + time_map->last_tempo_flag = ser_read_buf.get_int32() != 0; + long beats = ser_read_buf.get_int32(); + ser_read_buf.check_input_buffer(beats * 16 + 4); int i; for (i = 0; i < beats; i++) { - double time = ser_buf.get_double(); - double beat = ser_buf.get_double(); + double time = ser_read_buf.get_double(); + double beat = ser_read_buf.get_double(); time_map->insert_beat(time, beat); // printf("time_map: %g, %g\n", time, beat); } - long time_sig_len = ser_buf.get_int32(); - ser_buf.get_pad(); - ser_buf.check_input_buffer(time_sig_len * 24 + 8); + long time_sig_len = ser_read_buf.get_int32(); + ser_read_buf.get_pad(); + ser_read_buf.check_input_buffer(time_sig_len * 24 + 8); for (i = 0; i < time_sig_len; i++) { - double beat = ser_buf.get_double(); - double num = ser_buf.get_double(); - double den = ser_buf.get_double(); + double beat = ser_read_buf.get_double(); + double num = ser_read_buf.get_double(); + double den = ser_read_buf.get_double(); time_sig.insert(beat, num, den); } - long tracks_num = ser_buf.get_int32(); - ser_buf.get_pad(); + long tracks_num = ser_read_buf.get_int32(); + ser_read_buf.get_pad(); add_track(tracks_num - 1); // create tracks_num tracks for (i = 0; i < tracks_num; i++) { track(i)->unserialize_track(); @@ -1494,40 +1588,41 @@ void Alg_seq::unserialize_seq() // assume seq started at beginning of buffer. len measures // bytes after 'ALGS' header, so add 4 bytes and compare to // current buffer position -- they should agree - assert(ser_buf.get_posn() == len + 4); + assert(ser_read_buf.get_posn() == len + 4); } void Alg_track::unserialize_track() { - ser_buf.check_input_buffer(32); - assert(ser_buf.get_char() == 'A'); - assert(ser_buf.get_char() == 'L'); - assert(ser_buf.get_char() == 'G'); - assert(ser_buf.get_char() == 'T'); - long offset = ser_buf.get_posn(); // stored length does not include 'ALGT' - long bytes = ser_buf.get_int32(); - assert(bytes <= ser_buf.get_len() - offset); - units_are_seconds = (bool) ser_buf.get_int32(); - beat_dur = ser_buf.get_double(); - real_dur = ser_buf.get_double(); - int event_count = ser_buf.get_int32(); + ser_read_buf.check_input_buffer(32); + bool algt = (ser_read_buf.get_char() == 'A') && + (ser_read_buf.get_char() == 'L') && + (ser_read_buf.get_char() == 'G') && + (ser_read_buf.get_char() == 'T'); + assert(algt); + long offset = ser_read_buf.get_posn(); // stored length does not include 'ALGT' + long bytes = ser_read_buf.get_int32(); + assert(bytes <= ser_read_buf.get_len() - offset); + units_are_seconds = (bool) ser_read_buf.get_int32(); + beat_dur = ser_read_buf.get_double(); + real_dur = ser_read_buf.get_double(); + int event_count = ser_read_buf.get_int32(); for (int i = 0; i < event_count; i++) { - ser_buf.check_input_buffer(24); - long selected = ser_buf.get_int32(); - char type = (char) ser_buf.get_int32(); - long key = ser_buf.get_int32(); - long channel = ser_buf.get_int32(); - double time = ser_buf.get_double(); + ser_read_buf.check_input_buffer(24); + long selected = ser_read_buf.get_int32(); + char type = (char) ser_read_buf.get_int32(); + long key = ser_read_buf.get_int32(); + long channel = ser_read_buf.get_int32(); + double time = ser_read_buf.get_double(); if (type == 'n') { - ser_buf.check_input_buffer(20); - float pitch = ser_buf.get_float(); - float loud = ser_buf.get_float(); - double dur = ser_buf.get_double(); + ser_read_buf.check_input_buffer(20); + float pitch = ser_read_buf.get_float(); + float loud = ser_read_buf.get_float(); + double dur = ser_read_buf.get_double(); Alg_note *note = create_note(time, channel, key, pitch, loud, dur); - note->set_selected(selected); - long param_num = ser_buf.get_int32(); + note->set_selected(selected != 0); + long param_num = ser_read_buf.get_int32(); int j; // this builds a list of parameters in the correct order // (although order shouldn't matter) @@ -1541,42 +1636,43 @@ void Alg_track::unserialize_track() } else { assert(type == 'u'); Alg_update *update = create_update(time, channel, key); - update->set_selected(selected); + update->set_selected(selected != 0); unserialize_parameter(&(update->parameter)); append(update); } - ser_buf.get_pad(); + ser_read_buf.get_pad(); } - assert(offset + bytes == ser_buf.get_posn()); + assert(offset + bytes == ser_read_buf.get_posn()); } void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) { - char *attr = ser_buf.get_string(); + Alg_attribute attr = ser_read_buf.get_string(); parm_ptr->attr = symbol_table.insert_string(attr); switch (parm_ptr->attr_type()) { case 'r': - ser_buf.check_input_buffer(8); - parm_ptr->r = ser_buf.get_double(); + ser_read_buf.check_input_buffer(8); + parm_ptr->r = ser_read_buf.get_double(); break; case 's': - parm_ptr->s = heapify(ser_buf.get_string()); + parm_ptr->s = heapify(ser_read_buf.get_string()); break; case 'i': - ser_buf.check_input_buffer(4); - parm_ptr->i = ser_buf.get_int32(); + ser_read_buf.check_input_buffer(4); + parm_ptr->i = ser_read_buf.get_int32(); break; case 'l': - ser_buf.check_input_buffer(4); - parm_ptr->l = (bool) ser_buf.get_int32(); + ser_read_buf.check_input_buffer(4); + parm_ptr->l = ser_read_buf.get_int32() != 0; break; case 'a': - parm_ptr->a = symbol_table.insert_attribute(ser_buf.get_string()); + parm_ptr->a = symbol_table.insert_attribute(ser_read_buf.get_string()); break; } } +#pragma warning(default: 4800) void Alg_track::set_time_map(Alg_time_map *map) { @@ -1741,7 +1837,7 @@ void Alg_track::paste(double t, Alg_event_list *seq) assert(get_type() == 't'); // seq can be an Alg_event_list, an Alg_track, or an Alg_seq // if it is an Alg_event_list, units_are_seconds must match - bool prev_units_are_seconds = false; + bool prev_units_are_seconds; if (seq->get_type() == 'e') { assert(seq->get_owner()->get_units_are_seconds() == units_are_seconds); } else { // make it match @@ -1887,17 +1983,19 @@ Alg_event_list *Alg_track::find(double t, double len, bool all, void Alg_time_sigs::expand() { + assert(maxlen >= len); maxlen = (maxlen + 5); // extra growth for small sizes maxlen += (maxlen >> 2); // add 25% Alg_time_sig_ptr new_time_sigs = new Alg_time_sig[maxlen]; // now do copy memcpy(new_time_sigs, time_sigs, len * sizeof(Alg_time_sig)); - if (time_sigs) delete[] time_sigs; + if (time_sigs) + delete[] time_sigs; time_sigs = new_time_sigs; } -void Alg_time_sigs::insert(double beat, double num, double den) +void Alg_time_sigs::insert(double beat, double num, double den, bool force) { // find insertion point: for (int i = 0; i < len; i++) { @@ -1917,17 +2015,17 @@ void Alg_time_sigs::insert(double beat, double num, double den) // check if redundant with implied initial 4/4 time sig: (i == 0 && num == 4 && den == 4 && within(fmod(beat, 4), 0, ALG_EPS))) { - return; // redundant inserts are ignored here + if (!force) return; // redundant inserts can be ignored here } // make room for new event if (maxlen <= len) expand(); - len++; // insert new event at i memmove(&time_sigs[i + 1], &time_sigs[i], sizeof(Alg_time_sig) * (len - i)); time_sigs[i].beat = beat; time_sigs[i].num = num; time_sigs[i].den = den; + len++; return; } } @@ -1959,55 +2057,225 @@ int Alg_time_sigs::find_beat(double beat) } -void Alg_time_sigs::cut(double start, double end) -{ - // remove time_sig's from start to start+len -- these must be - // in beats (not seconds) - // now rewrite time_sig[]: copy from i_in to i_out (more or less) - int i_in = 0; - int i_out = 0; - // first, figure out where to begin cut region - i_in = find_beat(start); - i_out = i_in; +double Alg_time_sigs::get_bar_len(double beat) +{ + int i = find_beat(beat); + double num = 4.0; + double den = 4.0; + if (i != 0) { + num = time_sigs[i - 1].num; + den = time_sigs[i - 1].den; + } + return 4 * num / den; +} + +void Alg_time_sigs::cut(double start, double end, double dur) +{ + // remove time_sig's from start to end -- these must be + // in beats (not seconds). + // The duration of the whole sequence is dur (beats). + + // If the first bar line after end comes before a time signature + // and does not fall on a bar line, insert a time signature at + // the time of the bar line to retain relative bar line positions + + int i = find_beat(end); + // i is where you would insert a new time sig at beat, + // Case 1: beat coincides with a time sig at i. Time signature + // at beat means that there is a barline at beat, so when beat + // is shifted to start, the relative barline positions are preserved + if (len > 0 && + within(end, time_sigs[i].beat, ALG_EPS)) { + // beat coincides with time signature change, so end is on a barline + /* do nothing */ ; + // Case 2: there is no time signature before end + } else if (i == 0 && (len == 0 || + time_sigs[0].beat > end)) { + // If the next time signature does not fall on a barline, + // then end must not be on a barline, so there is a partial + // measure from end to the next barline. We need + // a time signature there to preserve relative barline + // locations. It may be that the next bar after start is + // due to another time signature, in which case we do not + // need to insert anything. + double measures = end / 4.0; + double imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // start is not on a barline, maybe add one here: + double bar_loc = (int(measures) + 1) * 4.0; + if (bar_loc < dur - ALG_EPS && + (len == 0 || time_sigs[0].beat > bar_loc + ALG_EPS)) { + insert(bar_loc, 4, 4, true); // forced insert + } + } + // This case should never be true because if i == 0, either there + // are no time signatures before beat (Case 2), + // or there is one time signature at beat (Case 1) + } else if (i == 0) { + /* do nothing (might be good to assert(false)) */ ; + // Case 3: i-1 must be the effective time sig position + } else { + // get the time signature in effect at end + Alg_time_sig &tsp = time_sigs[i - 1]; + double beats_per_measure = (tsp.num * 4) / tsp.den; + double measures = (end - tsp.beat) / beats_per_measure; + int imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // end is not on a measure, so we need to insert a time sig + // to force a bar line at the first measure location after + // beat, if any + double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1); + // insert new time signature at bar_loc + // It will have the same time signature, but the position will + // force a barline to match the barline before the shift + // However, we should not insert a barline if there is a + // time signature earlier than the barline time + if (i < len /* time_sigs[i] is the last one */ && + time_sigs[i].beat < bar_loc - ALG_EPS) { + /* do not insert because there's already a time signature */; + } else if (bar_loc < dur - ALG_EPS) { + insert(bar_loc, tsp.num, tsp.den, true); // forced insert + } + } + // else beat coincides with a barline, so no need for an extra + // time signature to force barline alignment + } + + // Figure out if time signature at start matches + // the time signature at end. If not, we need to insert a + // time signature at end to force the correct time signature + // there. + // Find time signature at start: + double start_num = 4.0; // default if no time signature specified + double start_den = 4.0; + i = find_beat(start); + // A time signature at start would go at index i, so the effective + // time signature prior to start is at i - 1. If i == 0, the default + // time signature is in effect prior to start. + if (i != 0) { + start_num = time_sigs[i - 1].num; + start_den = time_sigs[i - 1].den; + } + // Find the time signature at end: + double end_num = 4.0; // default if no time signature specified + double end_den = 4.0; + int j = find_beat(end); + if (j != 0) { + end_num = time_sigs[j - 1].num; + end_den = time_sigs[j - 1].den; + } + // compare: If meter changes and there is no time signature at end, + // insert a time signature at end + if (end < dur - ALG_EPS && + (start_num != end_num || start_den != end_den) && + (j >= len || !within(time_sigs[j].beat, end, ALG_EPS))) { + insert(end, end_num, end_den, true); + } + + // Remove time signatures from start to end (not including one AT + // end, if there is one there. Be careful with ALG_EPS on that one.) + + // since we may have inserted a time signature, find position again: + int i0 = find_beat(start); + int i1 = i0; // scan to end of cut region - while (i_in < len && time_sigs[i_in].beat < end) { - i_in = i_in + 1; - } - // change time_sig at start if necessary - // there's a time_sig that was skipped if i_in > i_out. - // if that's true and the next time change is at end, we're - // ok because it will be copied, but if the next time change - // is after end, then maybe we should insert a time change - // corresponding to what's in effect at end. We can skip this - // insert if it corresponds to whatever is in effect at start - if (i_in > i_out && i_in < len && - time_sigs[i_in].beat > end + ALG_EPS && - (i_out == 0 || time_sigs[i_out - 1].num != time_sigs[i_in - 1].num || - time_sigs[i_out - 1].den != time_sigs[i_in - 1].den)) { - time_sigs[i_out] = time_sigs[i_in - 1]; - time_sigs[i_out].beat = start; + while (i1 < len && time_sigs[i1].beat < end - ALG_EPS) { + i1++; } // scan from end to len(time_sig) - while (i_in < length()) { - Alg_time_sig &ts = time_sigs[i_in]; - ts.beat = ts.beat - (end - start); - time_sigs[i_out] = ts; - i_in = i_in + 1; - i_out = i_out + 1; + while (i1 < len) { + Alg_time_sig &ts = time_sigs[i1]; + ts.beat -= (end - start); + time_sigs[i0] = ts; + i0++; + i1++; } - len = i_out; + len = i1; } void Alg_time_sigs::trim(double start, double end) { - // remove time_sig's not in [start, start+end) + // remove time_sig's not in [start, end), but retain + // barline positions relative to the notes. This means that + // if the meter (time signature) changes between start and + // end that we need to insert a time signature at start. + // Also, if trim() would cause barlines to move, we need to + // insert a time signature on a barline (timesignatures + // imply the beginning of a bar even if the previous bar + // does not have enough beats. Note that bars do not need + // to have an integer number of beats). + // // units must be in beats (not seconds) - // copy from i_in to i_out as we scan time_sig array - int i_in = 0; - int i_out = 0; + // + // Uses Alg_time_sigs::cut() to avoid writing a special case + double dur = end + 1000; + if (len > 0) { + dur = time_sigs[len - 1].beat + 1000; + } + cut(end, dur, dur); + cut(0, start, dur); + +#ifdef IGNORE_THIS_OLD_CODE // first, skip time signatures up to start - i_in = find_beat(start); + int i = find_beat(start); + // i is where you would insert a new time sig at beat, + // Case 1: beat coincides with a time sig at i. Time signature + // at beat means that there is a barline at beat, so when beat + // is shifted to 0, the relative barline positions are preserved + if (len > 0 && + within(start, time_sigs[i].beat, ALG_EPS)) { + // beat coincides with time signature change, so offset must + // be a multiple of beats + /* do nothing */ ; + // Case 2: there is no time signature before start + } else if (i == 0 && (len == 0 || + time_sigs[0].beat > start)) { + // If the next time signature does not fall on a barline, + // then start must not be on a barline, so there is a partial + // measure from start to the next barline. We need + // a time signature there to preserve relative barline + // locations. It may be that the next bar after start is + // due to another time signature, in which case we do not + // need to insert anything. + double measures = start / 4.0; + double imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // start is not on a barline, maybe add one here: + double bar_loc = (int(measures) + 1) * 4.0; + if (len == 0 || time_sigs[1].beat > bar_loc + ALG_EPS) { + insert(bar_loc, 4, 4, true); + } + } + // This case should never be true because if i == 0, either there + // are no time signatures before beat (Case 2), + // or there is one time signature at beat (Case 1) + } else if (i == 0) { + /* do nothing (might be good to assert(false)) */ ; + // Case 3: i-1 must be the effective time sig position + } else { + i -= 1; // index the time signature in effect at start + Alg_time_sig &tsp = time_sigs[i]; + double beats_per_measure = (tsp.num * 4) / tsp.den; + double measures = (start - tsp.beat) / beats_per_measure; + int imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // beat is not on a measure, so we need to insert a time sig + // to force a bar line at the first measure location after + // beat, if any + double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1); + // insert new time signature at bar_loc + // It will have the same time signature, but the position will + // force a barline to match the barline before the shift + insert(bar_loc, tsp.num, tsp.den, true); + } + // else beat coincides with a barline, so no need for an extra + // time signature to force barline alignment + } + // since we may have inserted a time signature, find position again: + int i_in = find_beat(start); + int i_out = 0; + // put time_sig at start if necessary // if 0 < i_in < len, then the time sig at i_in is either // at start or after start. @@ -2034,7 +2302,7 @@ void Alg_time_sigs::trim(double start, double end) time_sigs[0].beat = 0.0; i_out = 1; } - // scan to end of cut region + // copy from i_in to i_out as we scan time_sig array to end of cut region while (i_in < len && time_sigs[i_in].beat < end - ALG_EPS) { Alg_time_sig &ts = time_sigs[i_in]; ts.beat = ts.beat - start; @@ -2043,6 +2311,7 @@ void Alg_time_sigs::trim(double start, double end) i_out++; } len = i_out; +#endif } @@ -2052,7 +2321,7 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) // show(); Alg_time_sigs &from = seq->time_sig; // printf("time_sig::insert from\n"); - from.show(); + // from.show(); // insert time signatures from seq into this time_sigs at start if (len == 0 && from.len == 0) { return; // default applies @@ -2061,6 +2330,13 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) // remember the time signature at the splice point double num_after_splice = 4; double den_after_splice = 4; // default + double num_before_splice = 4; + double den_before_splice = 4; // default + // this is computed for use in aligning beats after the inserted + // time signatures and duration. It is the position of time signature + // in effect immediately after start (the time signature will be + // before start or at start) + double beat_after_splice = 0.0; // three cases: // 1) time sig at splice is at i-1 // for this, we must have len>0 & i>0 @@ -2073,13 +2349,23 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) if (len > 0 && i > 0 && ((i < len && time_sigs[i].beat > start + ALG_EPS) || (i == len))) { + // no time_signature at i num_after_splice = time_sigs[i-1].num; den_after_splice = time_sigs[i-1].den; + beat_after_splice = time_sigs[i - 1].beat; + num_before_splice = num_after_splice; + den_before_splice = den_after_splice; } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { + // time_signature at i is at "start" beats num_after_splice = time_sigs[i].num; den_after_splice = time_sigs[i].den; + beat_after_splice = start; + if (i > 0) { // time signature before start is at i - 1 + num_before_splice = time_sigs[i-1].num; + den_before_splice = time_sigs[i-1].den; + } } - // i is where insert will go, time_sig[i].beat > start + // i is where insert will go, time_sig[i].beat >= start // begin by adding duration to time_sig's at i and above // move time signatures forward by duration of seq double dur = seq->get_beat_dur(); @@ -2089,46 +2375,189 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) } //printf("time_sig::insert after making space\n"); //show(); - // now insert initial time_signature at start. This may create + // If time signature of "from" is not the effective time signature + // at start, insert a time_signature at start. This may create // an extra measure if seq does not begin on a measure boundary - insert(start, 4, 4); // in case seq uses default starting signature + double num_of_insert = 4.0; + double den_of_insert = 4.0; + double beat_of_insert = 0.0; + int first_from_index = 0; // where to start copying from + if (from.length() > 0 && from[0].beat < ALG_EPS) { + // there is an initial time signature in "from" + num_of_insert = from[0].num; + den_of_insert = from[0].den; + // since we are handling the first time signature in from, + // we can start copying at index == 1: + first_from_index = 1; + } + // compare time signatures to see if we need a change at start: + if (num_before_splice != num_of_insert || + den_before_splice != den_of_insert) { + // note that this will overwrite an existing time signature if + // it is within ALG_EPS of start -- this is correct because the + // existing time signature will already be recorded as + // num_after_splice and den_after_splice + insert(start, num_of_insert, den_of_insert); + } //printf("time_sig::insert after 4/4 at start\n"); //show(); // insert time signatures from seq offset by start - for (i = 0; i < from.length(); i++) { - insert(start + from[i].beat, from[i].num, from[i].den); + for (i = 0; i < from.length() && from[i].beat < dur - ALG_EPS; i++) { + num_of_insert = from[i].num; // keep latest time signature info + den_of_insert = from[i].den; + beat_of_insert = from[i].beat; + insert(start + beat_of_insert, num_of_insert, den_of_insert); } //printf("time_sig::insert after pasting in sigs\n"); //show(); - // now insert time signature at end of splice - insert(start + dur, num_after_splice, den_after_splice); + // now insert time signature at end of splice if necessary + // if the time signature changes, we need to insert a time signature + // immediately: + if (num_of_insert != num_after_splice && + den_of_insert != den_after_splice) { + insert(start + dur, num_after_splice, den_after_splice); + num_of_insert = num_after_splice; + den_of_insert = den_after_splice; + beat_of_insert = start + dur; + } + // if the insert had a partial number of measures, we might need an + // additional time signature to realign the barlines after the insert + // To decide, we compare the beat of the first barline on or after + // start before the splice to the beat of the first barline on or + // after start + dur after the splice. In a sense, this is the "same" + // barline, so it should be shifted exactly by dur. + // First, compute the beat of the first barline on or after start: + double beats_per_measure = (num_after_splice * 4) / den_after_splice; + double measures = (start - beat_after_splice) / beats_per_measure; + // Measures might be slightly negative due to rounding. Use max() + // to eliminate any negative rounding error: + int imeasures = int(max(measures, 0.0)); + double old_bar_loc = beat_after_splice + (imeasures * beats_per_measure); + if (old_bar_loc < start) old_bar_loc += beats_per_measure; + // now old_bar_loc is the original first bar position after start + // Do similar calculation for position after end after the insertion: + // beats_per_measure already calculated because signatures match + measures = (start + dur - beat_of_insert) / beats_per_measure; + imeasures = int(max(measures, 0.0)); + double new_bar_loc = beat_of_insert + (imeasures * beats_per_measure); + if (new_bar_loc < start + dur) new_bar_loc += beats_per_measure; + // old_bar_loc should be shifted by dur: + old_bar_loc += dur; + // now the two bar locations should be equal, but due to rounding, + // they could be off by one measure + double diff = (new_bar_loc - old_bar_loc) + beats_per_measure; + double diff_in_measures = diff / beats_per_measure; + // if diff_in_measures is not (approximately) integer, we need to + // force a barline (time signature) after start + dur to maintain + // the relationship between barliness and notes + if (!within(diff_in_measures, ROUND(diff_in_measures), ALG_EPS)) { + // recall that old_bar_loc is shifted by dur + insert(old_bar_loc, num_after_splice, den_after_splice); + } //printf("time_sig::insert after sig at end of splice\n"); //show(); } -void Alg_time_sigs::insert_beats(double beat, double len) +void Alg_time_sigs::insert_beats(double start, double dur) { - int i; - // find the time_sig entry in effect at t - for (i = 0; i < len; i++) { - if (time_sigs[i].beat < beat + ALG_EPS) { - break; + int i = find_beat(start); + + // time_sigs[i] is after beat and needs to shift + // Compute the time of the first bar at or after beat so that + // a bar can be placed at bar_loc + dur + double tsnum = 4.0; + double tsden = 4.0; + double tsbeat = 0.0; // defaults + + // three cases: + // 1) time sig at splice is at i-1 + // for this, we must have len>0 & i>0 + // two sub-cases: + // A) i < len && time_sig[i].beat > start + // B) i == len + // 2) time_sig at splice is at i + // for this, i < len && time_sig[i].beat ~= start + // 3) time_sig at splice is default 4/4 + if (len > 0 && i > 0 && + ((i < len && time_sigs[i].beat > start + ALG_EPS) || + (i == len))) { + // no time_signature at i + tsnum = time_sigs[i-1].num; + tsden = time_sigs[i-1].den; + tsbeat = time_sigs[i-1].beat; + } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { + // time_signature at i is at "start" beats + tsnum = time_sigs[i].num; + tsden = time_sigs[i].den; + tsbeat = start; + i++; // we want i to be index of next time signature after start + } + // invariant: i is index of next time signature after start + + // increase beat times from i to len - 1 by dur + for (int j = i; j < len; j++) { + time_sigs[j].beat += dur; + } + + // insert a time signature to maintain bar positions if necessary + double beats_per_measure = (tsnum * 4) / tsden; + double measures = dur / beats_per_measure; // shift distance + int imeasures = ROUND(measures); + if (!within(measures, imeasures, ALG_EPS)) { + // shift is not a whole number of measures, so we may need to insert + // time signature after silence + // compute measures from time signature to next bar after time + measures = (start - tsbeat) / beats_per_measure; + // round up and add to tsbeat to get time of next bar + double bar_loc = tsbeat + beats_per_measure * (int(measures) + 1); + // translate bar_loc by len: + bar_loc += dur; // this is where we want a bar to be, but maybe + // there is a time signature change before bar, in which case we + // should not insert a new time signature + // The next time signature after start is at i if i < len + if (i < len && time_sigs[i].beat < bar_loc) { + /* do not insert */; + } else { + insert(bar_loc, tsnum, tsden); } } - // now, increase beat times by len - for (; i < len; i++) { - time_sigs[i].beat += len; +} + + +double Alg_time_sigs::nearest_beat(double beat) +{ + int i = find_beat(beat); + // i is where we would insert time signature at beat + // case 1: there is no time signature + if (i == 0 && len == 0) { + return ROUND(beat); + // case 2: beat falls approximately on time signature + } else if (i < len && within(time_sigs[i].beat, beat, ALG_EPS)) { + return time_sigs[i].beat; + // case 3: beat is after no time signature and before one + } else if (i == 0) { + double trial_beat = ROUND(beat); + // it is possible that we rounded up past a time signature + if (trial_beat > time_sigs[0].beat - ALG_EPS) { + return time_sigs[0].beat; + } + return trial_beat; } + // case 4: beat is after some time signature + double trial_beat = time_sigs[i - 1].beat + + ROUND(beat - time_sigs[i - 1].beat); + // rounding may advance trial_beat past next time signature: + if (i < len && trial_beat > time_sigs[i].beat - ALG_EPS) { + return time_sigs[i].beat; + } + return trial_beat; } Alg_tracks::~Alg_tracks() { - // Alg_events objects (track data) are not deleted, only the array - if (tracks) { - delete[] tracks; - } + reset(); } @@ -2193,16 +2622,165 @@ void Alg_tracks::reset() // all track events are incorporated into the seq, // so all we need to delete are the arrays of pointers for (int i = 0; i < len; i++) { + // printf("deleting track at %d (%x, this %x) = %x\n", i, &(tracks[i]), + // this, tracks[i]); delete tracks[i]; } if (tracks) delete [] tracks; tracks = NULL; len = 0; - maxlen = 0; // Modified by Ning Hu Nov.19 2002 + maxlen = 0; +} + + +void Alg_tracks::set_in_use(bool flag) +{ + for (int i = 0; i < len; i++) { + tracks[i]->in_use = flag; + } } -Alg_seq::Alg_seq(const char *filename, bool smf) +void Alg_iterator::expand_to(int new_max) +{ + maxlen = new_max; + Alg_pending_event_ptr new_pending_events = new Alg_pending_event[maxlen]; + // now do copy + memcpy(new_pending_events, pending_events, + len * sizeof(Alg_pending_event)); + if (pending_events) { + delete[] pending_events; + } + pending_events = new_pending_events; +} + + +void Alg_iterator::expand() +{ + maxlen = (maxlen + 5); // extra growth for small sizes + maxlen += (maxlen >> 2); // add 25% + expand_to(maxlen); +} + + +Alg_iterator::~Alg_iterator() +{ + if (pending_events) { + delete[] pending_events; + } +} + + +/* in the heap, the children of N are (N+1)*2 and (N+1)*2-1, so + * the parent of N is (N+1)/2-1. This would be easier if arrays + * were 1-based instead of 0-based + */ +#define HEAP_PARENT(loc) ((((loc) + 1) / 2) - 1) +#define FIRST_CHILD(loc) (((loc) * 2) + 1) + +void Alg_iterator::show() +{ + for (int i = 0; i < len; i++) { + Alg_pending_event_ptr p = &(pending_events[i]); + printf(" %d: %p[%ld]@%g on %d\n", i, p->events, p->index, + p->offset, p->note_on); + } +} + + +bool Alg_iterator::earlier(int i, int j) +// see if event i is earlier than event j +{ + // note-offs are scheduled ALG_EPS early so that if a note-off is + // followed immediately with the same timestamp by a note-on (common + // in MIDI files), the note-off will be scheduled first + + double t_i = pending_events[i].time; + double t_j = pending_events[j].time; + + if (t_i < t_j) return true; + // not sure if this case really exists or this is the best rule, but + // we want to give precedence to note-off events + else if (t_i == t_j && pending_events[j].note_on) return true; + return false; +} + + +void Alg_iterator::insert(Alg_events_ptr events, long index, + bool note_on, void *cookie, double offset) +{ + if (len == maxlen) expand(); + pending_events[len].events = events; + pending_events[len].index = index; + pending_events[len].note_on = note_on; + pending_events[len].cookie = cookie; + pending_events[len].offset = offset; + Alg_event_ptr event = (*events)[index]; + pending_events[len].time = (note_on ? event->time : + event->get_end_time() - ALG_EPS) + offset; + /* BEGIN DEBUG * + printf("insert %p=%p[%d] @ %g\n", event, events, index, + pending_events[len].time); + printf(" is_note %d note_on %d time %g dur %g end_time %g offset %g\n", + event->is_note(), note_on, event->time, event->get_duration(), + event->get_end_time(), offset); + } + * END DEBUG */ + int loc = len; + int loc_parent = HEAP_PARENT(loc); + len++; + // sift up: + while (loc > 0 && + earlier(loc, loc_parent)) { + // swap loc with loc_parent + Alg_pending_event temp = pending_events[loc]; + pending_events[loc] = pending_events[loc_parent]; + pending_events[loc_parent] = temp; + loc = loc_parent; + loc_parent = HEAP_PARENT(loc); + } +} + + +bool Alg_iterator::remove_next(Alg_events_ptr &events, long &index, + bool ¬e_on, void *&cookie, + double &offset, double &time) +{ + if (len == 0) return false; // empty! + events = pending_events[0].events; + index = pending_events[0].index; + note_on = pending_events[0].note_on; + offset = pending_events[0].offset; + cookie = pending_events[0].cookie; + offset = pending_events[0].offset; + time = pending_events[0].time; + len--; + pending_events[0] = pending_events[len]; + // sift down + long loc = 0; + long loc_child = FIRST_CHILD(loc); + while (loc_child < len) { + if (loc_child + 1 < len) { + if (earlier(loc_child + 1, loc_child)) { + loc_child++; + } + } + if (earlier(loc_child, loc)) { + Alg_pending_event temp = pending_events[loc]; + pending_events[loc] = pending_events[loc_child]; + pending_events[loc_child] = temp; + loc = loc_child; + loc_child = FIRST_CHILD(loc); + } else { + loc_child = len; + } + } + // printf("After remove:"); show(); + return true; +} + + +Alg_seq::Alg_seq(const char *filename, bool smf, double *offset_ptr) { basic_initialization(); ifstream inf(filename, smf ? ios::binary | ios::in : ios::in); @@ -2212,20 +2790,22 @@ Alg_seq::Alg_seq(const char *filename, bool smf) } if (smf) { error = alg_smf_read(inf, this); + if (offset_ptr) *offset_ptr = 0.0; } else { - error = alg_read(inf, this); + error = alg_read(inf, this, offset_ptr); } inf.close(); } -Alg_seq::Alg_seq(istream &file, bool smf) +Alg_seq::Alg_seq(istream &file, bool smf, double *offset_ptr) { basic_initialization(); if (smf) { error = alg_smf_read(file, this); + if (offset_ptr) *offset_ptr = 0.0; } else { - error = alg_read(file, this); + error = alg_read(file, this, offset_ptr); } } @@ -2284,8 +2864,8 @@ Alg_track_ptr Alg_seq::track(int i) return &(track_list[i]); } +#pragma warning(disable: 4715) // ok not to return a value here -#pragma GCC diagnostic ignored "-Wreturn-type" // ok not to return a value here Alg_event_ptr &Alg_seq::operator[](int i) { int ntracks = track_list.length(); @@ -2299,8 +2879,9 @@ Alg_event_ptr &Alg_seq::operator[](int i) } tr++; } - assert(false); // out of bounds + assert(false); // out of bounds } +#pragma warning(default: 4715) void Alg_seq::convert_to_beats() @@ -2369,11 +2950,12 @@ Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) // return sequence from start to start+len and modify this // sequence by removing that time-span { + double dur = get_dur(); // fix parameters to fall within existing sequence - if (start > get_dur()) return NULL; // nothing to cut + if (start > dur) return NULL; // nothing to cut if (start < 0) start = 0; // can't start before sequence starts - if (start + len > get_dur()) // can't cut after end: - len = get_dur() - start; + if (start + len > dur) // can't cut after end: + len = dur - start; Alg_seq_ptr result = new Alg_seq(); Alg_time_map_ptr map = new Alg_time_map(get_time_map()); @@ -2399,11 +2981,13 @@ Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) // we use len. double ts_start = start; double ts_end = start + len; + double ts_dur = dur; double ts_last_note_off = start + result->last_note_off; if (units_are_seconds) { ts_start = time_map->time_to_beat(ts_start); ts_end = time_map->time_to_beat(ts_end); ts_last_note_off = time_map->time_to_beat(ts_last_note_off); + ts_dur = time_map->time_to_beat(ts_dur); } // result is shifted from start to 0 and has length len, but // time_sig and time_map are copies from this. Adjust time_sig, @@ -2425,9 +3009,9 @@ Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) // we sliced out a portion of each track, so now we need to // slice out the corresponding sections of time_sig and time_map // as well as to adjust the duration. - time_sig.cut(ts_start, ts_end); + time_sig.cut(ts_start, ts_end, ts_dur); time_map->cut(start, len, units_are_seconds); - set_dur(get_dur() - len); + set_dur(dur - len); return result; } @@ -2456,9 +3040,11 @@ void Alg_seq::insert_silence(double t, double len) } else { time_map->insert_beats(t_beats, len_beats); } - if (time_sig.length() > 0) { - time_sig.insert_beats(t_beats, len_beats); - } + time_sig.insert_beats(t_beats, len_beats); + // Final duration is defined to be t + len + whatever was + // in the sequence after t (if any). This translates to + // t + len + max(dur - t, 0) + set_dur(t + len + max(get_dur() - t, 0.0)); } @@ -2516,9 +3102,9 @@ Alg_seq *Alg_seq::copy(double start, double len, bool all) void Alg_seq::paste(double start, Alg_seq *seq) { - // insert seq at time; open up space for it - // to manipulate time map, we need units as beats - // save original form so we can convert back if necessary + // Insert seq at time, opening up space for it. + // To manipulate time map, we need units as beats. + // Save original form so we can convert back if necessary. bool units_should_be_seconds = units_are_seconds; bool seq_units_should_be_seconds = seq->get_units_are_seconds(); if (units_are_seconds) { @@ -2595,10 +3181,11 @@ void Alg_seq::clear_track(int track_num, double start, double len, bool all) void Alg_seq::clear(double start, double len, bool all) { // Fix parameters to fall within existing sequence - if (start > get_dur()) return; // nothing to cut + double dur = get_dur(); + if (start > dur) return; // nothing to cut if (start < 0) start = 0; // can't start before sequence starts - if (start + len > get_dur()) // can't cut after end: - len = get_dur() - start; + if (start + len > dur) // can't cut after end: + len = dur - start; for (int i = 0; i < tracks(); i++) clear_track(i, start, len, all); @@ -2606,17 +3193,19 @@ void Alg_seq::clear(double start, double len, bool all) // Put units in beats to match time_sig's. double ts_start = start; double ts_end = start + len; + double ts_dur = dur; if (units_are_seconds) { ts_start = time_map->time_to_beat(ts_start); ts_end = time_map->time_to_beat(ts_end); + ts_dur = time_map->time_to_beat(ts_dur); } // we sliced out a portion of each track, so now we need to // slice out the corresponding sections of time_sig and time_map // as well as to adjust the duration. - time_sig.cut(ts_start, ts_end); + time_sig.cut(ts_start, ts_end, ts_dur); time_map->cut(start, len, units_are_seconds); - set_dur(get_dur() - len); + set_dur(dur - len); } @@ -2674,6 +3263,26 @@ bool Alg_seq::insert_beat(double time, double beat) } +// input is time, return value is time +double Alg_seq::nearest_beat_time(double time, double *beat) +{ + double b = time_map->time_to_beat(time); + b = time_sig.nearest_beat(b); + if (beat) *beat = b; + return time_map->beat_to_time(b); +} + + +bool Alg_seq::stretch_region(double b0, double b1, double dur) +{ + bool units_should_be_seconds = units_are_seconds; + convert_to_beats(); + bool result = time_map->stretch_region(b0, b1, dur); + if (units_should_be_seconds) convert_to_seconds(); + return result; +} + + bool Alg_seq::insert_tempo(double bpm, double beat) { double bps = bpm / 60.0; // convert to beats per second @@ -2724,6 +3333,12 @@ void Alg_seq::add_event(Alg_event_ptr event, int track_num) } +double Alg_seq::get_tempo(double beat) +{ + return time_map->get_tempo(beat); +} + + bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat) // set tempo from start_beat to end_beat { @@ -2731,12 +3346,21 @@ bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat) if (start_beat >= end_beat) return false; bool units_should_be_seconds = units_are_seconds; convert_to_beats(); + double dur = get_dur(); bool result = time_map->set_tempo(bpm, start_beat, end_beat); + // preserve sequence duration in beats when tempo changes + set_dur(dur); if (units_should_be_seconds) convert_to_seconds(); return result; } +double Alg_seq::get_bar_len(double beat) +{ + return time_sig.get_bar_len(beat); +} + + void Alg_seq::set_time_sig(double beat, double num, double den) { time_sig.insert(beat, num, den); @@ -2799,43 +3423,61 @@ void Alg_seq::set_events(Alg_event_ptr *events, long len, long max) */ -void Alg_seq::iteration_begin() +void Alg_iterator::begin_seq(Alg_seq_ptr s, void *cookie, double offset) { // keep an array of indexes into tracks - current = new long[track_list.length()]; + // printf("new pending\n"); int i; - for (i = 0; i < track_list.length(); i++) { - current[i] = 0; + for (i = 0; i < s->track_list.length(); i++) { + if (s->track_list[i].length() > 0) { + insert(&(s->track_list[i]), 0, true, cookie, offset); + } } } -Alg_event_ptr Alg_seq::iteration_next() +Alg_event_ptr Alg_iterator::next(bool *note_on, void **cookie_ptr, + double *offset_ptr, double end_time) // return the next event in time from any track { - long cur; // a track index - // find lowest next time of any track: - double next = 1000000.0; - int i, track = 0; - for (i = 0; i < track_list.length(); i++) { - Alg_track &tr = track_list[i]; - cur = current[i]; - if (cur < tr.length() && tr[cur]->time < next) { - next = tr[cur]->time; - track = i; - } - } - if (next < 1000000.0) { - return track_list[track][current[track]++]; - } else { + bool on; + double when; + if (!remove_next(events_ptr, index, on, cookie, offset, when)) { return NULL; } + if (note_on) *note_on = on; + Alg_event_ptr event = (*events_ptr)[index]; + if (on) { + if (note_off_flag && event->is_note() && + (end_time == 0 || + (*events_ptr)[index]->get_end_time() + offset < end_time)) { + // this was a note-on, so insert pending note-off + insert(events_ptr, index, false, cookie, offset); + } + // for both note-ons and updates, insert next event (at index + 1) + // DO NOT INCREMENT index: it must be preserved for request_note_off() + if (index + 1 < events_ptr->length() && + (end_time == 0 || // zero means ignore end time + // stop iterating when end time is reached + (*events_ptr)[index + 1]->time + offset < end_time)) { + insert(events_ptr, index + 1, true, cookie, offset); + } + } + if (cookie_ptr) *cookie_ptr = cookie; + if (offset_ptr) *offset_ptr = offset; + return event; } -void Alg_seq::iteration_end() +void Alg_iterator::request_note_off() +{ + assert(index >= 0 && index < events_ptr->length()); + insert(events_ptr, index, false, cookie, offset); +} + + +void Alg_iterator::end() { - delete[] current; } @@ -2848,17 +3490,25 @@ void Alg_seq::merge_tracks() } // preallocate array for efficiency: Alg_event_ptr *notes = new Alg_event_ptr[sum]; - iteration_begin(); + Alg_iterator iterator(this, false); + iterator.begin(); long notes_index = 0; Alg_event_ptr event; - while (( event = iteration_next() )) { + while ((event = iterator.next())) { notes[notes_index++] = event; } track_list.reset(); // don't need them any more add_track(0); track(0)->set_events(notes, sum, sum); - iteration_end(); + iterator.end(); +} + + +void Alg_seq::set_in_use(bool flag) +{ + Alg_track::set_in_use(flag); + track_list.set_in_use(flag); } diff --git a/plugins/MidiImport/portsmf/allegro.h b/plugins/MidiImport/portsmf/allegro.h index e83d4b46375..7c99eb7a730 100644 --- a/plugins/MidiImport/portsmf/allegro.h +++ b/plugins/MidiImport/portsmf/allegro.h @@ -46,11 +46,11 @@ // Just as serialization uses ser_buf for output, unserialization uses // unser_buf for reading. unser_buf is another static member of Alg_track. -#ifndef __ALLEGRO__ -#define __ALLEGRO__ -#include "debug.h" - -#include "lmmsconfig.h" +#ifndef ALLEGRO_H +#define ALLEGRO_H +#include +#include +#include #define ALG_EPS 0.000001 // epsilon #define ALG_DEFAULT_BPM 100.0 // default tempo @@ -67,7 +67,7 @@ char *heapify(const char *s); // put a string on the heap // the attribute 'tempor' (a real) is stored // as 'rtempor'. To get the string name, just // use attribute+1. -typedef char *Alg_attribute; +typedef const char *Alg_attribute; #define alg_attr_name(a) ((a) + 1) #define alg_attr_type(a) (*(a)) @@ -79,6 +79,19 @@ class Alg_atoms { maxlen = len = 0; atoms = NULL; } + // Note: the code is possibly more correct and faster without the + // following destructor, which will only run after the program takes + // a normal exit. Cleaning up after the program exit slows down the exit, + // and will cause problems if any other destructor tries to reference an + // Alg_atom (which will now be freed). The advantage of this code is + // that Alg_atoms will not be reported as memory leaks by automation + // that doesn't know better. -RBD + virtual ~Alg_atoms() { + for (int i = 0; i < len; i++) { + delete atoms[i]; + } + if (atoms) delete [] atoms; + } // insert/lookup an atttribute Alg_attribute insert_attribute(Alg_attribute attr); // insert/lookup attribute by name (without prefixed type) @@ -86,7 +99,7 @@ class Alg_atoms { private: long maxlen; long len; - char **atoms; + Alg_attribute *atoms; // insert an Attriubute not in table after moving attr to heap Alg_attribute insert_new(const char *name, char attr_type); @@ -101,18 +114,24 @@ extern Alg_atoms symbol_table; // Alg_parameter class typedef class Alg_parameter { public: - ~Alg_parameter(); + // This constructor guarantees that an Alg_parameter can be + // deleted safely without further initialization. It does not + // do anything useful, so it is expected that the creator will + // set attr and store a value in the appropriate union field. Alg_attribute attr; union { double r;// real - char *s; // string + const char *s; // string long i; // integer bool l; // logical - char *a; // symbol (atom) + const char *a; // symbol (atom) }; // anonymous union + + Alg_parameter() { attr = "i"; } + ~Alg_parameter(); void copy(Alg_parameter *); // copy from another parameter - char attr_type() { return alg_attr_type(attr); } - char *attr_name() { return alg_attr_name(attr); } + const char attr_type() { return alg_attr_type(attr); } + const char *attr_name() { return alg_attr_name(attr); } void set_attr(Alg_attribute a) { attr = a; } void show(); } *Alg_parameter_ptr; @@ -139,15 +158,17 @@ typedef class Alg_parameters { // attribute. If you have already done the symbol table lookup/insert // you can do these operations faster (in which case we should add // another set of functions that take attributes as arguments.) - static void insert_real(Alg_parameters **list, char *name, double r); + static void insert_real(Alg_parameters **list, const char *name, double r); // insert string will copy string to heap - static void insert_string(Alg_parameters **list, char *name, char *s); - static void insert_integer(Alg_parameters **list, char *name, long i); - static void insert_logical(Alg_parameters **list, char *name, bool l); - static void insert_atom(Alg_parameters **list, char *name, char *s); + static void insert_string(Alg_parameters **list, const char *name, + const char *s); + static void insert_integer(Alg_parameters **list, const char *name, long i); + static void insert_logical(Alg_parameters **list, const char *name, bool l); + static void insert_atom(Alg_parameters **list, const char *name, + const char *s); static Alg_parameters *remove_key(Alg_parameters **list, const char *name); // find an attribute/value pair - Alg_parameter_ptr find(Alg_attribute *attr); + Alg_parameter_ptr find(Alg_attribute attr); } *Alg_parameters_ptr; @@ -198,11 +219,11 @@ typedef class Alg_event { // attribute (first argument) must agree in type with the second arg. // The last letter of the attribute implies the type (see below). void set_parameter(Alg_parameter_ptr new_parameter); - void set_string_value(char *attr, char *value); - void set_real_value(char *attr, double value); - void set_logical_value(char *attr, bool value); - void set_integer_value(char *attr, long value); - void set_atom_value(char *attr, char *atom); + void set_string_value(const char *attr, const char *value); + void set_real_value(const char *attr, double value); + void set_logical_value(const char *attr, bool value); + void set_integer_value(const char *attr, long value); + void set_atom_value(const char *attr, const char *atom); // Some note methods. These fail (via assert()) if this is not a note: // @@ -222,17 +243,22 @@ typedef class Alg_event { // types. Attribute names end with a type designation: 's', 'r', 'l', // 'i', or 'a'. // - bool has_attribute(char *attr); // test if note has attribute/value pair - char get_attribute_type(char *attr); // get the associated type: + bool has_attribute(const char *attr); // test if note has attribute/value pair + char get_attribute_type(const char *attr); // get the associated type: // 's' = string, // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(char *attr, char *value = NULL); // get the string value - double get_real_value(char *attr, double value = 0.0); // get the real value - bool get_logical_value(char *attr, bool value = false); // get the logical value - long get_integer_value(char *attr, long value = 0); // get the integer value - char *get_atom_value(char *attr, char *value = NULL); // get the atom value - void delete_attribute(char *attr); // delete an attribute/value pair + // get the string value + const char *get_string_value(const char *attr, const char *value = NULL); + // get the real value + double get_real_value(const char *attr, double value = 0.0); + // get the logical value + bool get_logical_value(const char *attr, bool value = false); + // get the integer value + long get_integer_value(const char *attr, long value = 0); + // get the atom value + const char *get_atom_value(const char *attr, const char *value = NULL); + void delete_attribute(const char *attr); // delete an attribute/value pair // (ignore if no matching attribute/value pair exists) // Some attribute/value methods. These fail if this is not an update. @@ -243,13 +269,13 @@ typedef class Alg_event { char get_update_type(); // get the update's type: 's' = string, // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(); // get the update's string value + const char *get_string_value(); // get the update's string value // Notes: Caller does not own the return value. Do not modify. // Do not use after underlying Alg_seq is modified. double get_real_value(); // get the update's real value bool get_logical_value(); // get the update's logical value long get_integer_value(); // get the update's integer value - char *get_atom_value(); // get the update's atom value + const char *get_atom_value(); // get the update's atom value // Notes: Caller does not own the return value. Do not modify. // The return value's lifetime is forever. @@ -305,6 +331,9 @@ typedef class Alg_events { // creating a new track and adding notes to it. It is *not* // updated after uninsert(), so use it with care. double last_note_off; + // initially false, in_use can be used to mark "do not delete". If an + // Alg_events instance is deleted while "in_use", an assertion will fail. + bool in_use; virtual int length() { return len; } Alg_event_ptr &operator[](int i) { assert(i >= 0 && i < len); @@ -314,10 +343,11 @@ typedef class Alg_events { maxlen = len = 0; events = NULL; last_note_off = 0; + in_use = false; } // destructor deletes the events array, but not the // events themselves - ~Alg_events(); + virtual ~Alg_events(); void set_events(Alg_event_ptr *e, long l, long m) { if (events) delete [] events; events = e; len = l; maxlen = m; } @@ -468,8 +498,11 @@ typedef class Alg_time_map { // you want tracks to be in beat units. void insert_beat(double time, double beat); // add a point to the map bool insert_tempo(double tempo, double beat); // insert a tempo change + // get the tempo starting at beat + double get_tempo(double beat); // set the tempo over a region bool set_tempo(double tempo, double start_beat, double end_beat); + bool stretch_region(double b0, double b1, double dur); void cut(double start, double len, bool units_are_seconds); void trim(double start, double end, bool units_are_seconds); void paste(double start, Alg_track *tr); @@ -488,20 +521,76 @@ typedef class Alg_time_map { } *Alg_time_map_ptr; -typedef class Serial_buffer { -private: +// Serial_buffer is an abstract class with common elements of +// Serial_read_buffer and Serial_write_buffer +class Serial_buffer { + protected: char *buffer; char *ptr; long len; -public: + public: Serial_buffer() { buffer = NULL; ptr = NULL; len = 0; } - void init_for_write() { ptr = buffer; } + virtual ~Serial_buffer() { } + long get_posn() { return (long) (ptr - buffer); } long get_len() { return len; } +}; + + +typedef class Serial_read_buffer : public Serial_buffer { +public: + // note that a Serial_read_buffer is initialized for reading by + // setting buffer, but it is not the Serial_read_buffer's responsibility + // to delete the buffer (owner might want to reuse it), so the destructor + // does nothing. + virtual ~Serial_read_buffer() { } +#if defined(_WIN32) +#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits +#pragma warning(disable: 4311) // type cast pointer to long warning +#endif + void get_pad() { while (((long) ptr) & 7) ptr++; } +#if defined(_WIN32) +#pragma warning(default: 4311 546) +#endif + // Prepare to read n bytes from buf. The caller must manage buf: it is + // valid until reading is finished, and it is caller's responsibility + // to free buf when it is no longer needed. + void init_for_read(void *buf, long n) { + buffer = (char *) buf; + ptr = (char *) buf; + len = n; + } + char get_char() { return *ptr++; } + void unget_chars(int n) { ptr -= n; } // undo n get_char() calls + long get_int32() { long i = *((long *) ptr); ptr += 4; return i; } + float get_float() { float f = *((float *) ptr); ptr += 4; return f; } + double get_double() { double d = *((double *) ptr); ptr += sizeof(double); + return d; } + const char *get_string() { char *s = ptr; char *fence = buffer + len; + assert(ptr < fence); + while (*ptr++) assert(ptr < fence); + get_pad(); + return s; } + void check_input_buffer(long needed) { + assert(get_posn() + needed <= len); } +} *Serial_read_buffer_ptr; + + +typedef class Serial_write_buffer: public Serial_buffer { + public: + // Note: allegro.cpp declares one static instance of Serial_buffer to + // reduce large memory (re)allocations when serializing tracks for UNDO. + // This destructor will only run when the program exits, which will only + // add overhead to the exit process, but it will eliminate an incorrect + // report of memory leakage from automation that doesn't know better. -RBD + virtual ~Serial_write_buffer() { + if (buffer) delete [] buffer; + } + void init_for_write() { ptr = buffer; } // store_long writes a long at a given offset void store_long(long offset, long value) { assert(offset <= get_posn() - 4); @@ -509,20 +598,33 @@ typedef class Serial_buffer { *loc = value; } void check_buffer(long needed); - void set_string(char *s) { + void set_string(const char *s) { char *fence = buffer + len; assert(ptr < fence); + // two brackets surpress a g++ warning, because this is an + // assignment operator inside a test. while ((*ptr++ = *s++)) assert(ptr < fence); - // assert((char *)(((long) (ptr + 7)) & ~7) <= fence); + // 4311 is type cast pointer to long warning + // 4312 is type cast long to pointer warning +#if defined(_WIN32) +#pragma warning(disable: 4311 4312) +#endif + assert((char *)(((long) (ptr + 7)) & ~7) <= fence); +#if defined(_WIN32) +#pragma warning(default: 4311 4312) +#endif pad(); } void set_int32(long v) { *((long *) ptr) = v; ptr += 4; } void set_double(double v) { *((double *) ptr) = v; ptr += 8; } void set_float(float v) { *((float *) ptr) = v; ptr += 4; } void set_char(char v) { *ptr++ = v; } -#ifdef LMMS_BUILD_WIN64 - void pad() { while (((long long) ptr) & 7) set_char(0); } -#else +#if defined(_WIN32) +#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits +#pragma warning(disable: 4311) // type cast pointer to long warning +#endif void pad() { while (((long) ptr) & 7) set_char(0); } +#if defined(_WIN32) +#pragma warning(default: 4311 546) #endif void *to_heap(long *len) { *len = get_posn(); @@ -530,29 +632,7 @@ typedef class Serial_buffer { memcpy(newbuf, buffer, *len); return newbuf; } - void init_for_read(void *buf, long n) { - buffer = (char *) buf; - ptr = (char *) buf; - len = n; - } - char get_char() { return *ptr++; } - long get_int32() { long i = *((long *) ptr); ptr += 4; return i; } - float get_float() { float f = *((float *) ptr); ptr += 4; return f; } - double get_double() { double d = *((double *) ptr); ptr += sizeof(double); - return d; } - char *get_string() { char *s = ptr; char *fence = buffer + len; - assert(ptr < fence); - while (*ptr++) assert(ptr < fence); - get_pad(); - return s; } -#ifdef LMMS_BUILD_WIN64 - void get_pad() { while (((long long) ptr) & 7) ptr++; } -#else - void get_pad() { while (((long) ptr) & 7) ptr++; } -#endif - void check_input_buffer(long needed) { - assert(get_posn() + needed <= len); } -} *Serial_buffer_ptr; +} *Serial_write_buffer_ptr; typedef class Alg_seq *Alg_seq_ptr; @@ -564,7 +644,8 @@ typedef class Alg_track : public Alg_event_list { long get_int32(char **p, long *b); double get_double(char **p, long *b); float get_float(char **p, long *b); - static Serial_buffer ser_buf; + static Serial_read_buffer ser_read_buf; + static Serial_write_buffer ser_write_buf; void serialize_parameter(Alg_parameter *parm); // *buffer_ptr points to binary data, bytes_ptr points to how many // bytes have been used so far, len is length of binary data @@ -587,7 +668,8 @@ typedef class Alg_track : public Alg_event_list { // copy constructor: event_list is copied, map is installed and referenced Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, bool units_are_seconds); - virtual ~Alg_track() { set_time_map(NULL); } + virtual ~Alg_track() { // note: do not call set_time_map(NULL)! + if (time_map) time_map->dereference(); time_map = NULL; } // Returns a buffer containing a serialization of the // file. It will be an ASCII representation unless text is true. @@ -726,6 +808,7 @@ typedef class Alg_track : public Alg_event_list { virtual Alg_event_list *find(double t, double len, bool all, long channel_mask, long event_type_mask); + virtual void set_in_use(bool flag) { in_use = flag; } // // MIDI playback // @@ -785,11 +868,15 @@ class Alg_time_sigs { void show(); long length() { return len; } int find_beat(double beat); - void insert(double beat, double num, double den); - void cut(double start, double end); // remove from start to end + // get the number of beats per measure starting at beat + double get_bar_len(double beat); + void insert(double beat, double num, double den, bool force = false); + void cut(double start, double end, double dur); // remove from start to end void trim(double start, double end); // retain just start to end void paste(double start, Alg_seq *seq); void insert_beats(double beat, double len); // insert len beats at beat + // find the nearest beat (see Alg_seq::nearest_beat) to beat + double nearest_beat(double beat); }; @@ -816,26 +903,103 @@ typedef class Alg_tracks { void append(Alg_track_ptr track); void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds); void reset(); + void set_in_use(bool flag); // handy to set in_use flag on all tracks } *Alg_tracks_ptr; typedef enum { alg_no_error = 0, // no error reading Allegro or MIDI file alg_error_open = -800, // could not open Allegro or MIDI file - alg_error_syntax // something found in the file that could not be parsed; - // generally you should ignore syntax errors or look at the printed error messages - // because there are some things in standard midi files that we do not handle; - // (maybe we should only set alg_error_syntax when there is a real problem with - // the file as opposed to when there is some warning message for the user) + alg_error_syntax // something found in the file that could not be parsed; + // generally you should ignore syntax errors or look at the printed error + // messages because there are some things in standard midi files that we do + // not handle; (maybe we should only set alg_error_syntax when there is a + // real problem with the file as opposed to when there is some warning + // message for the user) } Alg_error; +typedef struct Alg_pending_event { + void *cookie; // client-provided sequence identifier + Alg_events *events; // the array of events + long index; // offset of this event + bool note_on; // is this a note-on or a note-off (if applicable)? + double offset; // time offset for events + double time; // time for this event +} *Alg_pending_event_ptr; + + +typedef class Alg_iterator { +private: + long maxlen; + void expand(); + void expand_to(int new_max); + long len; + Alg_seq_ptr seq; + Alg_pending_event *pending_events; + // the next four fields are mainly for request_note_off() + Alg_events_ptr events_ptr; // remembers events containing current event + long index; // remembers index of current event + void *cookie; // remembers the cookie associated with next event + double offset; + void show(); + bool earlier(int i, int j); + void insert(Alg_events_ptr events, long index, bool note_on, + void *cookie, double offset); + // returns the info on the next pending event in the priority queue + bool remove_next(Alg_events_ptr &events, long &index, bool ¬e_on, + void *&cookie, double &offset, double &time); +public: + bool note_off_flag; // remembers if we are iterating over note-off + // events as well as note-on and update events + long length() { return len; } + Alg_iterator(Alg_seq_ptr s, bool note_off) { + seq = s; + note_off_flag = note_off; + maxlen = len = 0; + pending_events = NULL; + } + // Normally, iteration is over the events in the one sequence used + // to instatiate the iterator (see above), but with this method, you + // can add more sequences to the iteration. Events are returned in + // time order, so effectively sequence events are merged. + // The optional offset is added to each event time of sequence s + // before merging/sorting. You should call begin_seq() for each + // sequence to be included in the iteration unless you call begin() + // (see below). + void begin_seq(Alg_seq_ptr s, void *cookie = NULL, double offset = 0.0); + ~Alg_iterator(); + // Prepare to enumerate events in order. If note_off_flag is true, then + // iteration_next will merge note-off events into the sequence. If you + // call begin(), you should not normally call begin_seq(). See above. + void begin(void *cookie = NULL) { begin_seq(seq, cookie); } + // return next event (or NULL). If iteration_begin was called with + // note_off_flag = true, and if note_on is not NULL, then *note_on + // is set to true when the result value represents a note-on or update. + // (With note_off_flag, each Alg_note event is returned twice, once + // at the note-on time, with *note_on == true, and once at the note-off + // time, with *note_on == false. If a cookie_ptr is passed, then the + // cookie corresponding to the event is stored at that address + // If end_time is 0, iterate through the entire sequence, but if + // end_time is non_zero, stop iterating at the last event before end_time + Alg_event_ptr next(bool *note_on = NULL, void **cookie_ptr = NULL, + double *offset_ptr = NULL, double end_time = 0); + // Sometimes, the caller wants to receive note-off events for a subset + // of the notes, typically the notes that are played and need to be + // turned off. In this case, when a note is turned on, the client + // should call request_note_off(). This will insert a note-off into + // the queue for the most recent note returned by next(). + void request_note_off(); + void end(); // clean up after enumerating events +} *Alg_iterator_ptr; + + // An Alg_seq is an array of Alg_events, each a sequence of Alg_event, // with a tempo map and a sequence of time signatures // typedef class Alg_seq : public Alg_track { protected: - long *current; // array of indexes used by iteration methods + Alg_iterator_ptr pending; // iterator used internally by Alg_seq methods void serialize_seq(); Alg_error error; // error code set by file readers // an internal function used for writing Allegro track names @@ -860,9 +1024,11 @@ typedef class Alg_seq : public Alg_track { Alg_seq(Alg_track_ref track) { seq_from_track(track); } Alg_seq(Alg_track_ptr track) { seq_from_track(*track); } void seq_from_track(Alg_track_ref tr); - Alg_seq(std::istream &file, bool smf); // create from file - Alg_seq(const char *filename, bool smf); // create from filename - ~Alg_seq(); + // create from file: + Alg_seq(std::istream &file, bool smf, double *offset_ptr = NULL); + // create from filename + Alg_seq(const char *filename, bool smf, double *offset_ptr = NULL); + virtual ~Alg_seq(); int get_read_error() { return error; } void serialize(void **buffer, long *bytes); void copy_time_sigs_to(Alg_seq *dest); // a utility function @@ -874,10 +1040,10 @@ typedef class Alg_seq : public Alg_track { void unserialize_seq(); // write an ascii representation to file - void write(std::ostream &file, bool in_secs); + void write(std::ostream &file, bool in_secs, double offset = 0.0); // returns true on success - bool write(const char *filename); - void smf_write(std::ofstream &file); + bool write(const char *filename, double offset = 0.0); + void smf_write(std::ostream &file); bool smf_write(const char *filename); // Returns the number of tracks @@ -917,23 +1083,34 @@ typedef class Alg_seq : public Alg_track { // find index of first score event after time long seek_time(double time, int track_num); bool insert_beat(double time, double beat); + // return the time of the beat nearest to time, also returns beat + // number through beat. This will correspond to an integer number + // of beats from the nearest previous time signature or 0.0, but + // since time signatures need not be on integer beat boundaries + // the beat location may not be on an integer beat (beat locations + // are measured from the beginning which is beat 0. + double nearest_beat_time(double time, double *beat); // warning: insert_tempo may change representation from seconds to beats bool insert_tempo(double bpm, double beat); - + // change the duration from b0 to b1 (beats) to dur (seconds) by + // scaling the intervening tempos + bool stretch_region(double b0, double b1, double dur); // add_event takes a pointer to an event on the heap. The event is not // copied, and this Alg_seq becomes the owner and freer of the event. void add_event(Alg_event_ptr event, int track_num); void add(Alg_event_ptr event) { assert(false); } // call add_event instead - // warning: set_tempo may change representation from seconds to beats + // get the tempo starting at beat + double get_tempo(double beat); bool set_tempo(double bpm, double start_beat, double end_beat); + + // get the bar length in beats starting at beat + double get_bar_len(double beat); void set_time_sig(double beat, double num, double den); void beat_to_measure(double beat, long *measure, double *m_beat, double *num, double *den); // void set_events(Alg_event_ptr *events, long len, long max); void merge_tracks(); // move all track data into one track - void iteration_begin(); // prepare to enumerate events in order - Alg_event_ptr iteration_next(); // return next event (or NULL) - void iteration_end(); // clean up after enumerating events + void set_in_use(bool flag); // set in_use flag on all tracks } *Alg_seq_ptr, &Alg_seq_ref; diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp index 7a1f5beed6f..307f9562b40 100644 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ b/plugins/MidiImport/portsmf/allegrord.cpp @@ -1,753 +1,774 @@ -#include "debug.h" -#include "stdlib.h" -#include "string.h" -#include "ctype.h" -#include "trace.h" -#include -#include -#include -#include "strparse.h" -#include "allegro.h" -#include "algrd_internal.h" - -using namespace std; - -#define streql(s1, s2) (strcmp(s1, s2) == 0) -#define field_max 80 - -class Alg_reader { -public: - istream *file; - string input_line; - int line_no; - String_parse line_parser; - bool line_parser_flag; - string field; - bool error_flag; - Alg_seq_ptr seq; - double tsnum; - double tsden; - - Alg_reader(istream *a_file, Alg_seq_ptr new_seq); - void readline(); - Alg_parameters_ptr process_attributes(Alg_parameters_ptr attributes, - double time); - bool parse(); - long parse_chan(string &field); - long parse_int(string &field); - int find_real_in(string &field, int n); - double parse_real(string &field); - void parse_error(string &field, long offset, const char *message); - double parse_dur(string &field, double base); - double parse_after_dur(double dur, string &field, int n, double base); - double parse_loud(string &field); - long parse_key(string &field); - double parse_pitch(string &field); - long parse_after_key(int key, string &field, int n); - long find_int_in(string &field, int n); - bool parse_attribute(string &field, Alg_parameter_ptr parm); - bool parse_val(Alg_parameter_ptr param, string &s, int i); - bool check_type(char type_char, Alg_parameter_ptr param); -}; - - -double Alg_reader::parse_pitch(string &field) -{ - if (isdigit(field[1])) { - int last = find_real_in(field, 1); - string real_string = field.substr(1, last - 1); - return atof(real_string.c_str()); - } else { - return (double) parse_key(field); - } -} - - -// it is the responsibility of the caller to delete -// the seq -Alg_reader::Alg_reader(istream *a_file, Alg_seq_ptr new_seq) -{ - file = a_file; // save the file - line_parser_flag = false; - line_no = 0; - tsnum = 4; // default time signature - tsden = 4; - seq = new_seq; -} - - -Alg_error alg_read(istream &file, Alg_seq_ptr new_seq) - // read a sequence from allegro file -{ - assert(new_seq); - Alg_reader alg_reader(&file, new_seq); - bool err = alg_reader.parse(); - return (err ? alg_error_syntax : alg_no_error); -} - - -void Alg_reader::readline() -{ - // a word about memory management: this Alg_reader has a - // member variable input_line that holds a line of input - // it is reused for each line. input_line is parsed by - // line_parser, which holds a reference to input_line - line_parser_flag = false; - if (getline(*file, input_line)) { - line_parser.init(&input_line); - line_parser_flag = true; - error_flag = false; - } -} - - -Alg_parameters_ptr Alg_reader::process_attributes( - Alg_parameters_ptr attributes, double time) -{ - // print "process_attributes:", attributes - bool ts_flag = false; - if (attributes) { - Alg_parameters_ptr a; - bool in_seconds = seq->get_units_are_seconds(); - if ((a = Alg_parameters::remove_key(&attributes, "tempor"))) { - double tempo = a->parm.r; - seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time)); - } - if ((a = Alg_parameters::remove_key(&attributes, "beatr"))) { - double beat = a->parm.r; - seq->insert_beat(time, beat); - } - if ((a = Alg_parameters::remove_key(&attributes, "timesig_numr"))) { - tsnum = a->parm.r; - ts_flag = true; - } - if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) { - tsden = a->parm.r; - ts_flag = true; - } - if (ts_flag) { - seq->set_time_sig(seq->get_time_map()->time_to_beat(time), - tsnum, tsden); - } - if (in_seconds) seq->convert_to_seconds(); - } - return attributes; // in case it was modified -} - - -bool Alg_reader::parse() -{ - int voice = 0; - int key = 60; - double loud = 100.0; - double pitch = 60.0; - double dur = 1.0; - double time = 0.0; - int track_num = 0; - seq->convert_to_seconds(); - //seq->set_real_dur(0.0); // just in case it's not initialized already - readline(); - bool valid = false; // ignore blank lines - while (line_parser_flag) { - bool time_flag = false; - bool next_flag = false; - double next; - bool voice_flag = false; - bool loud_flag = false; - bool dur_flag = false; - bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax - double new_pitch = 0.0; - bool new_key_flag = false; // "K" syntax - int new_key = 0; - Alg_parameters_ptr attributes = NULL; - if (line_parser.peek() == '#') { - // look for #track - line_parser.get_nonspace_quoted(field); - if (streql(field.c_str(), "#track")) { - line_parser.get_nonspace_quoted(field); // number - field.insert(0, " "); // need char at beginning because - // parse_int ignores the first character of the argument - track_num = parse_int(field); - seq->add_track(track_num); - } - // maybe we have a sequence or track name - line_parser.get_remainder(field); - // if there is a non-space character after #track n then - // use it as sequence or track name. Note that because we - // skip over spaces, a sequence or track name cannot begin - // with leading blanks. Another decision is that the name - // must be at time zero - if (field.length() > 0) { - // insert the field as sequence name or track name - Alg_update_ptr update = new Alg_update; - update->chan = -1; - update->time = 0; - update->set_identifier(-1); - // sequence name is whatever is on track 0 - // other tracks have track names - const char *attr = - (track_num == 0 ? "seqnames" : "tracknames"); - update->parameter.set_attr(symbol_table.insert_string(attr)); - update->parameter.s = heapify(field.c_str()); - seq->add_event(update, track_num); - } - } else { - // we must have a track to insert into - if (seq->tracks() == 0) seq->add_track(0); - line_parser.get_nonspace_quoted(field); - char pk = line_parser.peek(); - // attributes are parsed as two adjacent nonspace_quoted tokens - // so we have to conditionally call get_nonspace_quoted() again - if (pk && !isspace(pk)) { - string field2; - line_parser.get_nonspace_quoted(field2); - field.append(field2); - } - while (field[0]) { - char first = toupper(field[0]); - if (strchr("ABCDEFGKLPUSIQHW-", first)) { - valid = true; // it's a note or event - } - if (first == 'V') { - if (voice_flag) { - parse_error(field, 0, "Voice specified twice"); - } else { - voice = parse_chan(field); - } - voice_flag = true; - } else if (first == 'T') { - if (time_flag) { - parse_error(field, 0, "Time specified twice"); - } else { - time = parse_dur(field, 0.0); - } - time_flag = true; - } else if (first == 'N') { - if (next_flag) { - parse_error(field, 0, "Next specified twice"); - } else { - next = parse_dur(field, time); - } - next_flag = true; - } else if (first == 'K') { - if (new_key_flag) { - parse_error(field, 0, "Key specified twice"); - } else { - new_key = parse_key(field); - new_key_flag = true; - } - } else if (first == 'L') { - if (loud_flag) { - parse_error(field, 0, "Loudness specified twice"); - } else { - loud = parse_loud(field); - } - loud_flag = true; - } else if (first == 'P') { - if (new_pitch_flag) { - parse_error(field, 0, "Pitch specified twice"); - } else { - new_pitch = parse_pitch(field); - new_pitch_flag = true; - } - } else if (first == 'U') { - if (dur_flag) { - parse_error(field, 0, "Dur specified twice"); - } else { - dur = parse_dur(field, time); - dur_flag = true; - } - } else if (strchr("SIQHW", first)) { - if (dur_flag) { - parse_error(field, 0, "Dur specified twice"); - } else { - // prepend 'U' to field, copy EOS too - field.insert(0, 1, 'U'); - dur = parse_dur(field, time); - dur_flag = true; - } - } else if (strchr("ABCDEFG", first)) { - if (new_pitch_flag) { - parse_error(field, 0, "Pitch specified twice"); - } else { - // prepend 'P' to field - field.insert(0, 1, 'P'); - new_pitch = parse_pitch(field); - new_pitch_flag = true; - } - } else if (first == '-') { - Alg_parameter parm; - if (parse_attribute(field, &parm)) { // enter attribute-value pair - attributes = new Alg_parameters(attributes); - attributes->parm = parm; - parm.s = NULL; // protect string from deletion by destructor - } - } else { - parse_error(field, 0, "Unknown field"); - } - - if (error_flag) { - field[0] = 0; // exit the loop - } else { - line_parser.get_nonspace_quoted(field); - pk = line_parser.peek(); - // attributes are parsed as two adjacent nonspace_quoted - // tokens so we have to conditionally call - // get_nonspace_quoted() again - if (pk && !isspace(pk)) { - string field2; - line_parser.get_nonspace_quoted(field2); - field.append(field2); - } - } - } - // a case analysis: - // Key < 128 implies pitch unless pitch is explicitly given - // Pitch implies Key unless key is explicitly given, - // Pitch is rounded to nearest integer to determine the Key - // if necessary, so MIDI files will lose the pitch fraction - // A-G is a Pitch specification (therefore it implies Key) - // K60 P60 -- both are specified, use 'em - // K60 P60 C4 -- overconstrained, an error - // K60 C4 -- OK, but K60 is already implied by C4 - // K60 -- OK, pitch is 60 - // C4 P60 -- over constrained - // P60 -- OK, key is 60 - // P60.1 -- OK, key is 60 - // C4 -- OK, key is 60, pitch is 60 - // -- OK, key and pitch from before - // K200 P60 -- ok, pitch is 60 - // K200 with neither P60 nor C4 uses - // pitch from before - - // figure out what the key/instance is: - if (new_key_flag) { // it was directly specified - key = new_key; - } else if (new_pitch_flag) { - // pitch was specified, but key was not; get key from pitch - key = (int) (new_pitch + 0.5); // round to integer key number - } - if (new_pitch_flag) { - pitch = new_pitch; - } else if (key < 128 && new_key_flag) { - // no explicit pitch, but key < 128, so it implies pitch - pitch = key; - new_pitch_flag = true; - } - // now we've acquired new parameters - // if (it is a note, then enter the note - if (valid) { - // change tempo or beat - attributes = process_attributes(attributes, time); - // if there's a duration or pitch, make a note: - if (new_pitch_flag || dur_flag) { - Alg_note_ptr note_ptr = new Alg_note; - note_ptr->chan = voice; - note_ptr->time = time; - note_ptr->dur = dur; - note_ptr->set_identifier(key); - note_ptr->pitch = pitch; - note_ptr->loud = loud; - note_ptr->parameters = attributes; - seq->add_event(note_ptr, track_num); // sort later - if (seq->get_real_dur() < (time + dur)) seq->set_real_dur(time + dur); - } else { - int update_key = -1; - // key must appear explicitly; otherwise - // update applies to channel - if (new_key_flag) { - update_key = key; - } - if (loud_flag) { - Alg_update_ptr new_upd = new Alg_update; - new_upd->chan = voice; - new_upd->time = time; - new_upd->set_identifier(update_key); - new_upd->parameter.set_attr(symbol_table.insert_string("loudr")); - new_upd->parameter.r = pitch; - seq->add_event(new_upd, track_num); - if (seq->get_real_dur() < time) seq->set_real_dur(time); - } - if (attributes) { - while (attributes) { - Alg_update_ptr new_upd = new Alg_update; - new_upd->chan = voice; - new_upd->time = time; - new_upd->set_identifier(update_key); - new_upd->parameter = attributes->parm; - seq->add_event(new_upd, track_num); - Alg_parameters_ptr p = attributes; - attributes = attributes->next; - p->parm.s = NULL; // so we don't delete the string - delete p; - } - } - } - if (next_flag) { - time = time + next; - } else if (dur_flag || new_pitch_flag) { // a note: incr by dur - time = time + dur; - } - } - } - readline(); - } - if (!error_flag) { // why not convert even if there was an error? -RBD - seq->convert_to_seconds(); // make sure format is correct - } - // real_dur is valid, translate to beat_dur - seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur())); - return error_flag; -} - - -long Alg_reader::parse_chan(string &field) -{ - const char *int_string = field.c_str() + 1; - const char *msg = "Integer or - expected"; - const char *p = int_string; - char c; - // check that all chars in int_string are digits or '-': - while ((c = *p++)) { - if (!isdigit(c) && c != '-') { - parse_error(field, p - field.c_str() - 1, msg); - return 0; - } - } - p--; // p now points to end-of-string character - if (p - int_string == 0) { - // bad: string length is zero - parse_error(field, 1, msg); - return 0; - } - if (p - int_string == 1 && int_string[0] == '-') { - // special case: entire string is "-", interpret as -1 - return -1; - } - return atoi(int_string); -} - - -long Alg_reader::parse_int(string &field) -{ - const char *int_string = field.c_str() + 1; - const char *msg = "Integer expected"; - const char *p = int_string; - char c; - // check that all chars in int_string are digits: - while ((c = *p++)) { - if (!isdigit(c)) { - parse_error(field, p - field.c_str() - 1, msg); - return 0; - } - } - p--; // p now points to end-of-string character - if (p - int_string == 0) { - // bad: string length is zero - parse_error(field, 1, msg); - return 0; - } - return atoi(int_string); -} - - -int Alg_reader::find_real_in(string &field, int n) -{ - // scans from offset n to the end of a real constant - bool decimal = false; - int len = field.length(); - for (int i = n; i < len; i++) { - char c = field[i]; - if (!isdigit(c)) { - if (c == '.' && !decimal) { - decimal = true; - } else { - return i; - } - } - } - return field.length(); -} - - -double Alg_reader::parse_real(string &field) -{ - const char *msg = "Real expected"; - int last = find_real_in(field, 1); - string real_string = field.substr(1, last - 1); - if (last <= 1 || last < (int) field.length()) { - parse_error(field, 1, msg); - return 0; - } - return atof(real_string.c_str()); -} - - -void Alg_reader::parse_error(string &field, long offset, const char *message) -{ - int position = line_parser.pos - field.length() + offset; - error_flag = true; - puts(line_parser.str->c_str()); - for (int i = 0; i < position; i++) { - putc(' ', stdout); - } - putc('^', stdout); - printf(" %s\n", message); -} - - -double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; - - -double Alg_reader::parse_dur(string &field, double base) -{ - const char *msg = "Duration expected"; - const char *durs = "SIQHW"; - const char *p; - int last; - double dur; - if (field.length() < 2) { - // fall through to error message - return -1; - } else if (isdigit(field[1])) { - last = find_real_in(field, 1); - string real_string = field.substr(1, last - 1); - dur = atof(real_string.c_str()); - // convert dur from seconds to beats - dur = seq->get_time_map()->time_to_beat(base + dur) - - seq->get_time_map()->time_to_beat(base); - } else if ((p = strchr(durs, toupper(field[1])))) { - dur = duration_lookup[p - durs]; - last = 2; - } else { - parse_error(field, 1, msg); - return 0; - } - dur = parse_after_dur(dur, field, last, base); - dur = seq->get_time_map()->beat_to_time( - seq->get_time_map()->time_to_beat(base) + dur) - base; - return dur; -} - - -double Alg_reader::parse_after_dur(double dur, string &field, - int n, double base) -{ - if ((int) field.length() == n) { - return dur; - } - if (toupper(field[n]) == 'T') { - return parse_after_dur(dur * 2/3, field, n + 1, base); - } - if (field[n] == '.') { - return parse_after_dur(dur * 1.5, field, n + 1, base); - } - if (isdigit(field[n])) { - int last = find_real_in(field, n); - string a_string = field.substr(n, last - n); - double f = atof(a_string.c_str()); - return parse_after_dur(dur * f, field, last, base); - } - if (field[n] == '+') { - string a_string = field.substr(n + 1); - return dur + parse_dur( - a_string, seq->get_time_map()->beat_to_time( - seq->get_time_map()->time_to_beat(base) + dur)); - } - parse_error(field, n, "Unexpected character in duration"); - return dur; -} - -struct loud_lookup_struct { - const char *str; - int val; -} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100}, - {"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60}, - {NULL, 0} }; - - -double Alg_reader::parse_loud(string &field) -{ - const char *msg = "Loudness expected"; - if (isdigit(field[1])) { - return parse_int(field); - } else { - string dyn = field.substr(1); - transform(dyn.begin(), dyn.end(), dyn.begin(), ::toupper); - for (int i = 0; loud_lookup[i].str; i++) { - if (streql(loud_lookup[i].str, dyn.c_str())) { - return (double) loud_lookup[i].val; - } - } - } - parse_error(field, 1, msg); - return 100.0; -} - - -int key_lookup[] = {21, 23, 12, 14, 16, 17, 19}; - - -// the field can be K or K[A-G] or P[A-G] -// (this can be called from parse_pitch() to handle [A-G]) -// Notice that the routine ignores the first character: K or P -// -long Alg_reader::parse_key(string &field) -{ - const char *msg = "Pitch expected"; - const char *pitches = "ABCDEFG"; - const char *p; - if (isdigit(field[1])) { - // This routine would not have been called if field = "P" - // so it must be "K" so must be an integer. - return parse_int(field); - } else if ((p = strchr(pitches, toupper(field[1])))) { - long key = key_lookup[p - pitches]; - key = parse_after_key(key, field, 2); - return key; - } - parse_error(field, 1, msg); - return 0; -} - - -long Alg_reader::parse_after_key(int key, string &field, int n) -{ - if ((int) field.length() == n) { - return key; - } - char c = toupper(field[n]); - if (c == 'S') { - return parse_after_key(key + 1, field, n + 1); - } - if (c == 'F') { - return parse_after_key(key - 1, field, n + 1); - } - if (isdigit(field[n])) { - int last = find_int_in(field, n); - string octave = field.substr(n, last - n); - int oct = atoi(octave.c_str()); - return parse_after_key(key + oct * 12, field, last); - } - parse_error(field, n, "Unexpected character in pitch"); - return key; -} - - -long Alg_reader::find_int_in(string &field, int n) -{ - while ((int) field.length() > n && isdigit(field[n])) { - n = n + 1; - } - return n; -} - - -bool Alg_reader::parse_attribute(string &field, Alg_parameter_ptr param) -{ - int i = 1; - while (i < (int) field.length()) { - if (field[i] == ':') { - string attr = field.substr(1, i - 1); - char type_char = field[i - 1]; - if (strchr("iarsl", type_char)) { - param->set_attr(symbol_table.insert_string(attr.c_str())); - parse_val(param, field, i + 1); - } else { - parse_error(field, 0, "attribute needs to end with typecode: i,a,r,s, or l"); - } - return !error_flag; - } - i = i + 1; - } - return false; -} - - -bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i) -{ - int len = (int) s.length(); - if (i >= len) { - return false; - } - if (s[i] == '"') { - if (!check_type('s', param)) { - return false; - } - // note: (len - i) includes 2 quote characters but no EOS character - // so total memory to allocate is (len - i) - 1 - char *r = new char[(len - i) - 1]; - strncpy(r, s.c_str() + i + 1, (len - i) - 2); - r[(len - i) - 2] = 0; // terminate the string - param->s = r; - } else if (s[i] == '\'') { - if (!check_type('a', param)) { - return false; - } - string r = s.substr(i + 1, len - i - 2); - param->a = symbol_table.insert_string(r.c_str()); - } else if (param->attr_type() == 'l') { - if (streql(s.c_str() + i, "true") || - streql(s.c_str() + i, "t")) { - param->l = true; - } else if (streql(s.c_str() + i, "false") || - streql(s.c_str() + i, "nil")) { - param->l = false; - } else return false; - } else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') { - int pos = i; - bool period = false; - if (s[pos] == '-') { - pos++; - } - while (pos < len) { - if (isdigit(s[pos])) { - ; - } else if (!period && s[pos] == '.') { - period = true; - } else { - parse_error(s, pos, "Unexpected char in number"); - return false; - } - pos = pos + 1; - } - string r = s.substr(i, len - i); - if (period) { - if (!check_type('r', param)) { - return false; - } - param->r = atof(r.c_str()); - } else { - if (param->attr_type() == 'r') { - param->r = atoi(r.c_str()); - } else if (!check_type('i', param)) { - return false; - } else { - param->i = atoi(r.c_str()); - } - } - } else { - parse_error(s, i, "invalid value"); - return false; - } - return true; -} - - -bool Alg_reader::check_type(char type_char, Alg_parameter_ptr param) -{ - return param->attr_type() == type_char; -} - - -//duration_lookup = {"S": 0.5, "I": 0.5, "Q": 1, "H": 2, "W": 4} -//key_lookup = {"C": 12, "D": 14, "E": 16, "F": 17, "G": 19, "A": 21, "B": 23} - -/* -def test(): - reader = Alg_reader(open("data\\test.gro", "r")) - reader.parse() - score = reader->seq.notes - print "score:", score - reader = nil -*/ +#include "assert.h" +#include "stdlib.h" +#include "string.h" +#include "ctype.h" +#include "trace.h" +#include +#include +#include +#include "strparse.h" +#include "allegro.h" +#include "algrd_internal.h" + +using namespace std; + +#define streql(s1, s2) (strcmp(s1, s2) == 0) +#define field_max 80 + +class Alg_reader { +public: + istream *file; + string input_line; + int line_no; + String_parse line_parser; + bool line_parser_flag; + string field; + bool error_flag; + Alg_seq_ptr seq; + double tsnum; + double tsden; + double offset; + bool offset_found; + + Alg_reader(istream *a_file, Alg_seq_ptr new_seq); + void readline(); + Alg_parameters_ptr process_attributes(Alg_parameters_ptr attributes, + double time); + bool parse(); + long parse_chan(string &field); + long parse_int(string &field); + int find_real_in(string &field, int n); + double parse_real(string &field); + void parse_error(string &field, long offset, char *message); + double parse_dur(string &field, double base); + double parse_after_dur(double dur, string &field, int n, double base); + double parse_loud(string &field); + long parse_key(string &field); + double parse_pitch(string &field); + long parse_after_key(int key, string &field, int n); + long find_int_in(string &field, int n); + bool parse_attribute(string &field, Alg_parameter_ptr parm); + bool parse_val(Alg_parameter_ptr param, string &s, int i); + bool check_type(char type_char, Alg_parameter_ptr param); +}; + + +double Alg_reader::parse_pitch(string &field) +{ + if (isdigit(field[1])) { + int last = find_real_in(field, 1); + string real_string = field.substr(1, last - 1); + return atof(real_string.c_str()); + } else { + return (double) parse_key(field); + } +} + + +// it is the responsibility of the caller to delete +// the seq +Alg_reader::Alg_reader(istream *a_file, Alg_seq_ptr new_seq) +{ + file = a_file; // save the file + line_parser_flag = false; + line_no = 0; + tsnum = 4; // default time signature + tsden = 4; + seq = new_seq; + offset = 0.0; + offset_found = false; +} + + +Alg_error alg_read(istream &file, Alg_seq_ptr new_seq, double *offset_ptr) + // read a sequence from allegro file +{ + assert(new_seq); + Alg_reader alg_reader(&file, new_seq); + bool err = alg_reader.parse(); + if (!err && offset_ptr) { + *offset_ptr = alg_reader.offset; + } + return (err ? alg_error_syntax : alg_no_error); +} + + +void Alg_reader::readline() +{ + // a word about memory management: this Alg_reader has a + // member variable input_line that holds a line of input + // it is reused for each line. input_line is parsed by + // line_parser, which holds a reference to input_line + line_parser_flag = false; + if (getline(*file, input_line)) { + line_parser.init(&input_line); + line_parser_flag = true; + error_flag = false; + } +} + + +Alg_parameters_ptr Alg_reader::process_attributes( + Alg_parameters_ptr attributes, double time) +{ + // print "process_attributes:", attributes + bool ts_flag = false; + if (attributes) { + Alg_parameters_ptr a; + bool in_seconds = seq->get_units_are_seconds(); + if (a = Alg_parameters::remove_key(&attributes, "tempor")) { + double tempo = a->parm.r; + seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time)); + } + if (a = Alg_parameters::remove_key(&attributes, "beatr")) { + double beat = a->parm.r; + seq->insert_beat(time, beat); + } + if (a = Alg_parameters::remove_key(&attributes, "timesig_numr")) { + tsnum = a->parm.r; + ts_flag = true; + } + if (a = Alg_parameters::remove_key(&attributes, "timesig_denr")) { + tsden = a->parm.r; + ts_flag = true; + } + if (ts_flag) { + seq->set_time_sig(seq->get_time_map()->time_to_beat(time), + tsnum, tsden); + } + if (in_seconds) seq->convert_to_seconds(); + } + return attributes; // in case it was modified +} + + +bool Alg_reader::parse() +{ + int voice = 0; + int key = 60; + double loud = 100.0; + double pitch = 60.0; + double dur = 1.0; + double time = 0.0; + int track_num = 0; + seq->convert_to_seconds(); + //seq->set_real_dur(0.0); // just in case it's not initialized already + readline(); + bool valid = false; // ignore blank lines + while (line_parser_flag) { + bool time_flag = false; + bool next_flag = false; + double next; + bool voice_flag = false; + bool loud_flag = false; + bool dur_flag = false; + bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax + double new_pitch = 0.0; + bool new_key_flag = false; // "K" syntax + int new_key = 0; + Alg_parameters_ptr attributes = NULL; + if (line_parser.peek() == '#') { + // look for #track + line_parser.get_nonspace_quoted(field); + if (streql(field.c_str(), "#track")) { + line_parser.get_nonspace_quoted(field); // number + field.insert(0, " "); // need char at beginning because + // parse_int ignores the first character of the argument + track_num = parse_int(field); + seq->add_track(track_num); + + // maybe we have a sequence or track name + line_parser.get_remainder(field); + // if there is a non-space character after #track n then + // use it as sequence or track name. Note that because we + // skip over spaces, a sequence or track name cannot begin + // with leading blanks. Another decision is that the name + // must be at time zero + if (field.length() > 0) { + // insert the field as sequence name or track name + Alg_update_ptr update = new Alg_update; + update->chan = -1; + update->time = 0; + update->set_identifier(-1); + // sequence name is whatever is on track 0 + // other tracks have track names + const char *attr = + (track_num == 0 ? "seqnames" : "tracknames"); + update->parameter.set_attr( + symbol_table.insert_string(attr)); + update->parameter.s = heapify(field.c_str()); + seq->add_event(update, track_num); + } + } else if (streql(field.c_str(), "#offset")) { + if (offset_found) { + parse_error(field, 0, "#offset specified twice"); + } + offset_found = true; + line_parser.get_nonspace_quoted(field); // number + field.insert(0, " "); // need char at beginning because + // parse_real ignores first character in the argument + offset = parse_real(field); + } + } else { + // we must have a track to insert into + if (seq->tracks() == 0) seq->add_track(0); + line_parser.get_nonspace_quoted(field); + char pk = line_parser.peek(); + // attributes are parsed as two adjacent nonspace_quoted tokens + // so we have to conditionally call get_nonspace_quoted() again + if (pk && !isspace(pk)) { + string field2; + line_parser.get_nonspace_quoted(field2); + field.append(field2); + } + while (field[0]) { + char first = toupper(field[0]); + if (strchr("ABCDEFGKLPUSIQHW-", first)) { + valid = true; // it's a note or event + } + if (first == 'V') { + if (voice_flag) { + parse_error(field, 0, "Voice specified twice"); + } else { + voice = parse_chan(field); + } + voice_flag = true; + } else if (first == 'T') { + if (time_flag) { + parse_error(field, 0, "Time specified twice"); + } else { + time = parse_dur(field, 0.0); + } + time_flag = true; + } else if (first == 'N') { + if (next_flag) { + parse_error(field, 0, "Next specified twice"); + } else { + next = parse_dur(field, time); + } + next_flag = true; + } else if (first == 'K') { + if (new_key_flag) { + parse_error(field, 0, "Key specified twice"); + } else { + new_key = parse_key(field); + new_key_flag = true; + } + } else if (first == 'L') { + if (loud_flag) { + parse_error(field, 0, "Loudness specified twice"); + } else { + loud = parse_loud(field); + } + loud_flag = true; + } else if (first == 'P') { + if (new_pitch_flag) { + parse_error(field, 0, "Pitch specified twice"); + } else { + new_pitch = parse_pitch(field); + new_pitch_flag = true; + } + } else if (first == 'U') { + if (dur_flag) { + parse_error(field, 0, "Dur specified twice"); + } else { + dur = parse_dur(field, time); + dur_flag = true; + } + } else if (strchr("SIQHW", first)) { + if (dur_flag) { + parse_error(field, 0, "Dur specified twice"); + } else { + // prepend 'U' to field, copy EOS too + field.insert((unsigned int) 0, 1, 'U'); + dur = parse_dur(field, time); + dur_flag = true; + } + } else if (strchr("ABCDEFG", first)) { + if (new_pitch_flag) { + parse_error(field, 0, "Pitch specified twice"); + } else { + // prepend 'P' to field + field.insert((unsigned int) 0, 1, 'P'); + new_pitch = parse_pitch(field); + new_pitch_flag = true; + } + } else if (first == '-') { + Alg_parameter parm; + if (parse_attribute(field, &parm)) { // enter attribute-value pair + attributes = new Alg_parameters(attributes); + attributes->parm = parm; + parm.s = NULL; // protect string from deletion by destructor + } + } else { + parse_error(field, 0, "Unknown field"); + } + + if (error_flag) { + field[0] = 0; // exit the loop + } else { + line_parser.get_nonspace_quoted(field); + pk = line_parser.peek(); + // attributes are parsed as two adjacent nonspace_quoted + // tokens so we have to conditionally call + // get_nonspace_quoted() again + if (pk && !isspace(pk)) { + string field2; + line_parser.get_nonspace_quoted(field2); + field.append(field2); + } + } + } + // a case analysis: + // Key < 128 implies pitch unless pitch is explicitly given + // Pitch implies Key unless key is explicitly given, + // Pitch is rounded to nearest integer to determine the Key + // if necessary, so MIDI files will lose the pitch fraction + // A-G is a Pitch specification (therefore it implies Key) + // K60 P60 -- both are specified, use 'em + // K60 P60 C4 -- overconstrained, an error + // K60 C4 -- OK, but K60 is already implied by C4 + // K60 -- OK, pitch is 60 + // C4 P60 -- over constrained + // P60 -- OK, key is 60 + // P60.1 -- OK, key is 60 + // C4 -- OK, key is 60, pitch is 60 + // -- OK, key and pitch from before + // K200 P60 -- ok, pitch is 60 + // K200 with neither P60 nor C4 uses + // pitch from before + + // figure out what the key/instance is: + if (new_key_flag) { // it was directly specified + key = new_key; + } else if (new_pitch_flag) { + // pitch was specified, but key was not; get key from pitch + key = (int) (new_pitch + 0.5); // round to integer key number + } + if (new_pitch_flag) { + pitch = new_pitch; + } else if (key < 128 && new_key_flag) { + // no explicit pitch, but key < 128, so it implies pitch + pitch = key; + new_pitch_flag = true; + } + // now we've acquired new parameters + // if (it is a note, then enter the note + if (valid) { + // change tempo or beat + attributes = process_attributes(attributes, time); + // if there's a duration or pitch, make a note: + if (new_pitch_flag || dur_flag) { + Alg_note_ptr note_ptr = new Alg_note; + note_ptr->chan = voice; + note_ptr->time = time; + note_ptr->dur = dur; + note_ptr->set_identifier(key); + note_ptr->pitch = (float) pitch; + note_ptr->loud = (float) loud; + note_ptr->parameters = attributes; + seq->add_event(note_ptr, track_num); // sort later + if (seq->get_real_dur() < (time + dur)) seq->set_real_dur(time + dur); + } else { + int update_key = -1; + // key must appear explicitly; otherwise + // update applies to channel + if (new_key_flag) { + update_key = key; + } + if (loud_flag) { + Alg_update_ptr new_upd = new Alg_update; + new_upd->chan = voice; + new_upd->time = time; + new_upd->set_identifier(update_key); + new_upd->parameter.set_attr(symbol_table.insert_string("loudr")); + new_upd->parameter.r = pitch; + seq->add_event(new_upd, track_num); + if (seq->get_real_dur() < time) seq->set_real_dur(time); + } + if (attributes) { + while (attributes) { + Alg_update_ptr new_upd = new Alg_update; + new_upd->chan = voice; + new_upd->time = time; + new_upd->set_identifier(update_key); + new_upd->parameter = attributes->parm; + seq->add_event(new_upd, track_num); + Alg_parameters_ptr p = attributes; + attributes = attributes->next; + p->parm.s = NULL; // so we don't delete the string + delete p; + } + } + } + if (next_flag) { + time = time + next; + } else if (dur_flag || new_pitch_flag) { // a note: incr by dur + time = time + dur; + } + } + } + readline(); + } + if (!error_flag) { // why not convert even if there was an error? -RBD + seq->convert_to_seconds(); // make sure format is correct + } + // real_dur is valid, translate to beat_dur + seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur())); + return error_flag; +} + + +long Alg_reader::parse_chan(string &field) +{ + const char *int_string = field.c_str() + 1; + char *msg = "Integer or - expected"; + const char *p = int_string; + char c; + // check that all chars in int_string are digits or '-': + while (c = *p++) { + if (!isdigit(c) && c != '-') { + parse_error(field, p - field.c_str() - 1, msg); + return 0; + } + } + p--; // p now points to end-of-string character + if (p - int_string == 0) { + // bad: string length is zero + parse_error(field, 1, msg); + return 0; + } + if (p - int_string == 1 && int_string[0] == '-') { + // special case: entire string is "-", interpret as -1 + return -1; + } + return atoi(int_string); +} + + +long Alg_reader::parse_int(string &field) +{ + const char *int_string = field.c_str() + 1; + char *msg = "Integer expected"; + const char *p = int_string; + char c; + // check that all chars in int_string are digits: + while (c = *p++) { + if (!isdigit(c)) { + parse_error(field, p - field.c_str() - 1, msg); + return 0; + } + } + p--; // p now points to end-of-string character + if (p - int_string == 0) { + // bad: string length is zero + parse_error(field, 1, msg); + return 0; + } + return atoi(int_string); +} + + +int Alg_reader::find_real_in(string &field, int n) +{ + // scans from offset n to the end of a real constant + bool decimal = false; + int len = field.length(); + if (n < len && field[n] == '-') n += 1; // parse one minus sign + for (int i = n; i < len; i++) { + char c = field[i]; + if (!isdigit(c)) { + if (c == '.' && !decimal) { + decimal = true; + } else { + return i; + } + } + } + return len; +} + + +double Alg_reader::parse_real(string &field) +{ + char *msg = "Real expected"; + int last = find_real_in(field, 1); + string real_string = field.substr(1, last - 1); + if (last <= 1 || last < (int) field.length()) { + parse_error(field, 1, msg); + return 0; + } + return atof(real_string.c_str()); +} + + +void Alg_reader::parse_error(string &field, long offset, char *message) +{ + int position = line_parser.pos - field.length() + offset; + error_flag = true; + puts(line_parser.str->c_str()); + for (int i = 0; i < position; i++) { + putc(' ', stdout); + } + putc('^', stdout); + printf(" %s\n", message); +} + + +double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; + + +double Alg_reader::parse_dur(string &field, double base) +{ + char *msg = "Duration expected"; + char *durs = "SIQHW"; + char *p; + int last; + double dur; + if (field.length() < 2) { + // fall through to error message + return -1; + } else if (isdigit(field[1])) { + last = find_real_in(field, 1); + string real_string = field.substr(1, last - 1); + dur = atof(real_string.c_str()); + // convert dur from seconds to beats + dur = seq->get_time_map()->time_to_beat(base + dur) - + seq->get_time_map()->time_to_beat(base); + } else if (p = strchr(durs, toupper(field[1]))) { + dur = duration_lookup[p - durs]; + last = 2; + } else { + parse_error(field, 1, msg); + return 0; + } + dur = parse_after_dur(dur, field, last, base); + dur = seq->get_time_map()->beat_to_time( + seq->get_time_map()->time_to_beat(base) + dur) - base; + return dur; +} + + +double Alg_reader::parse_after_dur(double dur, string &field, + int n, double base) +{ + if ((int) field.length() == n) { + return dur; + } + if (toupper(field[n]) == 'T') { + return parse_after_dur(dur * 2/3, field, n + 1, base); + } + if (field[n] == '.') { + return parse_after_dur(dur * 1.5, field, n + 1, base); + } + if (isdigit(field[n])) { + int last = find_real_in(field, n); + string a_string = field.substr(n, last - n); + double f = atof(a_string.c_str()); + return parse_after_dur(dur * f, field, last, base); + } + if (field[n] == '+') { + string a_string = field.substr(n + 1); + return dur + parse_dur( + a_string, seq->get_time_map()->beat_to_time( + seq->get_time_map()->time_to_beat(base) + dur)); + } + parse_error(field, n, "Unexpected character in duration"); + return dur; +} + +struct loud_lookup_struct { + char *str; + int val; +} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100}, + {"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60}, + {NULL, 0} }; + + +double Alg_reader::parse_loud(string &field) +{ + char *msg = "Loudness expected"; + if (isdigit(field[1])) { + return parse_int(field); + } else { + string dyn = field.substr(1); + transform(dyn.begin(), dyn.end(), dyn.begin(), ::toupper); + for (int i = 0; loud_lookup[i].str; i++) { + if (streql(loud_lookup[i].str, dyn.c_str())) { + return (double) loud_lookup[i].val; + } + } + } + parse_error(field, 1, msg); + return 100.0; +} + + +int key_lookup[] = {21, 23, 12, 14, 16, 17, 19}; + + +// the field can be K or K[A-G] or P[A-G] +// (this can be called from parse_pitch() to handle [A-G]) +// Notice that the routine ignores the first character: K or P +// +long Alg_reader::parse_key(string &field) +{ + char *msg = "Pitch expected"; + char *pitches = "ABCDEFG"; + char *p; + if (isdigit(field[1])) { + // This routine would not have been called if field = "P" + // so it must be "K" so must be an integer. + return parse_int(field); + } else if (p = strchr(pitches, toupper(field[1]))) { + long key = key_lookup[p - pitches]; + key = parse_after_key(key, field, 2); + return key; + } + parse_error(field, 1, msg); + return 0; +} + + +long Alg_reader::parse_after_key(int key, string &field, int n) +{ + if ((int) field.length() == n) { + return key; + } + char c = toupper(field[n]); + if (c == 'S') { + return parse_after_key(key + 1, field, n + 1); + } + if (c == 'F') { + return parse_after_key(key - 1, field, n + 1); + } + if (isdigit(field[n])) { + int last = find_int_in(field, n); + string octave = field.substr(n, last - n); + int oct = atoi(octave.c_str()); + return parse_after_key(key + oct * 12, field, last); + } + parse_error(field, n, "Unexpected character in pitch"); + return key; +} + + +long Alg_reader::find_int_in(string &field, int n) +{ + while ((int) field.length() > n && isdigit(field[n])) { + n = n + 1; + } + return n; +} + + +bool Alg_reader::parse_attribute(string &field, Alg_parameter_ptr param) +{ + int i = 1; + while (i < (int) field.length()) { + if (field[i] == ':') { + string attr = field.substr(1, i - 1); + char type_char = field[i - 1]; + if (strchr("iarsl", type_char)) { + param->set_attr(symbol_table.insert_string(attr.c_str())); + parse_val(param, field, i + 1); + } else { + parse_error(field, 0, "attribute needs to end with typecode: i,a,r,s, or l"); + } + return !error_flag; + } + i = i + 1; + } + return false; +} + + +bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i) +{ + int len = (int) s.length(); + if (i >= len) { + return false; + } + if (s[i] == '"') { + if (!check_type('s', param)) { + return false; + } + // note: (len - i) includes 2 quote characters but no EOS character + // so total memory to allocate is (len - i) - 1 + char *r = new char[(len - i) - 1]; + strncpy(r, s.c_str() + i + 1, (len - i) - 2); + r[(len - i) - 2] = 0; // terminate the string + param->s = r; + } else if (s[i] == '\'') { + if (!check_type('a', param)) { + return false; + } + string r = s.substr(i + 1, len - i - 2); + param->a = symbol_table.insert_string(r.c_str()); + } else if (param->attr_type() == 'l') { + if (streql(s.c_str() + i, "true") || + streql(s.c_str() + i, "t")) { + param->l = true; + } else if (streql(s.c_str() + i, "false") || + streql(s.c_str() + i, "nil")) { + param->l = false; + } else return false; + } else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') { + int pos = i; + bool period = false; + int sign = 1; + if (s[pos] == '-') { + sign = -1; + pos++; + } + while (pos < len) { + if (isdigit(s[pos])) { + ; + } else if (!period && s[pos] == '.') { + period = true; + } else { + parse_error(s, pos, "Unexpected char in number"); + return false; + } + pos = pos + 1; + } + string r = s.substr(i, len - i); + if (period) { + if (!check_type('r', param)) { + return false; + } + param->r = atof(r.c_str()); + } else { + if (param->attr_type() == 'r') { + param->r = atoi(r.c_str()); + } else if (!check_type('i', param)) { + return false; + } else { + param->i = atoi(r.c_str()); + } + } + } else { + parse_error(s, i, "invalid value"); + return false; + } + return true; +} + + +bool Alg_reader::check_type(char type_char, Alg_parameter_ptr param) +{ + return param->attr_type() == type_char; +} + + +//duration_lookup = {"S": 0.5, "I": 0.5, "Q": 1, "H": 2, "W": 4} +//key_lookup = {"C": 12, "D": 14, "E": 16, "F": 17, "G": 19, "A": 21, "B": 23} + +/* +def test(): + reader = Alg_reader(open("data\\test.gro", "r")) + reader.parse() + score = reader->seq.notes + print "score:", score + reader = nil +*/ diff --git a/plugins/MidiImport/portsmf/allegrosmfrd.cpp b/plugins/MidiImport/portsmf/allegrosmfrd.cpp index 49e2ef03ee6..456fe500048 100644 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfrd.cpp @@ -1,445 +1,455 @@ -// midifile reader - -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "debug.h" -#include -#include -#include "allegro.h" -#include "algsmfrd_internal.h" -#include "mfmidi.h" -#include "trace.h" - -using namespace std; - -typedef class Alg_pending { -public: - Alg_note_ptr note; - class Alg_pending *next; - Alg_pending(Alg_note_ptr n, class Alg_pending *list) { - note = n; next = list; } -} *Alg_pending_ptr; - - -class Alg_midifile_reader: public Midifile_reader { -public: - istream *file; - Alg_seq_ptr seq; - int divisions; - Alg_pending_ptr pending; - Alg_track_ptr track; - int track_number; // the number of the (current) track - // chan is actual_channel + channel_offset_per_track * track_num + - // channel_offset_per_track * port - long channel_offset_per_track; // used to encode track number into channel - // default is 0, set this to 0 to merge all tracks to 16 channels - long channel_offset_per_port; // used to encode port number into channel - // default is 16, set to 0 to ignore port prefix meta events - // while reading, this is channel_offset_per_track * track_num - int channel_offset; - - Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) { - file = &f; - pending = NULL; - seq = new_seq; - channel_offset_per_track = 0; - channel_offset_per_port = 16; - track_number = -1; // no tracks started yet, 1st will be #0 - meta_channel = -1; - port = 0; - } - // delete destroys the seq member as well, so set it to NULL if you - // copied the pointer elsewhere - ~Alg_midifile_reader(); - // the following is used to load the Alg_seq from the file: - bool parse(); - - void set_nomerge(bool flag) { Mf_nomerge = flag; } - void set_skipinit(bool flag) { Mf_skipinit = flag; } - long get_currtime() { return Mf_currtime; } - -protected: - int meta_channel; // the channel for meta events, set by MIDI chan prefix - int port; // value from the portprefix meta event - - double get_time(); - void update(int chan, int key, Alg_parameter_ptr param); - void *Mf_malloc(size_t size) { return malloc(size); } - void Mf_free(void *obj, size_t size) { free(obj); } - /* Methods to be called while processing the MIDI file. */ - void Mf_starttrack(); - void Mf_endtrack(); - int Mf_getc(); - void Mf_chanprefix(int chan); - void Mf_portprefix(int port); - void Mf_eot(); - void Mf_error(const char *); - void Mf_header(int,int,int); - void Mf_on(int,int,int); - void Mf_off(int,int,int); - void Mf_pressure(int,int,int); - void Mf_controller(int,int,int); - void Mf_pitchbend(int,int,int); - void Mf_program(int,int); - void Mf_chanpressure(int,int); - void binary_msg(int len, char *msg, const char *attr_string); - void Mf_sysex(int,char*); - void Mf_arbitrary(int,char*); - void Mf_metamisc(int,int,char*); - void Mf_seqnum(int); - void Mf_smpte(int,int,int,int,int); - void Mf_timesig(int,int,int,int); - void Mf_tempo(int); - void Mf_keysig(int,int); - void Mf_sqspecific(int,char*); - void Mf_text(int,int,char*); -}; - - -Alg_midifile_reader::~Alg_midifile_reader() -{ - while (pending) { - Alg_pending_ptr to_be_freed = pending; - pending = pending->next; - delete to_be_freed; - } - finalize(); // free Mf reader memory -} - - -bool Alg_midifile_reader::parse() -{ - channel_offset = 0; - seq->convert_to_beats(); - midifile(); - seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur())); - return midifile_error != 0; -} - - -void Alg_midifile_reader::Mf_starttrack() -{ - // printf("starting new track\n"); - // create a new track that will share the sequence time map - // since time is in beats, the seconds parameter is false - track_number++; - seq->add_track(track_number); // make sure track exists - track = seq->track(track_number); // keep pointer to current track - meta_channel = -1; - port = 0; -} - - -void Alg_midifile_reader::Mf_endtrack() -{ - // note: track is already part of seq, so do not add it here - // printf("finished track, length %d number %d\n", track->len, track_num / 100); - channel_offset += seq->channel_offset_per_track; - track = NULL; - double now = get_time(); - if (seq->get_beat_dur() < now) seq->set_beat_dur(now); - meta_channel = -1; - port = 0; -} - - -int Alg_midifile_reader::Mf_getc() -{ - return file->get(); -} - - -void Alg_midifile_reader::Mf_chanprefix(int chan) -{ - meta_channel = chan; -} - - -void Alg_midifile_reader::Mf_portprefix(int p) -{ - port = p; -} - - -void Alg_midifile_reader::Mf_eot() -{ - meta_channel = -1; - port = 0; -} - - -void Alg_midifile_reader::Mf_error(const char *msg) -{ - fprintf(stdout, "Midifile reader error: %s\n", msg); -} - - -void Alg_midifile_reader::Mf_header(int format, int ntrks, int division) -{ - if (format > 1) { - char msg[80]; - sprintf(msg, "file format %d not implemented", format); - Mf_error(msg); - } - divisions = division; -} - - -double Alg_midifile_reader::get_time() -{ - double beat = ((double) get_currtime()) / divisions; - return beat; -} - - -void Alg_midifile_reader::Mf_on(int chan, int key, int vel) -{ - assert(!seq->get_units_are_seconds()); - if (vel == 0) { - Mf_off(chan, key, vel); - return; - } - Alg_note_ptr note = new Alg_note(); - pending = new Alg_pending(note, pending); - /* trace("on: %d at %g\n", key, get_time()); */ - note->time = get_time(); - note->chan = chan + channel_offset + port * channel_offset_per_port; - note->dur = 0; - note->set_identifier(key); - note->pitch = (float) key; - note->loud = (float) vel; - track->append(note); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_off(int chan, int key, int vel) -{ - double time = get_time(); - Alg_pending_ptr *p = &pending; - while (*p) { - if ((*p)->note->get_identifier() == key && - (*p)->note->chan == - chan + channel_offset + port * channel_offset_per_port) { - (*p)->note->dur = time - (*p)->note->time; - // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur); - Alg_pending_ptr to_be_freed = *p; - *p = to_be_freed->next; - delete to_be_freed; - } else { - p = &((*p)->next); - } - } - meta_channel = -1; -} - - -void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param) -{ - Alg_update_ptr update = new Alg_update; - update->time = get_time(); - update->chan = chan; - if (chan != -1) { - update->chan = chan + channel_offset + port * channel_offset_per_port; - } - update->set_identifier(key); - update->parameter = *param; - // prevent the destructor from destroying the string twice! - // the new Update takes the string from param - if (param->attr_type() == 's') param->s = NULL; - track->append(update); -} - - -void Alg_midifile_reader::Mf_pressure(int chan, int key, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, key, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_controller(int chan, int control, int val) -{ - Alg_parameter parameter; - char name[32]; - sprintf(name, "control%dr", control); - parameter.set_attr(symbol_table.insert_string(name)); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("bendr")); - parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_program(int chan, int program) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("programi")); - parameter.i = program; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_chanpressure(int chan, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::binary_msg(int len, char *msg, - const char *attr_string) -{ - Alg_parameter parameter; - char *hexstr = new char[len * 2 + 1]; - for (int i = 0; i < len; i++) { - sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); - } - parameter.s = hexstr; - parameter.set_attr(symbol_table.insert_string(attr_string)); - update(meta_channel, -1, ¶meter); -} - - -void Alg_midifile_reader::Mf_sysex(int len, char *msg) -{ - // sysex messages become updates with attribute sysexs and a hex string - binary_msg(len, msg, "sysexs"); -} - - -void Alg_midifile_reader::Mf_arbitrary(int len, char *msg) -{ - Mf_error("arbitrary data ignored"); -} - - -void Alg_midifile_reader::Mf_metamisc(int type, int len, char *msg) -{ - char text[128]; - sprintf(text, "metamsic data, type 0x%x, ignored", type); - Mf_error(text); -} - - -void Alg_midifile_reader::Mf_seqnum(int n) -{ - Mf_error("seqnum data ignored"); -} - - -static const char *fpsstr[4] = {"24", "25", "29.97", "30"}; - -void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, - int frames, int subframes) -{ - // string will look like "24fps:01h:27m:07s:19.00f" - // 30fps (drop frame) is notated as "29.97fps" - char text[32]; - int fps = (hours >> 6) & 3; - hours &= 0x1F; - sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df", - fpsstr[fps], hours, mins, secs, frames, subframes); - Alg_parameter smpteoffset; - smpteoffset.s = heapify(text); - smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); - update(meta_channel, -1, &smpteoffset); - // Mf_error("SMPTE data ignored"); -} - - -void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4) -{ - seq->set_time_sig(get_currtime() / divisions, i1, 1 << i2); -} - - -void Alg_midifile_reader::Mf_tempo(int tempo) -{ - double beat = get_currtime(); - beat = beat / divisions; // convert to quarters - // 6000000 us/min / n us/beat => beat / min - double bpm = 60000000.0 / tempo; - seq->insert_tempo(bpm, beat); -} - - -void Alg_midifile_reader::Mf_keysig(int key, int mode) -{ - Alg_parameter key_parm; - key_parm.set_attr(symbol_table.insert_string("keysigi")); - // use 0 for C major, 1 for G, -1 for F, etc., that is, - // the number of sharps, where flats are negative sharps - key_parm.i = key; //<<<---- fix this - // use -1 to mean "all channels" - update(meta_channel, -1, &key_parm); - Alg_parameter mode_parm; - mode_parm.set_attr(symbol_table.insert_string("modea")); - mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") : - symbol_table.insert_string("minor")); - update(meta_channel, -1, &mode_parm); -} - - -void Alg_midifile_reader::Mf_sqspecific(int len, char *msg) -{ - // sequencer specific messages become updates with attribute sqspecifics - // and a hex string for the value - binary_msg(len, msg, "sqspecifics"); -} - - -char *heapify2(int len, char *s) -{ - char *h = new char[len + 1]; - memcpy(h, s, len); - h[len] = 0; - return h; -} - - -void Alg_midifile_reader::Mf_text(int type, int len, char *msg) -{ - Alg_parameter text; - text.s = heapify2(len, msg); - const char *attr = "miscs"; - if (type == 1) attr = "texts"; - else if (type == 2) attr = "copyrights"; - else if (type == 3) - attr = (track_number == 0 ? "seqnames" : "tracknames"); - else if (type == 4) attr = "instruments"; - else if (type == 5) attr = "lyrics"; - else if (type == 6) attr = "markers"; - else if (type == 7) attr = "cues"; - text.set_attr(symbol_table.insert_string(attr)); - update(meta_channel, -1, &text); -} - - -// parse file into a seq. -Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq) -{ - assert(new_seq); - Alg_midifile_reader ar(file, new_seq); - bool err = ar.parse(); - ar.seq->set_real_dur(ar.seq->get_time_map()-> - beat_to_time(ar.seq->get_beat_dur())); - return (err ? alg_error_syntax : alg_no_error); -} +// midifile reader + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "assert.h" +#include +#include +#include "allegro.h" +#include "algsmfrd_internal.h" +#include "mfmidi.h" +#include "trace.h" + +using namespace std; + +typedef class Alg_note_list { +public: + Alg_note_ptr note; + class Alg_note_list *next; + Alg_note_list(Alg_note_ptr n, class Alg_note_list *list) { + note = n; next = list; } +} *Alg_note_list_ptr; + + +class Alg_midifile_reader: public Midifile_reader { +public: + istream *file; + Alg_seq_ptr seq; + int divisions; + Alg_note_list_ptr note_list; + Alg_track_ptr track; + int track_number; // the number of the (current) track + // chan is actual_channel + channel_offset_per_track * track_num + + // channel_offset_per_track * port + long channel_offset_per_track; // used to encode track number into channel + // default is 0, set this to 0 to merge all tracks to 16 channels + long channel_offset_per_port; // used to encode port number into channel + // default is 16, set to 0 to ignore port prefix meta events + // while reading, this is channel_offset_per_track * track_num + int channel_offset; + + Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) { + file = &f; + note_list = NULL; + seq = new_seq; + channel_offset_per_track = 0; + channel_offset_per_port = 16; + track_number = -1; // no tracks started yet, 1st will be #0 + meta_channel = -1; + port = 0; + } + // delete destroys the seq member as well, so set it to NULL if you + // copied the pointer elsewhere + ~Alg_midifile_reader(); + // the following is used to load the Alg_seq from the file: + bool parse(); + + void set_nomerge(bool flag) { Mf_nomerge = flag; } + void set_skipinit(bool flag) { Mf_skipinit = flag; } + long get_currtime() { return Mf_currtime; } + +protected: + int meta_channel; // the channel for meta events, set by MIDI chan prefix + int port; // value from the portprefix meta event + + double get_time(); + void update(int chan, int key, Alg_parameter_ptr param); + void *Mf_malloc(size_t size) { return malloc(size); } + void Mf_free(void *obj, size_t size) { free(obj); } + /* Methods to be called while processing the MIDI file. */ + void Mf_starttrack(); + void Mf_endtrack(); + int Mf_getc(); + void Mf_chanprefix(int chan); + void Mf_portprefix(int port); + void Mf_eot(); + void Mf_error(char *); + void Mf_header(int,int,int); + void Mf_on(int,int,int); + void Mf_off(int,int,int); + void Mf_pressure(int,int,int); + void Mf_controller(int,int,int); + void Mf_pitchbend(int,int,int); + void Mf_program(int,int); + void Mf_chanpressure(int,int); + void binary_msg(int len, unsigned char *msg, const char *attr_string); + void Mf_sysex(int,unsigned char*); + void Mf_arbitrary(int,unsigned char*); + void Mf_metamisc(int,int,unsigned char*); + void Mf_seqnum(int); + void Mf_smpte(int,int,int,int,int); + void Mf_timesig(int,int,int,int); + void Mf_tempo(int); + void Mf_keysig(int,int); + void Mf_sqspecific(int,unsigned char*); + void Mf_text(int,int,unsigned char*); +}; + + +Alg_midifile_reader::~Alg_midifile_reader() +{ + while (note_list) { + Alg_note_list_ptr to_be_freed = note_list; + note_list = note_list->next; + delete to_be_freed; + } + finalize(); // free Mf reader memory +} + + +bool Alg_midifile_reader::parse() +{ + channel_offset = 0; + seq->convert_to_beats(); + midifile(); + seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur())); + return midifile_error != 0; +} + + +void Alg_midifile_reader::Mf_starttrack() +{ + // printf("starting new track\n"); + // create a new track that will share the sequence time map + // since time is in beats, the seconds parameter is false + track_number++; + seq->add_track(track_number); // make sure track exists + track = seq->track(track_number); // keep pointer to current track + meta_channel = -1; + port = 0; +} + + +void Alg_midifile_reader::Mf_endtrack() +{ + // note: track is already part of seq, so do not add it here + // printf("finished track, length %d number %d\n", track->len, track_num / 100); + channel_offset += seq->channel_offset_per_track; + track = NULL; + double now = get_time(); + if (seq->get_beat_dur() < now) seq->set_beat_dur(now); + meta_channel = -1; + port = 0; +} + + +int Alg_midifile_reader::Mf_getc() +{ + return file->get(); +} + + +void Alg_midifile_reader::Mf_chanprefix(int chan) +{ + meta_channel = chan; +} + + +void Alg_midifile_reader::Mf_portprefix(int p) +{ + port = p; +} + + +void Alg_midifile_reader::Mf_eot() +{ + meta_channel = -1; + port = 0; +} + + +void Alg_midifile_reader::Mf_error(char *msg) +{ + fprintf(stdout, "Midifile reader error: %s\n", msg); +} + + +void Alg_midifile_reader::Mf_header(int format, int ntrks, int division) +{ + if (format > 1) { + char msg[80]; +#pragma warning(disable: 4996) // msg is long enough + sprintf(msg, "file format %d not implemented", format); +#pragma warning(default: 4996) + Mf_error(msg); + } + divisions = division; +} + + +double Alg_midifile_reader::get_time() +{ + double beat = ((double) get_currtime()) / divisions; + return beat; +} + + +void Alg_midifile_reader::Mf_on(int chan, int key, int vel) +{ + assert(!seq->get_units_are_seconds()); + if (vel == 0) { + Mf_off(chan, key, vel); + return; + } + Alg_note_ptr note = new Alg_note(); + note_list = new Alg_note_list(note, note_list); + /* trace("on: %d at %g\n", key, get_time()); */ + note->time = get_time(); + note->chan = chan + channel_offset + port * channel_offset_per_port; + note->dur = 0; + note->set_identifier(key); + note->pitch = (float) key; + note->loud = (float) vel; + track->append(note); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_off(int chan, int key, int vel) +{ + double time = get_time(); + Alg_note_list_ptr *p = ¬e_list; + while (*p) { + if ((*p)->note->get_identifier() == key && + (*p)->note->chan == + chan + channel_offset + port * channel_offset_per_port) { + (*p)->note->dur = time - (*p)->note->time; + // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur); + Alg_note_list_ptr to_be_freed = *p; + *p = to_be_freed->next; + delete to_be_freed; + } else { + p = &((*p)->next); + } + } + meta_channel = -1; +} + + +void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param) +{ + Alg_update_ptr update = new Alg_update; + update->time = get_time(); + update->chan = chan; + if (chan != -1) { + update->chan = chan + channel_offset + port * channel_offset_per_port; + } + update->set_identifier(key); + update->parameter = *param; + // prevent the destructor from destroying the string twice! + // the new Update takes the string from param + if (param->attr_type() == 's') param->s = NULL; + track->append(update); +} + + +void Alg_midifile_reader::Mf_pressure(int chan, int key, int val) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("pressurer")); + parameter.r = val / 127.0; + update(chan, key, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_controller(int chan, int control, int val) +{ + Alg_parameter parameter; + char name[32]; +#pragma warning(disable: 4996) // name is long enough + sprintf(name, "control%dr", control); +#pragma warning(default: 4996) + parameter.set_attr(symbol_table.insert_string(name)); + parameter.r = val / 127.0; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("bendr")); + parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_program(int chan, int program) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("programi")); + parameter.i = program; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::Mf_chanpressure(int chan, int val) +{ + Alg_parameter parameter; + parameter.set_attr(symbol_table.insert_string("pressurer")); + parameter.r = val / 127.0; + update(chan, -1, ¶meter); + meta_channel = -1; +} + + +void Alg_midifile_reader::binary_msg(int len, unsigned char *msg, + const char *attr_string) +{ + Alg_parameter parameter; + char *hexstr = new char[len * 2 + 1]; + for (int i = 0; i < len; i++) { +#pragma warning(disable: 4996) // hexstr is long enough + sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); +#pragma warning(default: 4996) + } + parameter.s = hexstr; + parameter.set_attr(symbol_table.insert_string(attr_string)); + update(meta_channel, -1, ¶meter); +} + + +void Alg_midifile_reader::Mf_sysex(int len, unsigned char *msg) +{ + // sysex messages become updates with attribute sysexs and a hex string + binary_msg(len, msg, "sysexs"); +} + + +void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg) +{ + Mf_error("arbitrary data ignored"); +} + + +void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg) +{ + char text[128]; +#pragma warning(disable: 4996) // text is long enough + sprintf(text, "metamsic data, type 0x%x, ignored", type); +#pragma warning(default: 4996) + Mf_error(text); +} + + +void Alg_midifile_reader::Mf_seqnum(int n) +{ + Mf_error("seqnum data ignored"); +} + + +static char *fpsstr[4] = {"24", "25", "29.97", "30"}; + +void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, + int frames, int subframes) +{ + // string will look like "24fps:01h:27m:07s:19.00f" + // 30fps (drop frame) is notated as "29.97fps" + char text[32]; + int fps = (hours >> 6) & 3; + hours &= 0x1F; +#pragma warning(disable: 4996) // text is long enough + sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df", + fpsstr[fps], hours, mins, secs, frames, subframes); +#pragma warning(default: 4996) + Alg_parameter smpteoffset; + smpteoffset.s = heapify(text); + smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); + update(meta_channel, -1, &smpteoffset); + // Mf_error("SMPTE data ignored"); +} + + +void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4) +{ + seq->set_time_sig(double(get_currtime()) / divisions, i1, 1 << i2); +} + + +void Alg_midifile_reader::Mf_tempo(int tempo) +{ + double beat = get_currtime(); + beat = beat / divisions; // convert to quarters + // 6000000 us/min / n us/beat => beat / min + double bpm = 60000000.0 / tempo; + seq->insert_tempo(bpm, beat); +} + + +void Alg_midifile_reader::Mf_keysig(int key, int mode) +{ + Alg_parameter key_parm; + key_parm.set_attr(symbol_table.insert_string("keysigi")); + // use 0 for C major, 1 for G, -1 for F, etc., that is, + // the number of sharps, where flats are negative sharps + key_parm.i = key; //<<<---- fix this + // use -1 to mean "all channels" + update(meta_channel, -1, &key_parm); + Alg_parameter mode_parm; + mode_parm.set_attr(symbol_table.insert_string("modea")); + mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") : + symbol_table.insert_string("minor")); + update(meta_channel, -1, &mode_parm); +} + + +void Alg_midifile_reader::Mf_sqspecific(int len, unsigned char *msg) +{ + // sequencer specific messages become updates with attribute sqspecifics + // and a hex string for the value + binary_msg(len, msg, "sqspecifics"); +} + + +char *heapify2(int len, unsigned char *s) +{ + char *h = new char[len + 1]; + memcpy(h, s, len); + h[len] = 0; + return h; +} + + +void Alg_midifile_reader::Mf_text(int type, int len, unsigned char *msg) +{ + Alg_parameter text; + text.s = heapify2(len, msg); + const char *attr = "miscs"; + if (type == 1) attr = "texts"; + else if (type == 2) attr = "copyrights"; + else if (type == 3) + attr = (track_number == 0 ? "seqnames" : "tracknames"); + else if (type == 4) attr = "instruments"; + else if (type == 5) attr = "lyrics"; + else if (type == 6) attr = "markers"; + else if (type == 7) attr = "cues"; + text.set_attr(symbol_table.insert_string(attr)); + update(meta_channel, -1, &text); +} + + +// parse file into a seq. +Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq) +{ + assert(new_seq); + Alg_midifile_reader ar(file, new_seq); + bool err = ar.parse(); + ar.seq->set_real_dur(ar.seq->get_time_map()-> + beat_to_time(ar.seq->get_beat_dur())); + return (err ? alg_error_syntax : alg_no_error); +} diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp index 5a76c44ed9f..f58183173d2 100644 --- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfwr.cpp @@ -34,7 +34,7 @@ class Alg_smf_write { // chan is actual_channel + channels_per_track * track_number // default is 100, set this to 0 to merge all tracks to 16 channels - void write(ofstream &file /* , midiFileFormat = 1 */); + void write(ostream &file /* , midiFileFormat = 1 */); private: long previous_divs; // time in ticks of most recently written event @@ -46,7 +46,7 @@ class Alg_smf_write { void write_note(Alg_note_ptr note, bool on); void write_update(Alg_update_ptr update); void write_text(Alg_update_ptr update, char type); - void write_binary(int type_byte, char *msg); + void write_binary(int type_byte, const char *msg); void write_midi_channel_prefix(Alg_update_ptr update); void write_smpteoffset(Alg_update_ptr update, char *s); void write_data(int data); @@ -162,7 +162,7 @@ void Alg_smf_write::write_note(Alg_note_ptr note, bool on) //printf("deltaDivisions: %d, beats elapsed: %g, on? %c\n", deltaDivisions, note->time, on); - char chan = (note->chan & 15); + char chan = char(note->chan & 15); int pitch = int(note->pitch + 0.5); if (pitch < 0) { pitch = pitch % 12; @@ -184,8 +184,8 @@ void Alg_smf_write::write_midi_channel_prefix(Alg_update_ptr update) { if (update->chan >= 0) { // write MIDI Channel Prefix write_delta(update->time); - out_file->put(0xFF); // Meta Event - out_file->put(0x20); // Type code for MIDI Channel Prefix + out_file->put('\xFF'); // Meta Event + out_file->put('\x20'); // Type code for MIDI Channel Prefix out_file->put(1); // length out_file->put(to_midi_channel(update->chan)); // one thing odd about the Std MIDI File spec is that once @@ -201,7 +201,7 @@ void Alg_smf_write::write_text(Alg_update_ptr update, char type) { write_midi_channel_prefix(update); write_delta(update->time); - out_file->put(0xFF); + out_file->put('\xFF'); out_file->put(type); out_file->put((char) strlen(update->parameter.s)); *out_file << update->parameter.s; @@ -212,8 +212,8 @@ void Alg_smf_write::write_smpteoffset(Alg_update_ptr update, char *s) { write_midi_channel_prefix(update); write_delta(update->time); - out_file->put(0xFF); // meta event - out_file->put(0x54); // smpte offset type code + out_file->put('\xFF'); // meta event + out_file->put('\x54'); // smpte offset type code out_file->put(5); // length for (int i = 0; i < 5; i++) *out_file << s[i]; } @@ -255,13 +255,13 @@ static char hex_to_nibble(char c) } -static char hex_to_char(char *s) +static char hex_to_char(const char *s) { return (hex_to_nibble(s[0]) << 4) + hex_to_nibble(s[1]); } -void Alg_smf_write::write_binary(int type_byte, char *msg) +void Alg_smf_write::write_binary(int type_byte, const char *msg) { int len = strlen(msg) / 2; out_file->put(type_byte); @@ -275,7 +275,7 @@ void Alg_smf_write::write_binary(int type_byte, char *msg) void Alg_smf_write::write_update(Alg_update_ptr update) { - char *name = update->parameter.attr_name(); + const char *name = update->parameter.attr_name(); /****Non-Meta Events****/ if (!strcmp(name, "pressurer")) { @@ -312,7 +312,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) write_data(val); } else if (!strcmp(name, "sysexs") && update->parameter.attr_type() == 's') { - char *s = update->parameter.s; + const char *s = update->parameter.s; if (s[0] && s[1] && toupper(s[0]) == 'F' && s[1] == '0') { s += 2; // skip the initial "F0" byte in message: it is implied } @@ -320,9 +320,9 @@ void Alg_smf_write::write_update(Alg_update_ptr update) write_binary(0xF0, s); } else if (!strcmp(name, "sqspecifics") && update->parameter.attr_type() == 's') { - char *s = update->parameter.s; + const char *s = update->parameter.s; write_delta(update->time); - out_file->put(0xFF); + out_file->put('\xFF'); write_binary(0x7F, s); /****Text Events****/ @@ -349,11 +349,11 @@ void Alg_smf_write::write_update(Alg_update_ptr update) // smpteoffset is specified as "24fps:00h:10m:00s:11.00f" // the following simple parser does not reject all badly // formatted strings, but it should parse good strings ok - char *s = update->parameter.s; + const char *s = update->parameter.s; int len = strlen(s); char smpteoffset[5]; if (len < 24) return; // not long enough, must be bad format - int fps = 0; + int fps; if (s[0] == '2') { if (s[1] == '4') fps = 0; else if (s[1] == '5') fps = 1; @@ -390,8 +390,8 @@ void Alg_smf_write::write_update(Alg_update_ptr update) } if (keysig != -99 && keysig_mode) { // write when both are defined write_delta(keysig_when); - out_file->put(0xFF); - out_file->put(0x59); + out_file->put('\xFF'); + out_file->put('\x59'); out_file->put(2); // mask off high bits so that this value appears to be positive // i.e. -1 -> 0xFF (otherwise, write_data will clip -1 to 0) @@ -482,9 +482,9 @@ void Alg_smf_write::write_tempo(int divs, int tempo) // printf("Inserting tempo %f after %f clocks.\n", tempo, delta); write_varinum(divs - previous_divs); previous_divs = divs; - out_file->put(0xFF); - out_file->put(0x51); - out_file->put(0x03); + out_file->put('\xFF'); + out_file->put('\x51'); + out_file->put('\x03'); write_24bit((int)tempo); } @@ -512,12 +512,11 @@ void Alg_smf_write::write_tempo_change(int i) void Alg_smf_write::write_time_signature(int i) { Alg_time_sigs &ts = seq->time_sig; + write_delta(ts[i].beat); // write the time signature - long divs = ROUND(ts[i].beat * division); - write_varinum(divs - previous_divs); - out_file->put(0xFF); - out_file->put(0x58); // time signature - out_file->put(4); // length of message + out_file->put('\xFF'); + out_file->put('\x58'); // time signature + out_file->put('\x04'); // length of message out_file->put(ROUND(ts[i].num)); int den = ROUND(ts[i].den); int den_byte = 0; @@ -532,7 +531,7 @@ void Alg_smf_write::write_time_signature(int i) -void Alg_smf_write::write(ofstream &file) +void Alg_smf_write::write(ostream &file) { int track_len_offset; int track_end_offset; @@ -564,9 +563,9 @@ void Alg_smf_write::write(ofstream &file) // End of track event write_varinum(0); // delta time - out_file->put(0xFF); - out_file->put(0x2F); - out_file->put(0x00); + out_file->put('\xFF'); + out_file->put('\x2F'); + out_file->put('\x00'); // Go back and write in the length of the track track_end_offset = out_file->tellp(); @@ -632,7 +631,7 @@ void Alg_smf_write::write_varinum(int value) } -void Alg_seq::smf_write(ofstream &file) +void Alg_seq::smf_write(ostream &file) { Alg_smf_write writer(this); writer.write(file); @@ -646,4 +645,3 @@ bool Alg_seq::smf_write(const char *filename) outf.close(); return true; } - diff --git a/plugins/MidiImport/portsmf/allegrowr.cpp b/plugins/MidiImport/portsmf/allegrowr.cpp index 3b266f84cac..3142cfd7307 100644 --- a/plugins/MidiImport/portsmf/allegrowr.cpp +++ b/plugins/MidiImport/portsmf/allegrowr.cpp @@ -1,6 +1,6 @@ // allegrowr.cpp -- write sequence to an Allegro file (text) -#include "debug.h" +#include "assert.h" #include "stdlib.h" #include #include @@ -56,32 +56,34 @@ Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n, // find a name and write it, return a pointer to it so the track // writer knows what update (if any) to skip { - Alg_event_ptr e = NULL; + Alg_event_ptr e = NULL; // e is the result, default is NULL file << "#track " << n; const char *attr = symbol_table.insert_string( n == 0 ? "seqnames" : "tracknames"); // search for name in events with timestamp of 0 for (int i = 0; i < events.length(); i++) { - e = events[i]; - if (e->time > 0) break; - if (e->is_update()) { - Alg_update_ptr u = (Alg_update_ptr) e; + Alg_event_ptr ue = events[i]; + if (ue->time > 0) break; + if (ue->is_update()) { + Alg_update_ptr u = (Alg_update_ptr) ue; if (u->parameter.attr == attr) { file << " " << u->parameter.s; + e = ue; // return the update event we found break; } } } - file << endl; - return e; + file << endl; // end of line containing #track [] + return e; // return parameter event with name if one was found } -void Alg_seq::write(ostream &file, bool in_secs) +void Alg_seq::write(ostream &file, bool in_secs, double offset) { int i, j; if (in_secs) convert_to_seconds(); else convert_to_beats(); + file << "#offset " << offset << endl; Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]); Alg_beats &beats = time_map->beats; for (i = 0; i < beats.len - 1; i++) { @@ -171,11 +173,11 @@ void Alg_seq::write(ostream &file, bool in_secs) } } -bool Alg_seq::write(const char *filename) +bool Alg_seq::write(const char *filename, double offset) { ofstream file(filename); if (file.fail()) return false; - write(file, units_are_seconds); - file.close(); - return true; + write(file, units_are_seconds, offset); + file.close(); + return true; } diff --git a/plugins/MidiImport/portsmf/mfmidi.cpp b/plugins/MidiImport/portsmf/mfmidi.cpp index 52f93b83764..eba292f8d4f 100644 --- a/plugins/MidiImport/portsmf/mfmidi.cpp +++ b/plugins/MidiImport/portsmf/mfmidi.cpp @@ -13,6 +13,7 @@ #include "stdio.h" #include "mfmidi.h" #include "string.h" +#include "assert.h" #define MIDIFILE_ERROR -1 @@ -34,15 +35,16 @@ void Midifile_reader::midifile() while (ntrks-- > 0 && !midifile_error) readtrack(); } -int Midifile_reader::readmt(const char *s, int skip) +int Midifile_reader::readmt(char *s, int skip) /* read through the "MThd" or "MTrk" header string */ /* if skip == 1, we attempt to skip initial garbage. */ { + assert(strlen(s) == 4); // must be "MThd" or "MTrk" int nread = 0; char b[4]; char buff[32]; int c; - const char *errmsg = "expecting "; + char *errmsg = "expecting "; retry: while ( nread<4 ) { @@ -66,8 +68,10 @@ int Midifile_reader::readmt(const char *s, int skip) goto retry; } err: +#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths (void) strcpy(buff,errmsg); (void) strcat(buff,s); +#pragma warning(default: 4996) // turn it back on mferror(buff); return(0); } @@ -189,7 +193,7 @@ void Midifile_reader::readtrack() msginit(); while ( Mf_toberead > lookfor ) { - char c = egetc(); + unsigned char c = egetc(); if (midifile_error) return; msgadd(c); } @@ -253,15 +257,17 @@ void Midifile_reader::readtrack() void Midifile_reader::badbyte(int c) { char buff[32]; - +#pragma warning(disable: 4996) // safe in this case (void) sprintf(buff,"unexpected byte: 0x%02x",c); +#pragma warning(default: 4996) mferror(buff); } void Midifile_reader::metaevent(int type) { int leng = msgleng(); - char *m = msg(); + // made this unsigned to avoid sign extend + unsigned char *m = msg(); switch ( type ) { case 0x00: @@ -408,7 +414,7 @@ int Midifile_reader::read16bit() return to16bit(c1,c2); } -void Midifile_reader::mferror(const char *s) +void Midifile_reader::mferror(char *s) { Mf_error(s); midifile_error = 1; @@ -444,7 +450,7 @@ void Midifile_reader::msginit() Msgindex = 0; } -char *Midifile_reader::msg() +unsigned char *Midifile_reader::msg() { return(Msgbuff); } @@ -464,21 +470,16 @@ void Midifile_reader::msgadd(int c) void Midifile_reader::msgenlarge() { - char *newmess; - char *oldmess = Msgbuff; + unsigned char *newmess; + unsigned char *oldmess = Msgbuff; int oldleng = Msgsize; Msgsize += MSGINCREMENT; - newmess = (char *) Mf_malloc((sizeof(char) * Msgsize) ); + newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) ); /* copy old message into larger new one */ if ( oldmess != 0 ) { - register char *p = newmess; - register char *q = oldmess; - register char *endq = &oldmess[oldleng]; - - for ( ; q!=endq ; p++,q++ ) - *p = *q; + memcpy(newmess, oldmess, oldleng); Mf_free(oldmess, oldleng); } Msgbuff = newmess; diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h index d0049294bc2..4c862029cf9 100644 --- a/plugins/MidiImport/portsmf/mfmidi.h +++ b/plugins/MidiImport/portsmf/mfmidi.h @@ -46,7 +46,7 @@ class Midifile_reader { virtual void Mf_chanprefix(int) = 0; virtual void Mf_portprefix(int) = 0; virtual void Mf_eot() = 0; - virtual void Mf_error(const char *) = 0; + virtual void Mf_error(char *) = 0; virtual void Mf_header(int,int,int) = 0; virtual void Mf_on(int,int,int) = 0; virtual void Mf_off(int,int,int) = 0; @@ -55,16 +55,16 @@ class Midifile_reader { virtual void Mf_pitchbend(int,int,int) = 0; virtual void Mf_program(int,int) = 0; virtual void Mf_chanpressure(int,int) = 0; - virtual void Mf_sysex(int,char*) = 0; - virtual void Mf_arbitrary(int,char*) = 0; - virtual void Mf_metamisc(int,int,char*) = 0; + virtual void Mf_sysex(int,unsigned char*) = 0; + virtual void Mf_arbitrary(int,unsigned char*) = 0; + virtual void Mf_metamisc(int,int,unsigned char*) = 0; virtual void Mf_seqnum(int) = 0; virtual void Mf_smpte(int,int,int,int,int) = 0; virtual void Mf_timesig(int,int,int,int) = 0; virtual void Mf_tempo(int) = 0; virtual void Mf_keysig(int,int) = 0; - virtual void Mf_sqspecific(int,char*) = 0; - virtual void Mf_text(int,int,char*) = 0; + virtual void Mf_sqspecific(int,unsigned char*) = 0; + virtual void Mf_text(int,int,unsigned char*) = 0; private: long Mf_toberead; @@ -73,7 +73,7 @@ class Midifile_reader { long read32bit(); int read16bit(); void msgenlarge(); - char *msg(); + unsigned char *msg(); int readheader(); void readtrack(); void sysex(); @@ -81,16 +81,16 @@ class Midifile_reader { int egetc(); int msgleng(); - int readmt(const char *, int); + int readmt(char*,int); long to32bit(int,int,int,int); int to16bit(int,int); - void mferror(const char *); + void mferror(char *); void badbyte(int); void metaevent(int); void msgadd(int); void chanmessage(int,int,int); - char *Msgbuff; + unsigned char *Msgbuff; long Msgsize; long Msgindex; }; diff --git a/plugins/MidiImport/portsmf/strparse.cpp b/plugins/MidiImport/portsmf/strparse.cpp index 7665b4ae058..592a21d62f4 100644 --- a/plugins/MidiImport/portsmf/strparse.cpp +++ b/plugins/MidiImport/portsmf/strparse.cpp @@ -1,5 +1,5 @@ -#include #include +#include // #include -- for debugging (cout) #include "ctype.h" using namespace std; @@ -48,10 +48,10 @@ void String_parse::get_nonspace_quoted(string &field) } -char *escape_chars[] = { (char *) "\\n", (char *)"\\t", (char *)"\\\\", (char *)"\\r", (char *) "\\\""}; +static const char *const escape_chars[] = {"\\n", "\\t", "\\\\", "\\r", "\\\""}; -void string_escape(string &result, char *str, const char *quote) +void string_escape(string &result, const char *str, const char *quote) { int length = (int) strlen(str); if (quote[0]) { @@ -59,8 +59,8 @@ void string_escape(string &result, char *str, const char *quote) } for (int i = 0; i < length; i++) { if (!isalnum((unsigned char) str[i])) { - char *chars = (char *)"\n\t\\\r\""; - char *special = strchr(chars, str[i]); + const char *const chars = "\n\t\\\r\""; + const char *const special = strchr(chars, str[i]); if (special) { result.append(escape_chars[special - chars]); } else { @@ -78,7 +78,7 @@ void String_parse::get_remainder(std::string &field) field.clear(); skip_space(); int len = str->length() - pos; - if ((*str)[len - 1] == '\n') { // if str ends in newline, + if ((len > 0) && ((*str)[len - 1] == '\n')) { // if str ends in newline, len--; // reduce length to ignore newline } field.insert(0, *str, pos, len); diff --git a/plugins/MidiImport/portsmf/strparse.h b/plugins/MidiImport/portsmf/strparse.h index 74f01591974..0c64b07b934 100644 --- a/plugins/MidiImport/portsmf/strparse.h +++ b/plugins/MidiImport/portsmf/strparse.h @@ -15,4 +15,4 @@ class String_parse { void get_remainder(std::string &field); }; -void string_escape(std::string &result, char *s, const char *quote); +void string_escape(std::string &result, const char *s, const char *quote); diff --git a/plugins/MidiImport/portsmf/trace.cpp b/plugins/MidiImport/portsmf/trace.cpp index 7c1999db570..38c050fe466 100644 --- a/plugins/MidiImport/portsmf/trace.cpp +++ b/plugins/MidiImport/portsmf/trace.cpp @@ -15,7 +15,7 @@ void trace(char *format, ...) char msg[256]; va_list args; va_start(args, format); - _vsnprintf(msg, 256, format, args); + _vsnprintf_s(msg, 256, _TRUNCATE, format, args); va_end(args); #ifdef _DEBUG _CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, msg); From 9eb787c9e81f955d041a987e94f23f728dfcdb8e Mon Sep 17 00:00:00 2001 From: Veratil Date: Sun, 23 Aug 2020 16:15:25 -0500 Subject: [PATCH 077/180] Update MIDI loading and parsing portsmf fixes: * Fix allegro warnings as errors * Fix msvc missing max. Use MAX macro instead --- plugins/MidiImport/MidiImport.cpp | 49 ++++++++++---------- plugins/MidiImport/portsmf/allegro.cpp | 40 +++++++++-------- plugins/MidiImport/portsmf/allegro.h | 33 +++++++------- plugins/MidiImport/portsmf/allegrord.cpp | 50 ++++++++++++--------- plugins/MidiImport/portsmf/allegrosmfrd.cpp | 28 +++++++----- plugins/MidiImport/portsmf/allegrosmfwr.cpp | 2 +- plugins/MidiImport/portsmf/mfmidi.cpp | 17 ++++--- plugins/MidiImport/portsmf/mfmidi.h | 3 +- 8 files changed, 123 insertions(+), 99 deletions(-) diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index f183fb418f2..8b2aa6117d6 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "MidiImport.h" #include "TrackContainer.h" @@ -305,7 +306,7 @@ class smfMidiChannel bool MidiImport::readSMF( TrackContainer* tc ) { - + const int MIDI_CC_COUNT = 128 + 1; // 0-127 (128) + pitch bend const int preTrackSteps = 2; QProgressDialog pd( TrackContainer::tr( "Importing MIDI-file..." ), TrackContainer::tr( "Cancel" ), 0, preTrackSteps, gui->mainWindow() ); @@ -315,10 +316,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) pd.setValue( 0 ); - std::stringstream stream; - QByteArray arr = readAllData(); - stream.str(std::string(arr.constData(), arr.size())); - + std::istringstream stream(readAllData().toStdString()); Alg_seq_ptr seq = new Alg_seq(stream, true); seq->convert_to_beats(); @@ -326,8 +324,12 @@ bool MidiImport::readSMF( TrackContainer* tc ) pd.setValue( 1 ); // 128 CC + Pitch Bend - smfMidiCC ccs[129]; - smfMidiChannel chs[256]; + smfMidiCC ccs[MIDI_CC_COUNT]; + + // channels can be set out of 256 range + // using unordered_map should fix most invalid loads and crashes while loading + std::unordered_map chs; + // NOTE: unordered_map::operator[] creates a new element if none exists MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); AutomationTrack * nt = dynamic_cast( @@ -407,7 +409,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) Alg_track_ptr trk = seq->track( t ); pd.setValue( t + preTrackSteps ); - for( int c = 0; c < 129; c++ ) + for( int c = 0; c < MIDI_CC_COUNT; c++ ) { ccs[c].clear(); } @@ -423,7 +425,10 @@ bool MidiImport::readSMF( TrackContainer* tc ) if( evt->is_update() ) { QString attr = evt->get_attribute(); - if( attr == "tracknames" && evt->get_update_type() == 's' ) { + // seqnames is a track0 identifier (see allegro code) + if (attr == (t == 0 ? "seqnames" : "tracknames") + && evt->get_update_type() == 's') + { trackName = evt->get_string_value(); handled = true; } @@ -444,7 +449,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) printf( "\n" ); } } - else if( evt->is_note() && evt->chan < 256 ) + else if (evt->is_note()) { smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); Alg_note_ptr noteEvt = dynamic_cast( evt ); @@ -558,28 +563,26 @@ bool MidiImport::readSMF( TrackContainer* tc ) delete seq; - for( int c=0; c < 256; ++c ) + for( auto& c: chs ) { - if (chs[c].hasNotes) + if (c.second.hasNotes) { - chs[c].splitPatterns(); + c.second.splitPatterns(); } - else if (chs[c].it) + else if (c.second.it) { printf(" Should remove empty track\n"); // must delete trackView first - but where is it? //tc->removeTrack( chs[c].it ); //it->deleteLater(); } - } - - // Set channel 10 to drums as per General MIDI's orders - if( chs[9].hasNotes && chs[9].it_inst && chs[9].isSF2 ) - { - // AFAIK, 128 should be the standard bank for drums in SF2. - // If not, this has to be made configurable. - chs[9].it_inst->childModel( "bank" )->setValue( 128 ); - chs[9].it_inst->childModel( "patch" )->setValue( 0 ); + // Set channel 10 to drums as per General MIDI's orders + if (c.first % 16l == 9 /* channel 10 */ + && c.second.hasNotes && c.second.it_inst && c.second.isSF2) + { + c.second.it_inst->childModel("bank")->setValue(128); + c.second.it_inst->childModel("patch")->setValue(0); + } } return true; diff --git a/plugins/MidiImport/portsmf/allegro.cpp b/plugins/MidiImport/portsmf/allegro.cpp index 3c5a2a5a575..5664e910eca 100644 --- a/plugins/MidiImport/portsmf/allegro.cpp +++ b/plugins/MidiImport/portsmf/allegro.cpp @@ -30,7 +30,7 @@ using namespace std; // 4311 is type cast ponter to long warning // 4996 is warning against strcpy // 4267 is size_t to long warning -#pragma warning(disable: 4311 4996 4267) +//#pragma warning(disable: 4311 4996 4267) Alg_atoms symbol_table; Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables Serial_write_buffer Alg_track::ser_write_buf; @@ -720,7 +720,7 @@ Alg_event_list::Alg_event_list(Alg_track *owner) } -Alg_event_ptr &Alg_event_list::operator [](int i) +Alg_event_ptr const &Alg_event_list::operator [](int i) { assert(i >= 0 && i < len); return events[i]; @@ -739,8 +739,8 @@ void Alg_event_list::set_start_time(Alg_event *event, double t) // For Alg_track, change the time and move the event to the right place // For Alg_seq, find the track and do the update there - long index, i; - Alg_track_ptr track_ptr; + long index = 0, i; + Alg_track_ptr track_ptr = nullptr; if (type == 'e') { // this is an Alg_event_list // make sure the owner has not changed its event set assert(events_owner && @@ -1522,7 +1522,7 @@ Alg_track *Alg_track::unserialize(void *buffer, long len) bool alg = ser_read_buf.get_char() == 'A' && ser_read_buf.get_char() == 'L' && ser_read_buf.get_char() == 'G'; - assert(alg); + assert(alg); (void)alg; // unused variable char c = ser_read_buf.get_char(); if (c == 'S') { Alg_seq *seq = new Alg_seq; @@ -1539,7 +1539,7 @@ Alg_track *Alg_track::unserialize(void *buffer, long len) } -#pragma warning(disable: 4800) // long to bool performance warning +//#pragma warning(disable: 4800) // long to bool performance warning /* Note: this Alg_seq must have a default initialized Alg_time_map. * It will be filled in with data from the ser_read_buf buffer. @@ -1551,9 +1551,9 @@ void Alg_seq::unserialize_seq() (ser_read_buf.get_char() == 'L') && (ser_read_buf.get_char() == 'G') && (ser_read_buf.get_char() == 'S'); - assert(algs); + assert(algs); (void)algs; // unused variable long len = ser_read_buf.get_int32(); - assert(ser_read_buf.get_len() >= len); + assert(ser_read_buf.get_len() >= len); (void)len; // unused variable channel_offset_per_track = ser_read_buf.get_int32(); units_are_seconds = ser_read_buf.get_int32() != 0; beat_dur = ser_read_buf.get_double(); @@ -1599,10 +1599,10 @@ void Alg_track::unserialize_track() (ser_read_buf.get_char() == 'L') && (ser_read_buf.get_char() == 'G') && (ser_read_buf.get_char() == 'T'); - assert(algt); + assert(algt); (void)algt; // unused variable long offset = ser_read_buf.get_posn(); // stored length does not include 'ALGT' long bytes = ser_read_buf.get_int32(); - assert(bytes <= ser_read_buf.get_len() - offset); + assert(bytes <= ser_read_buf.get_len() - offset); (void)offset; (void)bytes; // unused variable units_are_seconds = (bool) ser_read_buf.get_int32(); beat_dur = ser_read_buf.get_double(); real_dur = ser_read_buf.get_double(); @@ -1672,7 +1672,7 @@ void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) } } -#pragma warning(default: 4800) +//#pragma warning(default: 4800) void Alg_track::set_time_map(Alg_time_map *map) { @@ -1840,6 +1840,7 @@ void Alg_track::paste(double t, Alg_event_list *seq) bool prev_units_are_seconds; if (seq->get_type() == 'e') { assert(seq->get_owner()->get_units_are_seconds() == units_are_seconds); + prev_units_are_seconds = seq->get_owner()->get_units_are_seconds(); } else { // make it match Alg_track_ptr tr = (Alg_track_ptr) seq; prev_units_are_seconds = tr->get_units_are_seconds(); @@ -2381,14 +2382,14 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) double num_of_insert = 4.0; double den_of_insert = 4.0; double beat_of_insert = 0.0; - int first_from_index = 0; // where to start copying from + /* int first_from_index = 0; // where to start copying from TODO: LMMS commented out unused variable */ if (from.length() > 0 && from[0].beat < ALG_EPS) { // there is an initial time signature in "from" num_of_insert = from[0].num; den_of_insert = from[0].den; // since we are handling the first time signature in from, // we can start copying at index == 1: - first_from_index = 1; + /* first_from_index = 1; TODO: LMMS commented out unused variable */ } // compare time signatures to see if we need a change at start: if (num_before_splice != num_of_insert || @@ -2431,14 +2432,14 @@ void Alg_time_sigs::paste(double start, Alg_seq *seq) double measures = (start - beat_after_splice) / beats_per_measure; // Measures might be slightly negative due to rounding. Use max() // to eliminate any negative rounding error: - int imeasures = int(max(measures, 0.0)); + int imeasures = int(MAX(measures, 0.0)); double old_bar_loc = beat_after_splice + (imeasures * beats_per_measure); if (old_bar_loc < start) old_bar_loc += beats_per_measure; // now old_bar_loc is the original first bar position after start // Do similar calculation for position after end after the insertion: // beats_per_measure already calculated because signatures match measures = (start + dur - beat_of_insert) / beats_per_measure; - imeasures = int(max(measures, 0.0)); + imeasures = int(MAX(measures, 0.0)); double new_bar_loc = beat_of_insert + (imeasures * beats_per_measure); if (new_bar_loc < start + dur) new_bar_loc += beats_per_measure; // old_bar_loc should be shifted by dur: @@ -2864,9 +2865,9 @@ Alg_track_ptr Alg_seq::track(int i) return &(track_list[i]); } -#pragma warning(disable: 4715) // ok not to return a value here +//#pragma warning(disable: 4715) // ok not to return a value here -Alg_event_ptr &Alg_seq::operator[](int i) +Alg_event_ptr const &Alg_seq::operator[](int i) { int ntracks = track_list.length(); int tr = 0; @@ -2880,8 +2881,9 @@ Alg_event_ptr &Alg_seq::operator[](int i) tr++; } assert(false); // out of bounds + return NULL; } -#pragma warning(default: 4715) +//#pragma warning(default: 4715) void Alg_seq::convert_to_beats() @@ -3044,7 +3046,7 @@ void Alg_seq::insert_silence(double t, double len) // Final duration is defined to be t + len + whatever was // in the sequence after t (if any). This translates to // t + len + max(dur - t, 0) - set_dur(t + len + max(get_dur() - t, 0.0)); + set_dur(t + len + MAX(get_dur() - t, 0.0)); } diff --git a/plugins/MidiImport/portsmf/allegro.h b/plugins/MidiImport/portsmf/allegro.h index 7c99eb7a730..ed684fd8a88 100644 --- a/plugins/MidiImport/portsmf/allegro.h +++ b/plugins/MidiImport/portsmf/allegro.h @@ -391,7 +391,7 @@ typedef class Alg_event_list : public Alg_events { // When applied to an Alg_seq, events are enumerated track // by track with increasing indices. This operation is not // particularly fast on an Alg_seq. - virtual Alg_event_ptr &operator[](int i); + virtual Alg_event_ptr const &operator[](int i); Alg_event_list() { sequence_number = 0; beat_dur = 0.0; real_dur = 0.0; events_owner = NULL; type = 'e'; } Alg_event_list(Alg_track *owner); @@ -549,12 +549,12 @@ typedef class Serial_read_buffer : public Serial_buffer { // does nothing. virtual ~Serial_read_buffer() { } #if defined(_WIN32) -#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits -#pragma warning(disable: 4311) // type cast pointer to long warning +//#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits +//#pragma warning(disable: 4311) // type cast pointer to long warning #endif - void get_pad() { while (((long) ptr) & 7) ptr++; } + void get_pad() { while ((intptr_t) ptr & 7) ptr++; } #if defined(_WIN32) -#pragma warning(default: 4311 546) +//#pragma warning(default: 4311 546) #endif // Prepare to read n bytes from buf. The caller must manage buf: it is // valid until reading is finished, and it is caller's responsibility @@ -571,7 +571,7 @@ typedef class Serial_read_buffer : public Serial_buffer { double get_double() { double d = *((double *) ptr); ptr += sizeof(double); return d; } const char *get_string() { char *s = ptr; char *fence = buffer + len; - assert(ptr < fence); + assert(ptr < fence); (void)fence; // unused variable while (*ptr++) assert(ptr < fence); get_pad(); return s; } @@ -600,18 +600,18 @@ typedef class Serial_write_buffer: public Serial_buffer { void check_buffer(long needed); void set_string(const char *s) { char *fence = buffer + len; - assert(ptr < fence); + assert(ptr < fence); (void)fence; // unused variable // two brackets surpress a g++ warning, because this is an // assignment operator inside a test. while ((*ptr++ = *s++)) assert(ptr < fence); // 4311 is type cast pointer to long warning // 4312 is type cast long to pointer warning #if defined(_WIN32) -#pragma warning(disable: 4311 4312) +//#pragma warning(disable: 4311 4312) #endif assert((char *)(((long) (ptr + 7)) & ~7) <= fence); #if defined(_WIN32) -#pragma warning(default: 4311 4312) +//#pragma warning(default: 4311 4312) #endif pad(); } void set_int32(long v) { *((long *) ptr) = v; ptr += 4; } @@ -619,12 +619,12 @@ typedef class Serial_write_buffer: public Serial_buffer { void set_float(float v) { *((float *) ptr) = v; ptr += 4; } void set_char(char v) { *ptr++ = v; } #if defined(_WIN32) -#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits -#pragma warning(disable: 4311) // type cast pointer to long warning +//#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits +//#pragma warning(disable: 4311) // type cast pointer to long warning #endif - void pad() { while (((long) ptr) & 7) set_char(0); } + void pad() { while ((intptr_t) ptr & 7) set_char(0); } #if defined(_WIN32) -#pragma warning(default: 4311 546) +//#pragma warning(default: 4311 546) #endif void *to_heap(long *len) { *len = get_posn(); @@ -653,7 +653,7 @@ typedef class Alg_track : public Alg_event_list { public: void serialize_track(); void unserialize_track(); - virtual Alg_event_ptr &operator[](int i) { + virtual Alg_event_ptr const &operator[](int i) { assert(i >= 0 && i < len); return events[i]; } @@ -669,7 +669,8 @@ typedef class Alg_track : public Alg_event_list { Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, bool units_are_seconds); virtual ~Alg_track() { // note: do not call set_time_map(NULL)! - if (time_map) time_map->dereference(); time_map = NULL; } + if (time_map) time_map->dereference(); + time_map = NULL; } // Returns a buffer containing a serialization of the // file. It will be an ASCII representation unless text is true. @@ -1058,7 +1059,7 @@ typedef class Alg_seq : public Alg_track { // caller must not delete the result. Alg_track_ptr track(int); - virtual Alg_event_ptr &operator[](int i); + virtual Alg_event_ptr const &operator[](int i); virtual void convert_to_seconds(); virtual void convert_to_beats(); diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp index 307f9562b40..2dc683afd69 100644 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ b/plugins/MidiImport/portsmf/allegrord.cpp @@ -40,6 +40,7 @@ class Alg_reader { int find_real_in(string &field, int n); double parse_real(string &field); void parse_error(string &field, long offset, char *message); + void parse_error(string &field, long offset, const char *message); double parse_dur(string &field, double base); double parse_after_dur(double dur, string &field, int n, double base); double parse_loud(string &field); @@ -116,19 +117,19 @@ Alg_parameters_ptr Alg_reader::process_attributes( if (attributes) { Alg_parameters_ptr a; bool in_seconds = seq->get_units_are_seconds(); - if (a = Alg_parameters::remove_key(&attributes, "tempor")) { + if ((a = Alg_parameters::remove_key(&attributes, "tempor"))) { double tempo = a->parm.r; seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time)); } - if (a = Alg_parameters::remove_key(&attributes, "beatr")) { + if ((a = Alg_parameters::remove_key(&attributes, "beatr"))) { double beat = a->parm.r; seq->insert_beat(time, beat); } - if (a = Alg_parameters::remove_key(&attributes, "timesig_numr")) { + if ((a = Alg_parameters::remove_key(&attributes, "timesig_numr"))) { tsnum = a->parm.r; ts_flag = true; } - if (a = Alg_parameters::remove_key(&attributes, "timesig_denr")) { + if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) { tsden = a->parm.r; ts_flag = true; } @@ -158,7 +159,7 @@ bool Alg_reader::parse() while (line_parser_flag) { bool time_flag = false; bool next_flag = false; - double next; + double next = 0; bool voice_flag = false; bool loud_flag = false; bool dur_flag = false; @@ -422,11 +423,11 @@ bool Alg_reader::parse() long Alg_reader::parse_chan(string &field) { const char *int_string = field.c_str() + 1; - char *msg = "Integer or - expected"; + const char *msg = "Integer or - expected"; const char *p = int_string; char c; // check that all chars in int_string are digits or '-': - while (c = *p++) { + while ((c = *p++)) { if (!isdigit(c) && c != '-') { parse_error(field, p - field.c_str() - 1, msg); return 0; @@ -449,11 +450,11 @@ long Alg_reader::parse_chan(string &field) long Alg_reader::parse_int(string &field) { const char *int_string = field.c_str() + 1; - char *msg = "Integer expected"; + const char *msg = "Integer expected"; const char *p = int_string; char c; // check that all chars in int_string are digits: - while (c = *p++) { + while ((c = *p++)) { if (!isdigit(c)) { parse_error(field, p - field.c_str() - 1, msg); return 0; @@ -491,7 +492,7 @@ int Alg_reader::find_real_in(string &field, int n) double Alg_reader::parse_real(string &field) { - char *msg = "Real expected"; + const char *msg = "Real expected"; int last = find_real_in(field, 1); string real_string = field.substr(1, last - 1); if (last <= 1 || last < (int) field.length()) { @@ -514,15 +515,20 @@ void Alg_reader::parse_error(string &field, long offset, char *message) printf(" %s\n", message); } +void Alg_reader::parse_error(string &field, long offset, const char *message) +{ + parse_error(field, offset, const_cast(message)); +} + double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; double Alg_reader::parse_dur(string &field, double base) { - char *msg = "Duration expected"; - char *durs = "SIQHW"; - char *p; + const char *msg = "Duration expected"; + const char *durs = "SIQHW"; + const char *p; int last; double dur; if (field.length() < 2) { @@ -535,7 +541,7 @@ double Alg_reader::parse_dur(string &field, double base) // convert dur from seconds to beats dur = seq->get_time_map()->time_to_beat(base + dur) - seq->get_time_map()->time_to_beat(base); - } else if (p = strchr(durs, toupper(field[1]))) { + } else if ((p = strchr(durs, toupper(field[1])))) { dur = duration_lookup[p - durs]; last = 2; } else { @@ -578,7 +584,7 @@ double Alg_reader::parse_after_dur(double dur, string &field, } struct loud_lookup_struct { - char *str; + const char *str; int val; } loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100}, {"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60}, @@ -587,7 +593,7 @@ struct loud_lookup_struct { double Alg_reader::parse_loud(string &field) { - char *msg = "Loudness expected"; + const char *msg = "Loudness expected"; if (isdigit(field[1])) { return parse_int(field); } else { @@ -613,14 +619,14 @@ int key_lookup[] = {21, 23, 12, 14, 16, 17, 19}; // long Alg_reader::parse_key(string &field) { - char *msg = "Pitch expected"; - char *pitches = "ABCDEFG"; - char *p; + const char *msg = "Pitch expected"; + const char *pitches = "ABCDEFG"; + const char *p; if (isdigit(field[1])) { // This routine would not have been called if field = "P" // so it must be "K" so must be an integer. return parse_int(field); - } else if (p = strchr(pitches, toupper(field[1]))) { + } else if ((p = strchr(pitches, toupper(field[1])))) { long key = key_lookup[p - pitches]; key = parse_after_key(key, field, 2); return key; @@ -716,9 +722,9 @@ bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i) } else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') { int pos = i; bool period = false; - int sign = 1; + /* int sign = 1; LMMS unused variable */ if (s[pos] == '-') { - sign = -1; + /* sign = -1; LMMS unused variable */ pos++; } while (pos < len) { diff --git a/plugins/MidiImport/portsmf/allegrosmfrd.cpp b/plugins/MidiImport/portsmf/allegrosmfrd.cpp index 456fe500048..279462762a7 100644 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfrd.cpp @@ -75,6 +75,7 @@ class Alg_midifile_reader: public Midifile_reader { void Mf_portprefix(int port); void Mf_eot(); void Mf_error(char *); + void Mf_error(const char *); void Mf_header(int,int,int); void Mf_on(int,int,int); void Mf_off(int,int,int); @@ -174,14 +175,19 @@ void Alg_midifile_reader::Mf_error(char *msg) fprintf(stdout, "Midifile reader error: %s\n", msg); } +void Alg_midifile_reader::Mf_error(const char *msg) +{ + Mf_error(const_cast(msg)); +} + void Alg_midifile_reader::Mf_header(int format, int ntrks, int division) { if (format > 1) { char msg[80]; -#pragma warning(disable: 4996) // msg is long enough +//#pragma warning(disable: 4996) // msg is long enough sprintf(msg, "file format %d not implemented", format); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) Mf_error(msg); } divisions = division; @@ -268,9 +274,9 @@ void Alg_midifile_reader::Mf_controller(int chan, int control, int val) { Alg_parameter parameter; char name[32]; -#pragma warning(disable: 4996) // name is long enough +//#pragma warning(disable: 4996) // name is long enough sprintf(name, "control%dr", control); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) parameter.set_attr(symbol_table.insert_string(name)); parameter.r = val / 127.0; update(chan, -1, ¶meter); @@ -314,9 +320,9 @@ void Alg_midifile_reader::binary_msg(int len, unsigned char *msg, Alg_parameter parameter; char *hexstr = new char[len * 2 + 1]; for (int i = 0; i < len; i++) { -#pragma warning(disable: 4996) // hexstr is long enough +//#pragma warning(disable: 4996) // hexstr is long enough sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) } parameter.s = hexstr; parameter.set_attr(symbol_table.insert_string(attr_string)); @@ -340,9 +346,9 @@ void Alg_midifile_reader::Mf_arbitrary(int len, unsigned char *msg) void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg) { char text[128]; -#pragma warning(disable: 4996) // text is long enough +//#pragma warning(disable: 4996) // text is long enough sprintf(text, "metamsic data, type 0x%x, ignored", type); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) Mf_error(text); } @@ -353,7 +359,7 @@ void Alg_midifile_reader::Mf_seqnum(int n) } -static char *fpsstr[4] = {"24", "25", "29.97", "30"}; +static const char *fpsstr[4] = {"24", "25", "29.97", "30"}; void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, int frames, int subframes) @@ -363,10 +369,10 @@ void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs, char text[32]; int fps = (hours >> 6) & 3; hours &= 0x1F; -#pragma warning(disable: 4996) // text is long enough +//#pragma warning(disable: 4996) // text is long enough sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df", fpsstr[fps], hours, mins, secs, frames, subframes); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) Alg_parameter smpteoffset; smpteoffset.s = heapify(text); smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp index f58183173d2..7cac9a0412f 100644 --- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp +++ b/plugins/MidiImport/portsmf/allegrosmfwr.cpp @@ -353,7 +353,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) int len = strlen(s); char smpteoffset[5]; if (len < 24) return; // not long enough, must be bad format - int fps; + int fps = 0; if (s[0] == '2') { if (s[1] == '4') fps = 0; else if (s[1] == '5') fps = 1; diff --git a/plugins/MidiImport/portsmf/mfmidi.cpp b/plugins/MidiImport/portsmf/mfmidi.cpp index eba292f8d4f..7ea4b6ba357 100644 --- a/plugins/MidiImport/portsmf/mfmidi.cpp +++ b/plugins/MidiImport/portsmf/mfmidi.cpp @@ -35,7 +35,7 @@ void Midifile_reader::midifile() while (ntrks-- > 0 && !midifile_error) readtrack(); } -int Midifile_reader::readmt(char *s, int skip) +int Midifile_reader::readmt(const char *s, int skip) /* read through the "MThd" or "MTrk" header string */ /* if skip == 1, we attempt to skip initial garbage. */ { @@ -44,7 +44,7 @@ int Midifile_reader::readmt(char *s, int skip) char b[4]; char buff[32]; int c; - char *errmsg = "expecting "; + const char *errmsg = "expecting "; retry: while ( nread<4 ) { @@ -68,10 +68,10 @@ int Midifile_reader::readmt(char *s, int skip) goto retry; } err: -#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths +//#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths (void) strcpy(buff,errmsg); (void) strcat(buff,s); -#pragma warning(default: 4996) // turn it back on +//#pragma warning(default: 4996) // turn it back on mferror(buff); return(0); } @@ -257,9 +257,9 @@ void Midifile_reader::readtrack() void Midifile_reader::badbyte(int c) { char buff[32]; -#pragma warning(disable: 4996) // safe in this case +//#pragma warning(disable: 4996) // safe in this case (void) sprintf(buff,"unexpected byte: 0x%02x",c); -#pragma warning(default: 4996) +//#pragma warning(default: 4996) mferror(buff); } @@ -420,6 +420,11 @@ void Midifile_reader::mferror(char *s) midifile_error = 1; } +void Midifile_reader::mferror(const char *s) +{ + mferror(const_cast(s)); +} + /* The code below allows collection of a system exclusive message of */ /* arbitrary length. The Msgbuff is expanded as necessary. The only */ /* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */ diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h index 4c862029cf9..744411e3bba 100644 --- a/plugins/MidiImport/portsmf/mfmidi.h +++ b/plugins/MidiImport/portsmf/mfmidi.h @@ -81,10 +81,11 @@ class Midifile_reader { int egetc(); int msgleng(); - int readmt(char*,int); + int readmt(const char*,int); long to32bit(int,int,int,int); int to16bit(int,int); void mferror(char *); + void mferror(const char *); void badbyte(int); void metaevent(int); void msgadd(int); From 37f0d10e0b1c36448025af122b6ac1fe1995e9be Mon Sep 17 00:00:00 2001 From: Adam Hartley Date: Mon, 14 Sep 2020 01:51:37 +0100 Subject: [PATCH 078/180] CircleCI: Bump Xcode version to 9.4.1 (#5675) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7f509d7e90..01a00d912d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -170,7 +170,7 @@ jobs: environment: <<: *common_environment macos: - xcode: "9.3.1" + xcode: "9.4.1" steps: - checkout - *init From f211c192e8d902bb175c562bedbb1da49ec13a7b Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 16 Sep 2020 13:53:31 +0900 Subject: [PATCH 079/180] CircleCI: uninstall python@2 to prevent "brew install" errors --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 01a00d912d1..fe5b1f2fd7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -179,8 +179,8 @@ jobs: - run: name: Install Homebrew dependencies command: | - # unlink Homebrew's python 2 to prevent an node-gyp error - brew unlink python@2 || true + # uninstall Homebrew's python 2 to prevent errors on brew install + brew uninstall python@2 || true brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla - run: name: Install nodejs dependencies From af328003a05300eaad028d674f696eca7ed18719 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 17 Sep 2020 17:23:35 +0200 Subject: [PATCH 080/180] Use valid Semver versions for pre-releases (#5636) * Fix ProjectVersion handling of pre-releases * Add workaround for old, non-standard version * Attempt to fix versioning * More consistent comments * Apply suggestions from code review - Set CompareType's underlying type to int and revert change to ProjectVersion::compare's parameters - Add "None" and "All" as names elements of CompareType enum - Preserve hyphens in prerelease identifiers - Pad invalid (too short) versions to prevent crashes or nasty behavior - Compare numeric identifiers to non-numeric ones correctly - Don't interpret identifiers of form "-#" as numeric (where '#' is any number of digits) - Add tests to ensure fixes in this commit work and won't regress in the future * CMAKE fixes from code review Co-authored-by: Tres Finocchiaro * Remove unnecessary changes to CMake logic * More const, more reference * Apply suggestions from code review Co-authored-by: Tres Finocchiaro --- CMakeLists.txt | 2 +- cmake/CMakeLists.txt | 2 +- cmake/modules/VersionInfo.cmake | 36 +++++- include/ProjectVersion.h | 24 ++-- src/core/ProjectVersion.cpp | 175 ++++++++++++-------------- tests/src/core/ProjectVersionTest.cpp | 30 +++++ 6 files changed, 153 insertions(+), 116 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ead95c32db..aaeec055f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ IF(VERSION_STAGE) SET(VERSION "${VERSION}-${VERSION_STAGE}") ENDIF() IF(VERSION_BUILD) - SET(VERSION "${VERSION}.${VERSION_BUILD}") + SET(VERSION "${VERSION}-${VERSION_BUILD}") ENDIF() # Override version information for non-base builds diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index b27dec91e0f..833fad5819f 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -9,7 +9,7 @@ IF(VERSION_STAGE) SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${VERSION_STAGE}") ENDIF() IF(VERSION_BUILD) - SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}.${VERSION_BUILD}") + SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${VERSION_BUILD}") ENDIF() SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_UCASE}") SET(CPACK_SOURCE_GENERATOR "TBZ2") diff --git a/cmake/modules/VersionInfo.cmake b/cmake/modules/VersionInfo.cmake index cf6932cbbba..9571514a6eb 100644 --- a/cmake/modules/VersionInfo.cmake +++ b/cmake/modules/VersionInfo.cmake @@ -1,29 +1,56 @@ FIND_PACKAGE(Git) IF(GIT_FOUND AND NOT FORCE_VERSION) - # Look for git tag information (e.g. Tagged: "v1.0.0", Non-tagged: "v1.0.0-123-a1b2c3d") + SET(MAJOR_VERSION 0) + SET(MINOR_VERSION 0) + SET(PATCH_VERSION 0) + # Look for git tag information (e.g. Tagged: "v1.0.0", Untagged: "v1.0.0-123-a1b2c3d") + # Untagged format: [latest tag]-[number of commits]-[latest commit hash] EXECUTE_PROCESS( COMMAND "${GIT_EXECUTABLE}" describe --tags --match v[0-9].[0-9].[0-9]* OUTPUT_VARIABLE GIT_TAG WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 10 OUTPUT_STRIP_TRAILING_WHITESPACE) + # Read: TAG_LIST = GIT_TAG.split("-") STRING(REPLACE "-" ";" TAG_LIST "${GIT_TAG}") + # Read: TAG_LIST_LENGTH = TAG_LIST.length() LIST(LENGTH TAG_LIST TAG_LIST_LENGTH) + # Untagged versions contain at least 2 dashes, giving 3 strings on split. + # Hence, for untagged versions TAG_LIST_LENGTH = [dashes in latest tag] + 3. + # Corollary: if TAG_LIST_LENGTH <= 2, the version must be tagged. IF(TAG_LIST_LENGTH GREATER 0) + # Set FORCE_VERSION to TAG_LIST[0], strip any 'v's to get MAJ.MIN.PAT LIST(GET TAG_LIST 0 FORCE_VERSION) STRING(REPLACE "v" "" FORCE_VERSION "${FORCE_VERSION}") + # Split FORCE_VERSION on '.' and populate MAJOR/MINOR/PATCH_VERSION + STRING(REPLACE "." ";" MAJ_MIN_PAT "${FORCE_VERSION}") + LIST(GET MAJ_MIN_PAT 0 MAJOR_VERSION) + LIST(GET MAJ_MIN_PAT 1 MINOR_VERSION) + LIST(GET MAJ_MIN_PAT 2 PATCH_VERSION) ENDIF() + # 1 dash total: Dash in latest tag, no additional commits => pre-release IF(TAG_LIST_LENGTH EQUAL 2) LIST(GET TAG_LIST 1 VERSION_STAGE) SET(FORCE_VERSION "${FORCE_VERSION}-${VERSION_STAGE}") + # 2 dashes: Assume untagged with no dashes in latest tag name => stable + commits ELSEIF(TAG_LIST_LENGTH EQUAL 3) + # Get the number of commits and latest commit hash LIST(GET TAG_LIST 1 EXTRA_COMMITS) - SET(FORCE_VERSION "${FORCE_VERSION}.${EXTRA_COMMITS}") + LIST(GET TAG_LIST 2 COMMIT_HASH) + # Bump the patch version + MATH(EXPR PATCH_VERSION "${PATCH_VERSION}+1") + # Set the version to MAJOR.MINOR.PATCH-EXTRA_COMMITS+COMMIT_HASH + SET(FORCE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") + SET(FORCE_VERSION "${FORCE_VERSION}-${EXTRA_COMMITS}+${COMMIT_HASH}") + # 3 dashes: Assume untagged with 1 dash in latest tag name => pre-release + commits ELSEIF(TAG_LIST_LENGTH EQUAL 4) + # Get pre-release stage, number of commits, and latest commit hash LIST(GET TAG_LIST 1 VERSION_STAGE) LIST(GET TAG_LIST 2 EXTRA_COMMITS) - SET(FORCE_VERSION - "${FORCE_VERSION}-${VERSION_STAGE}.${EXTRA_COMMITS}") + LIST(GET TAG_LIST 3 COMMIT_HASH) + # Set the version to MAJOR.MINOR.PATCH-VERSION_STAGE.EXTRA_COMMITS+COMMIT_HASH + SET(FORCE_VERSION "${FORCE_VERSION}-${VERSION_STAGE}") + SET(FORCE_VERSION "${FORCE_VERSION}.${EXTRA_COMMITS}+${COMMIT_HASH}") ENDIF() ENDIF() @@ -74,4 +101,3 @@ MESSAGE("\n" "* Override version: -DFORCE_VERSION=x.x.x-x\n" "* Ignore Git information: -DFORCE_VERSION=internal\n" ) - diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index 5f7bf09b3c1..e938ccc81c7 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -28,6 +28,9 @@ #define PROJECT_VERSION_H #include +#include + +#include /*! \brief Version number parsing and comparison * @@ -36,16 +39,16 @@ class ProjectVersion { public: - enum CompareType { Major, Minor, Release, Stage, Build }; + enum CompareType : int { None = 0, Major=1, Minor=2, Release=3, Stage=4, Build=5, All = std::numeric_limits::max() }; + - ProjectVersion(QString version, CompareType c = Build); - ProjectVersion(const char * version, CompareType c = Build); + ProjectVersion(QString version, CompareType c = All); + ProjectVersion(const char * version, CompareType c = All); int getMajor() const { return m_major; } int getMinor() const { return m_minor; } - int getRelease() const { return m_release; } - QString getStage() const { return m_stage; } - int getBuild() const { return m_build; } + int getPatch() const { return m_patch; } + const QStringList& getLabels() const { return m_labels; } CompareType getCompareType() const { return m_compareType; } ProjectVersion setCompareType(CompareType compareType) { m_compareType = compareType; return * this; } @@ -54,11 +57,10 @@ class ProjectVersion private: QString m_version; - int m_major; - int m_minor; - int m_release; - QString m_stage; - int m_build; + int m_major = 0; + int m_minor = 0; + int m_patch = 0; + QStringList m_labels; CompareType m_compareType; } ; diff --git a/src/core/ProjectVersion.cpp b/src/core/ProjectVersion.cpp index ea97a6fec7d..6f3dcddbeb1 100644 --- a/src/core/ProjectVersion.cpp +++ b/src/core/ProjectVersion.cpp @@ -27,123 +27,105 @@ #include "ProjectVersion.h" -int parseMajor(QString & version) { - return version.section( '.', 0, 0 ).toInt(); -} - - - - -int parseMinor(QString & version) { - return version.section( '.', 1, 1 ).toInt(); -} - - - - -int parseRelease(QString & version) { - return version.section( '.', 2, 2 ).section( '-', 0, 0 ).toInt(); -} - - - - -QString parseStage(QString & version) { - return version.section( '.', 2, 2 ).section( '-', 1 ); -} - - - - -int parseBuild(QString & version) { - return version.section( '.', 3 ).toInt(); -} - ProjectVersion::ProjectVersion(QString version, CompareType c) : m_version(version), - m_major(parseMajor(m_version)), - m_minor(parseMinor(m_version)), - m_release(parseRelease(m_version)), - m_stage(parseStage(m_version)), - m_build(parseBuild(m_version)), m_compareType(c) { + // Version numbers may have build data, prefixed with a '+', + // but this mustn't affect version precedence in comparisons + QString metadataStripped = version.split("+").first(); + // They must have an obligatory initial segement, and may have + // optional identifiers prefaced by a '-'. Both parts affect precedence + QString obligatorySegment = metadataStripped.section('-', 0, 0); + QString prereleaseSegment = metadataStripped.section('-', 1); + + // The obligatory segment consists of three identifiers: MAJOR.MINOR.PATCH + QStringList mainVersion = obligatorySegment.split("."); + // HACK: Pad invalid versions in order to prevent crashes + while (mainVersion.size() < 3){ mainVersion.append("0"); } + m_major = mainVersion.at(0).toInt(); + m_minor = mainVersion.at(1).toInt(); + m_patch = mainVersion.at(2).toInt(); + + // Any # of optional pre-release identifiers may follow, separated by '.'s + if (!prereleaseSegment.isEmpty()){ m_labels = prereleaseSegment.split("."); } + + // HACK: Handle old (1.2.2 and earlier), non-standard versions of the form + // MAJOR.MINOR.PATCH.COMMITS, used for non-release builds from source. + if (mainVersion.size() >= 4 && m_major <= 1 && m_minor <= 2 && m_patch <= 2){ + // Drop the standard version identifiers. erase(a, b) removes [a,b) + mainVersion.erase(mainVersion.begin(), mainVersion.begin() + 3); + // Prepend the remaining identifiers as prerelease versions + m_labels = mainVersion + m_labels; + // Bump the patch version. x.y.z-a < x.y.z, but we want x.y.z.a > x.y.z + m_patch += 1; + } } -ProjectVersion::ProjectVersion(const char* version, CompareType c) : - m_version(QString(version)), - m_major(parseMajor(m_version)), - m_minor(parseMinor(m_version)), - m_release(parseRelease(m_version)), - m_stage(parseStage(m_version)), - m_build(parseBuild(m_version)), - m_compareType(c) +ProjectVersion::ProjectVersion(const char* version, CompareType c) : ProjectVersion(QString(version), c) { } +//! @param c Determines the number of identifiers to check when comparing int ProjectVersion::compare(const ProjectVersion & a, const ProjectVersion & b, CompareType c) { - if(a.getMajor() != b.getMajor()) - { - return a.getMajor() - b.getMajor(); - } - if(c == Major) - { - return 0; - } - - if(a.getMinor() != b.getMinor()) - { - return a.getMinor() - b.getMinor(); - } - if(c == Minor) - { - return 0; + // How many identifiers to compare before we consider the versions equal + const int limit = static_cast(c); + + // Use the value of limit to zero out identifiers we don't care about + int aMaj = 0, bMaj = 0, aMin = 0, bMin = 0, aPat = 0, bPat = 0; + if (limit >= 1){ aMaj = a.getMajor(); bMaj = b.getMajor(); } + if (limit >= 2){ aMin = a.getMinor(); bMin = b.getMinor(); } + if (limit >= 3){ aPat = a.getPatch(); bPat = b.getPatch(); } + + // Then we can compare as if we care about every identifier + if(aMaj != bMaj){ return aMaj - bMaj; } + if(aMin != bMin){ return aMin - bMin; } + if(aPat != bPat){ return aPat - bPat; } + + // Decide how many optional identifiers we care about + const int maxLabels = qMax(0, limit - 3); + const auto aLabels = a.getLabels().mid(0, maxLabels); + const auto bLabels = b.getLabels().mid(0, maxLabels); + + // We can only compare identifiers if both versions have them + const int commonLabels = qMin(aLabels.size(), bLabels.size()); + // If one version has optional labels and the other doesn't, + // the one without them is bigger + if (commonLabels == 0){ return bLabels.size() - aLabels.size(); } + + // Otherwise, compare as many labels as we can + for (int i = 0; i < commonLabels; i++){ + const QString& labelA = aLabels.at(i); + const QString& labelB = bLabels.at(i); + // If both labels are the same, skip + if (labelA == labelB){ continue; } + // Numeric and non-numeric identifiers compare differently + bool aIsNumeric = false, bIsNumeric = false; + const int numA = labelA.toInt(&aIsNumeric); + const int numB = labelB.toInt(&bIsNumeric); + // toInt reads '-x' as a negative number, semver says it's non-numeric + aIsNumeric &= !labelA.startsWith("-"); + bIsNumeric &= !labelB.startsWith("-"); + // If only one identifier is numeric, that one is smaller + if (aIsNumeric != bIsNumeric){ return aIsNumeric ? -1 : 1; } + // If both are numeric, compare as numbers + if (aIsNumeric && bIsNumeric){ return numA - numB; } + // Otherwise, compare lexically + return labelA.compare(labelB); } - if(a.getRelease() != b.getRelease()) - { - return a.getRelease() - b.getRelease(); - } - if(c == Release) - { - return 0; - } - - if(!(a.getStage().isEmpty() && b.getStage().isEmpty())) - { - // make sure 0.x.y > 0.x.y-alpha - if(a.getStage().isEmpty()) - { - return 1; - } - if(b.getStage().isEmpty()) - { - return -1; - } - - // 0.x.y-beta > 0.x.y-alpha - int cmp = QString::compare(a.getStage(), b.getStage()); - if(cmp) - { - return cmp; - } - } - if(c == Stage) - { - return 0; - } - - return a.getBuild() - b.getBuild(); + // If everything else matches, the version with more labels is bigger + return aLabels.size() - bLabels.size(); } @@ -153,6 +135,3 @@ int ProjectVersion::compare(ProjectVersion v1, ProjectVersion v2) { return compare(v1, v2, std::min(v1.getCompareType(), v2.getCompareType())); } - - - diff --git a/tests/src/core/ProjectVersionTest.cpp b/tests/src/core/ProjectVersionTest.cpp index 7c99727397f..e52088f6f8c 100644 --- a/tests/src/core/ProjectVersionTest.cpp +++ b/tests/src/core/ProjectVersionTest.cpp @@ -39,9 +39,39 @@ private slots: QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Minor) == "1.1.5"); QVERIFY( ! ( ProjectVersion("3.1.0", ProjectVersion::Minor) < "2.2.5" ) ); QVERIFY( ! ( ProjectVersion("2.5.0", ProjectVersion::Release) < "2.2.5" ) ); + //A pre-release version has lower precedence than a normal version QVERIFY(ProjectVersion("1.1.0") > "1.1.0-alpha"); + //But higher precedence than the previous version + QVERIFY(ProjectVersion("1.1.0-alpha") > "1.0.0"); + //Identifiers with letters or hyphens are compare lexically in ASCII sort order QVERIFY(ProjectVersion("1.1.0-alpha") < "1.1.0-beta"); QVERIFY(ProjectVersion("1.2.0-rc1") < "1.2.0-rc2"); + //Build metadata MUST be ignored when determining version precedence + QVERIFY(ProjectVersion("1.2.2") == "1.2.2+metadata"); + QVERIFY(ProjectVersion("1.0.0-alpha") < "1.0.0-alpha.1"); + QVERIFY(ProjectVersion("1.0.0-alpha.1") < "1.0.0-alpha.beta"); + QVERIFY(ProjectVersion("1.0.0-alpha.beta") < "1.0.0-beta"); + QVERIFY(ProjectVersion("1.0.0-beta.2") < "1.0.0-beta.11"); + //Test workaround for old, nonstandard version numbers + QVERIFY(ProjectVersion("1.2.2.42") == "1.2.3-42"); + QVERIFY(ProjectVersion("1.2.2.42") > "1.2.2.21"); + //Ensure that newer versions of the same format aren't upgraded + //in order to discourage use of incorrect versioning + QVERIFY(ProjectVersion("1.2.3.42") == "1.2.3"); + //CompareVersion "All" should compare every identifier + QVERIFY( + ProjectVersion("1.0.0-a.b.c.d.e.f.g.h.i.j.k.l", ProjectVersion::All) + < "1.0.0-a.b.c.d.e.f.g.h.i.j.k.m" + ); + //Prerelease identifiers may contain hyphens + QVERIFY(ProjectVersion("1.0.0-Alpha-1.2") > "1.0.0-Alpha-1.1"); + //We shouldn't crash on invalid versions + QVERIFY(ProjectVersion("1-invalid") == "1.0.0-invalid"); + QVERIFY(ProjectVersion("") == "0.0.0"); + //Numeric identifiers are smaller than non-numeric identiiers + QVERIFY(ProjectVersion("1.0.0-alpha") > "1.0.0-1"); + //An identifier of the form "-x" is non-numeric, not negative + QVERIFY(ProjectVersion("1.0.0-alpha.-1") > "1.0.0-alpha.1"); } } ProjectVersionTests; From 2bdd835a8ab03fec72ed7d4bac3a89077cbe2d62 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 17 Sep 2020 18:24:54 +0200 Subject: [PATCH 081/180] Switch out mailing list for Discord Because as far as I can tell that's where most of the active devs are available. --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1061ecff600..b1bea70a0c6 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ Information about what you can do and how can be found in the Before coding a new big feature, please _always_ [file an issue](https://github.com/LMMS/lmms/issues/new) for your idea and -suggestions about your feature and about the intended implementation on GitHub -or post to the LMMS developers mailinglist (lmms-devel@lists.sourceforge.net) -and wait for replies! Maybe there are different ideas, improvements, hints or +suggestions about your feature and about the intended implementation on GitHub, +or ask in one of the tech channels on Discord and wait for replies! Maybe there are different ideas, improvements, or hints, or maybe your feature is not welcome/needed at the moment. From 9e3f602194f43f3e7fa22640710459e54ffe3a29 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 17 Sep 2020 18:40:55 +0200 Subject: [PATCH 082/180] Fix 'Version difference' dialogue showing for differences in patch and lower after semantic versioning PR --- src/core/DataFile.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 44cb920d844..dbd0c84b741 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -772,10 +772,10 @@ void DataFile::upgrade_0_4_0_rc2() void DataFile::upgrade_1_0_99() { jo_id_t last_assigned_id = 0; - + QList idList; findIds(documentElement(), idList); - + QDomNodeList list = elementsByTagName("ladspacontrols"); for(int i = 0; !list.item(i).isNull(); ++i) { @@ -791,22 +791,22 @@ void DataFile::upgrade_1_0_99() QDomElement me = createElement("data"); me.setAttribute("value", el.attribute("data")); me.setAttribute("scale_type", "log"); - + jo_id_t id; for(id = last_assigned_id + 1; idList.contains(id); id++) { } - + last_assigned_id = id; idList.append(id); me.setAttribute("id", id); el.appendChild(me); - + } } } - } + } } @@ -1047,7 +1047,7 @@ void DataFile::upgrade_1_3_0() QDomElement attribute = attributes.item( k ).toElement(); if( attribute.attribute( "name" ) == "file" && - ( attribute.attribute( "value" ) == "calf" || + ( attribute.attribute( "value" ) == "calf" || attribute.attribute( "value" ) == "calf.so" ) ) { attribute.setAttribute( "value", "veal" ); @@ -1311,7 +1311,7 @@ void DataFile::upgrade_1_3_0() }; iterate_ladspa_ports(effect, fn); } - + if( attribute.attribute( "name" ) == "plugin" && attribute.attribute( "value" ) == "StereoTools" ) { @@ -1486,15 +1486,14 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) if( root.hasAttribute( "creatorversion" ) ) { - // compareType defaults to Build,so it doesn't have to be set here + // compareType defaults to All, so it doesn't have to be set here ProjectVersion createdWith = root.attribute( "creatorversion" ); ProjectVersion openedWith = LMMS_VERSION; if ( createdWith != openedWith ) { - // only one compareType needs to be set, and we can compare on one line because setCompareType returns ProjectVersion - if( createdWith.setCompareType( ProjectVersion::Minor ) - != openedWith ) + if( createdWith.setCompareType( ProjectVersion::Minor ) != + openedWith.setCompareType( ProjectVersion::Minor ) ) { if( gui != nullptr && root.attribute( "type" ) == "song" ) { @@ -1536,7 +1535,7 @@ void findIds(const QDomElement& elem, QList& idList) idList.append(elem.attribute("id").toInt()); } QDomElement child = elem.firstChildElement(); - while(!child.isNull()) + while(!child.isNull()) { findIds(child, idList); child = child.nextSiblingElement(); From 1dab416c6cb9dd437829693bdabad5533b37271e Mon Sep 17 00:00:00 2001 From: IanCaio Date: Thu, 17 Sep 2020 19:45:46 -0300 Subject: [PATCH 083/180] Improvements to the upgrade routines (#5660) * First commit This commit starts the improvements on the upgrade methods. We are going to change both the DataFile and ConfigManager to use separate version values from LMMS release versions, so we can easily bump those versions for new upgrade routines. This first commit starts implementing a new version value for the ConfigManager. * Change code as per requested on review As requested, the "configVersion" method was replaced by "legacyConfigVersion" instead, which is only used to return a configuration version if none is present in the configuration file. The configuration version of the current build is stored in a local variable called CONFIG_VERSION, making version bumping easier. Uses a switch statement instead of if-else to be able to make use of case-cascading. TODO: - Change the CONFIG_VERSION variable to a unsigned int? - Start working on the DataFile.cpp. * Changes the upgrade logic on DataFile.cpp Starts refactoring the upgrade logic on DataFile.cpp. Now the "version" attribute is used to indicate which fileVersion we are loading. If the value of version is "1.0", we have a legacy file and use the legacyFileVersion method to retrieve the integer version using the LMMS version. If the value of version is an integer, we just read it. The integer indicates the position in a list of upgrade methods where we should start from and run the upgrade methods. The file version of the build is held in the FILE_VERSION const on DataFile.h. It HAS TO match the number of upgrade methods that we have on our list. One of the versions had 2 upgrade routines (upgrade_1_2_0_rc3 and upgrade_1_2_0_rc2_42). They were merged into a single one (upgrade_1_2_0_rc3) because they were both called from a single version check, meaning that they are both part of a single fileVersion. Two fatal errors were added (which can later be improved to show an error messagebox): One if the version attribute doesn't exist, and another one if we are using a FILE_VERSION that doesn't match the number of upgrade methods (to avoid mistakes while coding new upgrades later). The configVersion variables and methods were changed to use unsigned int instead of int. TODO: - Make the list of upgrade methods static. - Add debug messages for each upgrade routine for testing purposes. * Make method vector a static const variable On DataFile.cpp, we now use the vector list of upgrade methods as a static const variable, so it only has to be constructed once. * Reorganize vector lists Reorganize vector lists so they are more easily readable. Revert changes on upgrade method names from ConfigManager.cpp. * Makes the file version bumping automatic The file version bumping on DataFile.cpp is now automatic (using the size of the m_upgradeMethods vector as a reference). FILE_VERSION constant was removed, and with it the qFatal error when it doesn't match the size of the methods vector. * Improve formatting of version and upgrades lists Improves the formatting of the vector lists of upgrade routines and LMMS versions (2 upgrade routines per line and 3 LMMS versions per line). Adds a qWarning for every upgrade routine for testing purposes, plus a qWarning that tells the current fileVersion/configVersion when upgrade is called. Removes extra space characters after the opening bracket of ConfigManager::upgrade_1_1_91. * Changes ConfigManager to use a vector of methods Changes ConfigManager to use a vector of upgrade methods, just like DataFile. The new Config Version can be calculated automatically now, so the CONFIG_VERSION constant was removed. Corrects a small comment on Datafile.h. * Addresses Dom's review requests - Changes legacyConfigVersion and legacyFileVersion from const unsigned int to just unsigned int, since the const is irrelevant in this context. - Changes the type alias for upgrade methods to start with an uppercase as per the code style guidelines. Moves the aliasing of the type to the class declaration so it can be used on both the method and on the vector list declaration. - Changes the vector list names from m_upgradeMethods to UPGRADE_METHODS, so it's more visible they are a static constant. - Move the upgradeVersions list from the legacyFileVersion method to the DataFile class, as an static const called UPGRADE_VERSIONS. - Uses std::upper_bound instead of std::find_if for the legacyFileVersion method. * Uses type alias on vector assignment Uses the UpgradeMethod type alias when defining the upgrade methods vector from both ConfigManager and DataFile. * Removes debugging warnings Removes the qWarning() calls that were placed for debugging purposes. --- include/ConfigManager.h | 12 ++- include/DataFile.h | 18 ++-- src/core/ConfigManager.cpp | 67 ++++++++++--- src/core/DataFile.cpp | 201 ++++++++++++++++--------------------- 4 files changed, 165 insertions(+), 133 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index de22d22af11..e5df02ff5e9 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -39,7 +39,6 @@ class LmmsCore; - const QString PROJECTS_PATH = "projects/"; const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; @@ -55,6 +54,9 @@ const QString PORTABLE_MODE_FILE = "/portable_mode.txt"; class LMMS_EXPORT ConfigManager : public QObject { Q_OBJECT + + using UpgradeMethod = void(ConfigManager::*)(); + public: static inline ConfigManager * inst() { @@ -219,6 +221,10 @@ class LMMS_EXPORT ConfigManager : public QObject return m_version; } + // Used when the configversion attribute is not present in a configuration file. + // Returns the appropriate config file version based on the LMMS version. + unsigned int legacyConfigVersion(); + QString defaultVersion() const; @@ -270,6 +276,9 @@ class LMMS_EXPORT ConfigManager : public QObject void upgrade_1_1_91(); void upgrade(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + QString m_workingDir; QString m_dataDir; QString m_vstDir; @@ -286,6 +295,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString m_backgroundPicFile; QString m_lmmsRcFile; QString m_version; + unsigned int m_configVersion; QStringList m_recentlyOpenedProjects; typedef QVector > stringPairVector; diff --git a/include/DataFile.h b/include/DataFile.h index 5890693da0a..38a1fa7631c 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -31,12 +31,16 @@ #include "lmms_export.h" #include "MemoryManager.h" +#include "ProjectVersion.h" class QTextStream; class LMMS_EXPORT DataFile : public QDomDocument { MM_OPERATORS + + using UpgradeMethod = void(DataFile::*)(); + public: enum Types { @@ -84,6 +88,8 @@ class LMMS_EXPORT DataFile : public QDomDocument return m_type; } + unsigned int legacyFileVersion(); + private: static Type type( const QString& typeName ); static QString typeName( Type type ); @@ -107,9 +113,13 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_0(); void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); - void upgrade_1_2_0_rc2_42(); void upgrade_1_3_0(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + // List of ProjectVersions for the legacyFileVersion method + static const std::vector UPGRADE_VERSIONS; + void upgrade(); void loadData( const QByteArray & _data, const QString & _sourceFile ); @@ -125,14 +135,10 @@ class LMMS_EXPORT DataFile : public QDomDocument QDomElement m_content; QDomElement m_head; Type m_type; + unsigned int m_fileVersion; } ; -const int LDF_MAJOR_VERSION = 1; -const int LDF_MINOR_VERSION = 0; -const QString LDF_VERSION_STRING = QString::number( LDF_MAJOR_VERSION ) + "." + QString::number( LDF_MINOR_VERSION ); - - #endif diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 53262dac7df..5336654aef0 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -38,6 +38,11 @@ #include "lmmsversion.h" +// Vector with all the upgrade methods +const std::vector ConfigManager::UPGRADE_METHODS = { + &ConfigManager::upgrade_1_1_90 , &ConfigManager::upgrade_1_1_91 +}; + static inline QString ensureTrailingSlash(const QString & s ) { if(! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\')) @@ -51,7 +56,9 @@ static inline QString ensureTrailingSlash(const QString & s ) ConfigManager * ConfigManager::s_instanceOfMe = NULL; -ConfigManager::ConfigManager() : m_version(defaultVersion()) +ConfigManager::ConfigManager() : + m_version(defaultVersion()), + m_configVersion( UPGRADE_METHODS.size() ) { if (QFileInfo::exists(qApp->applicationDirPath() + PORTABLE_MODE_FILE)) { @@ -114,7 +121,7 @@ void ConfigManager::upgrade_1_1_90() void ConfigManager::upgrade_1_1_91() -{ +{ // rename displaydbv to displaydbfs if (!value("app", "displaydbv").isNull()) { setValue("app", "displaydbfs", value("app", "displaydbv")); @@ -131,17 +138,15 @@ void ConfigManager::upgrade() return; } - ProjectVersion createdWith = m_version; - - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.90") - { - upgrade_1_1_90(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_configVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.91") - { - upgrade_1_1_91(); - } + ProjectVersion createdWith = m_version; // Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc) if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION) @@ -151,6 +156,7 @@ void ConfigManager::upgrade() // Bump the version, now that we are upgraded m_version = LMMS_VERSION; + m_configVersion = UPGRADE_METHODS.size(); } QString ConfigManager::defaultVersion() const @@ -400,11 +406,23 @@ void ConfigManager::loadConfigFile(const QString & configFile) QDomNode node = root.firstChild(); - // Cache the config version for upgrade() + // Cache LMMS version if (!root.attribute("version").isNull()) { m_version = root.attribute("version"); } + // Get the version of the configuration file (for upgrade purposes) + if( root.attribute("configversion").isNull() ) + { + m_configVersion = legacyConfigVersion(); // No configversion attribute found + } + else + { + bool success; + m_configVersion = root.attribute("configversion").toUInt(&success); + if( !success ) qWarning("Config Version conversion failure."); + } + // create the settings-map out of the DOM while(!node.isNull()) { @@ -565,6 +583,7 @@ void ConfigManager::saveConfigFile() QDomElement lmms_config = doc.createElement("lmms"); lmms_config.setAttribute("version", m_version); + lmms_config.setAttribute("configversion", m_configVersion); doc.appendChild(lmms_config); for(settingsMap::iterator it = m_settings.begin(); @@ -673,3 +692,25 @@ void ConfigManager::initDevelopmentWorkingDir() cmakeCache.close(); } } + +// If configversion is not present, we will convert the LMMS version to the appropriate +// configuration file version for backwards compatibility. +unsigned int ConfigManager::legacyConfigVersion() +{ + ProjectVersion createdWith = m_version; + + createdWith.setCompareType(ProjectVersion::Build); + + if( createdWith < "1.1.90" ) + { + return 0; + } + else if( createdWith < "1.1.91" ) + { + return 1; + } + else + { + return 2; + } +} diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index dbd0c84b741..2ee74c41890 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -49,7 +49,28 @@ static void findIds(const QDomElement& elem, QList& idList); - +// Vector with all the upgrade methods +const std::vector DataFile::UPGRADE_METHODS = { + &DataFile::upgrade_0_2_1_20070501 , &DataFile::upgrade_0_2_1_20070508, + &DataFile::upgrade_0_3_0_rc2 , &DataFile::upgrade_0_3_0, + &DataFile::upgrade_0_4_0_20080104 , &DataFile::upgrade_0_4_0_20080118, + &DataFile::upgrade_0_4_0_20080129 , &DataFile::upgrade_0_4_0_20080409, + &DataFile::upgrade_0_4_0_20080607 , &DataFile::upgrade_0_4_0_20080622, + &DataFile::upgrade_0_4_0_beta1 , &DataFile::upgrade_0_4_0_rc2, + &DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0, + &DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3, + &DataFile::upgrade_1_3_0 +}; + +// Vector of all versions that have upgrade routines. +const std::vector DataFile::UPGRADE_VERSIONS = { + "0.2.1-20070501" , "0.2.1-20070508" , "0.3.0-rc2", + "0.3.0" , "0.4.0-20080104" , "0.4.0-20080118", + "0.4.0-20080129" , "0.4.0-20080409" , "0.4.0-20080607", + "0.4.0-20080622" , "0.4.0-beta1" , "0.4.0-rc2", + "1.0.99-0" , "1.1.0-0" , "1.1.91-0", + "1.2.0-rc3" , "1.3.0" +}; DataFile::typeDescStruct DataFile::s_types[DataFile::TypeCount] = @@ -71,11 +92,12 @@ DataFile::DataFile( Type type ) : QDomDocument( "lmms-project" ), m_content(), m_head(), - m_type( type ) + m_type( type ), + m_fileVersion( UPGRADE_METHODS.size() ) { appendChild( createProcessingInstruction("xml", "version=\"1.0\"")); QDomElement root = createElement( "lmms-project" ); - root.setAttribute( "version", LDF_VERSION_STRING ); + root.setAttribute( "version", m_fileVersion ); root.setAttribute( "type", typeName( type ) ); root.setAttribute( "creator", "LMMS" ); root.setAttribute( "creatorversion", LMMS_VERSION ); @@ -95,7 +117,8 @@ DataFile::DataFile( Type type ) : DataFile::DataFile( const QString & _fileName ) : QDomDocument(), m_content(), - m_head() + m_head(), + m_fileVersion( UPGRADE_METHODS.size() ) { QFile inFile( _fileName ); if( !inFile.open( QIODevice::ReadOnly ) ) @@ -873,31 +896,6 @@ void DataFile::upgrade_1_1_91() } -void DataFile::upgrade_1_2_0_rc3() -{ - // Upgrade from earlier bbtrack beat note behaviour of adding - // steps if a note is placed after the last step. - QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); - for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) - { - QDomNodeList patterns = bbtracks.item( i - ).toElement().elementsByTagName( - "pattern" ); - for( int j = 0; !patterns.item( j ).isNull(); ++j ) - { - int patternLength, steps; - QDomElement el = patterns.item( j ).toElement(); - if( el.attribute( "len" ) != "" ) - { - patternLength = el.attribute( "len" ).toInt(); - steps = patternLength / 12; - el.setAttribute( "steps", steps ); - } - } - } -} - - static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) { if( el.hasAttribute( "syncmode" ) ) @@ -930,8 +928,30 @@ static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) } -void DataFile::upgrade_1_2_0_rc2_42() +void DataFile::upgrade_1_2_0_rc3() { + // Upgrade from earlier bbtrack beat note behaviour of adding + // steps if a note is placed after the last step. + QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); + for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) + { + QDomNodeList patterns = bbtracks.item( i + ).toElement().elementsByTagName( + "pattern" ); + for( int j = 0; !patterns.item( j ).isNull(); ++j ) + { + int patternLength, steps; + QDomElement el = patterns.item( j ).toElement(); + if( el.attribute( "len" ) != "" ) + { + patternLength = el.attribute( "len" ).toInt(); + steps = patternLength / 12; + el.setAttribute( "steps", steps ); + } + } + } + + // DataFile::upgrade_1_2_0_rc2_42 QDomElement el = firstChildElement(); while ( !el.isNull() ) { @@ -1338,92 +1358,19 @@ void DataFile::upgrade_1_3_0() void DataFile::upgrade() { - ProjectVersion version = - documentElement().attribute( "creatorversion" ). - replace( "svn", "" ); - - if( version < "0.2.1-20070501" ) - { - upgrade_0_2_1_20070501(); - } - - if( version < "0.2.1-20070508" ) - { - upgrade_0_2_1_20070508(); - } - - if( version < "0.3.0-rc2" ) - { - upgrade_0_3_0_rc2(); - } - - if( version < "0.3.0" ) - { - upgrade_0_3_0(); - } - - if( version < "0.4.0-20080104" ) - { - upgrade_0_4_0_20080104(); - } - - if( version < "0.4.0-20080118" ) - { - upgrade_0_4_0_20080118(); - } - - if( version < "0.4.0-20080129" ) - { - upgrade_0_4_0_20080129(); - } - - if( version < "0.4.0-20080409" ) - { - upgrade_0_4_0_20080409(); - } - - if( version < "0.4.0-20080607" ) - { - upgrade_0_4_0_20080607(); - } - - if( version < "0.4.0-20080622" ) - { - upgrade_0_4_0_20080622(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_fileVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if( version < "0.4.0-beta1" ) - { - upgrade_0_4_0_beta1(); - } - if( version < "0.4.0-rc2" ) - { - upgrade_0_4_0_rc2(); - } - if( version < "1.0.99-0" ) - { - upgrade_1_0_99(); - } - if( version < "1.1.0-0" ) - { - upgrade_1_1_0(); - } - if( version < "1.1.91-0" ) - { - upgrade_1_1_91(); - } - if( version < "1.2.0-rc3" ) - { - upgrade_1_2_0_rc3(); - upgrade_1_2_0_rc2_42(); - } - if( version < "1.3.0" ) - { - upgrade_1_3_0(); - } + // Bump the file version (which should be the size of the upgrade methods vector) + m_fileVersion = UPGRADE_METHODS.size(); // update document meta data - documentElement().setAttribute( "version", LDF_VERSION_STRING ); + documentElement().setAttribute( "version", m_fileVersion ); documentElement().setAttribute( "type", typeName( type() ) ); documentElement().setAttribute( "creator", "LMMS" ); documentElement().setAttribute( "creatorversion", LMMS_VERSION ); @@ -1483,6 +1430,20 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); + if( root.hasAttribute( "version" ) ) + { + if( root.attribute( "version" ) == "1.0" ){ + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); + } + } if( root.hasAttribute( "creatorversion" ) ) { @@ -1541,3 +1502,17 @@ void findIds(const QDomElement& elem, QList& idList) child = child.nextSiblingElement(); } } + +unsigned int DataFile::legacyFileVersion() +{ + // Version of LMMs that created this project + ProjectVersion creator = + documentElement().attribute( "creatorversion" ). + replace( "svn", "" ); + + // Get an iterator pointing at the first upgrade we need to run (or at the end if there is no such upgrade) + auto firstRequiredUpgrade = std::upper_bound( UPGRADE_VERSIONS.begin(), UPGRADE_VERSIONS.end(), creator ); + + // Convert the iterator to an index, which is our file version (starting at 0) + return std::distance( UPGRADE_VERSIONS.begin(), firstRequiredUpgrade ); +} From 27a9f7711ee5e6f83648ec91625c027b876c2b0c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Fri, 18 Sep 2020 15:55:44 +0900 Subject: [PATCH 084/180] LadspaManager: Skip empty entries when looking for plugins (#5677) This fixes crashes when starting LMMS from specific working directories. --- src/core/LadspaManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index febbe5a9192..86acb661be5 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -59,6 +59,8 @@ LadspaManager::LadspaManager() for( QStringList::iterator it = ladspaDirectories.begin(); it != ladspaDirectories.end(); ++it ) { + // Skip empty entries as QDir will interpret it as the working directory + if ((*it).isEmpty()) { continue; } QDir directory( ( *it ) ); QFileInfoList list = directory.entryInfoList(); for( QFileInfoList::iterator file = list.begin(); From f6eeaba076af8cb1762a320c5c1b1810aee3e1fd Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 18 Sep 2020 16:38:42 +0200 Subject: [PATCH 085/180] Suppress windows.h's min and max macros, allowing use of numeric_limits's max Co-authored-by: Dominic Clark --- src/core/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/main.cpp b/src/core/main.cpp index 897d07abf54..c83578a3d10 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -40,6 +40,7 @@ #include #ifdef LMMS_BUILD_WIN32 +#define NOMINMAX #include #endif @@ -437,7 +438,7 @@ int main( int argc, char * * argv ) { return noInputFileError(); } - + QFile f( QString::fromLocal8Bit( argv[i] ) ); f.open( QIODevice::ReadOnly ); QByteArray d = qCompress( f.readAll() ) ; From a6d0b460fcd13e9c5762bf8d6cbe92022695a0ea Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 18 Sep 2020 18:48:39 +0200 Subject: [PATCH 086/180] Don't define NOMINMAX if it's already defined (#5678) Co-authored-by: Dominic Clark --- src/core/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/main.cpp b/src/core/main.cpp index c83578a3d10..9db0555222f 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -40,7 +40,9 @@ #include #ifdef LMMS_BUILD_WIN32 +#ifndef NOMINMAX #define NOMINMAX +#endif #include #endif From 2f37281d02b651766d94fe194d51984d343f4b1c Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Mon, 21 Sep 2020 02:55:46 +0200 Subject: [PATCH 087/180] Fix for Mixer volume percentage labels are off by a factor of 100 (#5661) Add m_conversionFactor to the AutomatableModelView. This factor will be applied to the model->value when displaying it in the contextmenu of the control for the reset and copy actions. The factor will be applied when copying the value to the clipboard. When pasting from the clipboard, the value will be divided by the factor. Remove the model->displayValue() calls when updating the reset/copy/paste action text labels as this gives e.g. in the Equalizer the wrong action text for the Frequency knobs. In the Fader class, remove the m_displayConversion variable but rather use the new m_conversionFactor variable. Rewire the setDisplayConversion() function to set the m_conversionFactor to 1.0 or 100.0. Faders in FxMixer show now the correct context menu. Copying and pasting values between faders or even volume knobs in tracks shows consistent behavior. Other faders (like in Eq) show the old behavior. --- include/AutomatableModelView.h | 4 ++++ include/Fader.h | 5 ++--- src/gui/AutomatableModelView.cpp | 25 +++++++++++++++++------ src/gui/widgets/Fader.cpp | 35 ++++++++++---------------------- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h index 1bcbd97d6fe..029ea16fba7 100644 --- a/include/AutomatableModelView.h +++ b/include/AutomatableModelView.h @@ -69,12 +69,16 @@ class LMMS_EXPORT AutomatableModelView : public ModelView void addDefaultActions( QMenu* menu ); + void setConversionFactor( float factor ); + float getConversionFactor(); + protected: virtual void mousePressEvent( QMouseEvent* event ); QString m_description; QString m_unit; + float m_conversionFactor; // Factor to be applied when the m_model->value is displayed } ; diff --git a/include/Fader.h b/include/Fader.h index bf1e7215e35..10f8c92ef05 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -99,7 +99,7 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView void setDisplayConversion( bool b ) { - m_displayConversion = b; + m_conversionFactor = b ? 100.0 : 1.0; } inline void setHintText( const QString & _txt_before, @@ -155,8 +155,7 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView QPixmap * m_back; QPixmap * m_leds; QPixmap * m_knob; - - bool m_displayConversion; + bool m_levelsDisplayedInDBFS; int m_moveStartPoint; diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index 039c75c9953..71f3ff8a3bb 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -42,7 +42,8 @@ static float floatFromClipboard(bool* ok=nullptr); AutomatableModelView::AutomatableModelView( ::Model* model, QWidget* _this ) : - ModelView( model, _this ) + ModelView( model, _this ), + m_conversionFactor( 1.0 ) { widget()->setAcceptDrops( true ); widget()->setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); @@ -56,14 +57,14 @@ void AutomatableModelView::addDefaultActions( QMenu* menu ) menu->addAction( embed::getIconPixmap( "reload" ), AutomatableModel::tr( "&Reset (%1%2)" ). - arg( model->displayValue( model->initValue() ) ). + arg( model->initValue() * m_conversionFactor ). arg( m_unit ), model, SLOT( reset() ) ); menu->addSeparator(); menu->addAction( embed::getIconPixmap( "edit_copy" ), AutomatableModel::tr( "&Copy value (%1%2)" ). - arg( model->displayValue( model->value() ) ). + arg( model->value() * m_conversionFactor ). arg( m_unit ), amvSlots, SLOT( copyToClipboard() ) ); @@ -71,7 +72,7 @@ void AutomatableModelView::addDefaultActions( QMenu* menu ) const float valueToPaste = floatFromClipboard(&canPaste); const QString pasteDesc = canPaste ? AutomatableModel::tr( "&Paste value (%1%2)"). - arg( model->displayValue( valueToPaste ) ). + arg( valueToPaste ). arg( m_unit ) : AutomatableModel::tr( "&Paste value"); QAction* pasteAction = menu->addAction( embed::getIconPixmap( "edit_paste" ), @@ -155,8 +156,20 @@ void AutomatableModelView::mousePressEvent( QMouseEvent* event ) } +void AutomatableModelView::setConversionFactor( float factor ) +{ + if( factor != 0.0 ) + { + m_conversionFactor = factor; + } +} +float AutomatableModelView::getConversionFactor() +{ + return m_conversionFactor; +} + AutomatableModelViewSlots::AutomatableModelViewSlots( AutomatableModelView* amv, QObject* parent ) : QObject(), @@ -243,7 +256,7 @@ void AutomatableModelViewSlots::unlinkAllModels() void AutomatableModelViewSlots::copyToClipboard() { QClipboard* clipboard = QApplication::clipboard(); - clipboard->setText(QString::number(m_amv->value())); + clipboard->setText(QString::number(m_amv->value() * m_amv->getConversionFactor())); } void AutomatableModelViewSlots::pasteFromClipboard() @@ -251,7 +264,7 @@ void AutomatableModelViewSlots::pasteFromClipboard() bool isNumber = false; const float number = floatFromClipboard(&isNumber); if (isNumber) { - m_amv->modelUntyped()->setValue(number); + m_amv->modelUntyped()->setValue(number / m_amv->getConversionFactor()); } } diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 1f0a13ec173..089964ec75e 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -72,7 +72,6 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : m_persistentPeak_R( 0.0 ), m_fMinPeak( 0.01f ), m_fMaxPeak( 1.1 ), - m_displayConversion( true ), m_levelsDisplayedInDBFS(false), m_moveStartPoint( -1 ), m_startValue( 0 ), @@ -102,6 +101,8 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : m_knob = s_knob; init(_model, _name); + + m_conversionFactor = 100.0; } @@ -114,7 +115,6 @@ Fader::Fader( FloatModel * model, const QString & name, QWidget * parent, QPixma m_persistentPeak_R( 0.0 ), m_fMinPeak( 0.01f ), m_fMaxPeak( 1.1 ), - m_displayConversion( false ), m_levelsDisplayedInDBFS(false), m_moveStartPoint( -1 ), m_startValue( 0 ), @@ -217,26 +217,13 @@ void Fader::mouseDoubleClickEvent( QMouseEvent* mouseEvent ) bool ok; float newValue; // TODO: dbV handling - if( m_displayConversion ) - { - newValue = QInputDialog::getDouble( this, tr( "Set value" ), - tr( "Please enter a new value between %1 and %2:" ). - arg( model()->minValue() * 100 ). - arg( model()->maxValue() * 100 ), - model()->getRoundedValue() * 100, - model()->minValue() * 100, - model()->maxValue() * 100, model()->getDigitCount(), &ok ) * 0.01f; - } - else - { - newValue = QInputDialog::getDouble( this, tr( "Set value" ), - tr( "Please enter a new value between %1 and %2:" ). - arg( model()->minValue() ). - arg( model()->maxValue() ), - model()->getRoundedValue(), - model()->minValue(), - model()->maxValue(), model()->getDigitCount(), &ok ); - } + newValue = QInputDialog::getDouble( this, tr( "Set value" ), + tr( "Please enter a new value between %1 and %2:" ). + arg( model()->minValue() * m_conversionFactor ). + arg( model()->maxValue() * m_conversionFactor ), + model()->getRoundedValue() * m_conversionFactor, + model()->minValue() * m_conversionFactor, + model()->maxValue() * m_conversionFactor, model()->getDigitCount(), &ok ) / m_conversionFactor; if( ok ) { @@ -330,14 +317,14 @@ void Fader::setPeak_R( float fPeak ) // update tooltip showing value and adjust position while changing fader value void Fader::updateTextFloat() { - if( ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() && m_displayConversion ) + if( ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() && m_conversionFactor == 100.0 ) { s_textFloat->setText( QString("Volume: %1 dBFS"). arg( ampToDbfs( model()->value() ), 3, 'f', 2 ) ); } else { - s_textFloat->setText( m_description + " " + QString("%1 ").arg( m_displayConversion ? model()->value() * 100 : model()->value() ) + " " + m_unit ); + s_textFloat->setText( m_description + " " + QString("%1 ").arg( model()->value() * m_conversionFactor ) + " " + m_unit ); } s_textFloat->moveGlobal( this, QPoint( width() - ( *m_knob ).width() - 5, knobPosY() - 46 ) ); } From 6d160fd77360ffa2bd32f2e6aaba8ca993439fe1 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Mon, 21 Sep 2020 09:04:44 +0200 Subject: [PATCH 088/180] basics: Change sampleFrame to use `std::array` (#5536) ... in order to make standard containers be able to store it. Required for #5532 (#4899) and the recording PR. This includes: * removing the `LocklessRingBuffer` specialization * passing samplerame in `StereoDelay::tick` as a reference Additional cleanups: * removing already unused typedef `sampleFrameA` * add some `const_cast` to make code more readable --- include/LocklessRingBuffer.h | 66 +++++----------------------------- include/lmms_basics.h | 8 ++--- plugins/Delay/StereoDelay.cpp | 2 +- plugins/Delay/StereoDelay.h | 2 +- src/core/Effect.cpp | 4 +-- src/core/SampleBuffer.cpp | 8 ++--- src/core/audio/AudioDevice.cpp | 4 +-- 7 files changed, 22 insertions(+), 72 deletions(-) diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h index d313fd72288..617f7727a89 100644 --- a/include/LocklessRingBuffer.h +++ b/include/LocklessRingBuffer.h @@ -28,84 +28,36 @@ #include #include -#include "lmms_basics.h" -#include "lmms_export.h" #include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" -//! A convenience layer for a realtime-safe and thread-safe multi-reader ring buffer library. +//! A convenience layer for a realtime-safe and thread-safe multi-reader ringbuffer template -class LocklessRingBufferBase +class LocklessRingBuffer { template friend class LocklessRingBufferReader; public: - LocklessRingBufferBase(std::size_t sz) : m_buffer(sz) + LocklessRingBuffer(std::size_t sz) : m_buffer(sz) { m_buffer.touch(); // reserve storage space before realtime operation starts } - ~LocklessRingBufferBase() {}; + ~LocklessRingBuffer() {}; std::size_t capacity() const {return m_buffer.maximum_eventual_write_space();} std::size_t free() const {return m_buffer.write_space();} void wakeAll() {m_notifier.wakeAll();} - -protected: - ringbuffer_t m_buffer; - QWaitCondition m_notifier; -}; - - -// The SampleFrameCopier is required because sampleFrame is just a two-element -// array and therefore does not have a copy constructor needed by std::copy. -class SampleFrameCopier -{ - const sampleFrame* m_src; -public: - SampleFrameCopier(const sampleFrame* src) : m_src(src) {} - void operator()(std::size_t src_offset, std::size_t count, sampleFrame* dest) - { - for (std::size_t i = src_offset; i < src_offset + count; i++, dest++) - { - (*dest)[0] = m_src[i][0]; - (*dest)[1] = m_src[i][1]; - } - } -}; - - -//! Standard ring buffer template for data types with copy constructor. -template -class LocklessRingBuffer : public LocklessRingBufferBase -{ -public: - LocklessRingBuffer(std::size_t sz) : LocklessRingBufferBase(sz) {}; - std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) { - std::size_t written = LocklessRingBufferBase::m_buffer.write(src, cnt); + std::size_t written = LocklessRingBuffer::m_buffer.write(src, cnt); // Let all waiting readers know new data are available. - if (notify) {LocklessRingBufferBase::m_notifier.wakeAll();} + if (notify) {LocklessRingBuffer::m_notifier.wakeAll();} return written; } -}; - -//! Specialized ring buffer template with write function modified to support sampleFrame. -template <> -class LocklessRingBuffer : public LocklessRingBufferBase -{ -public: - LocklessRingBuffer(std::size_t sz) : LocklessRingBufferBase(sz) {}; - - std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) - { - SampleFrameCopier copier(src); - std::size_t written = LocklessRingBufferBase::m_buffer.write_func(copier, cnt); - // Let all waiting readers know new data are available. - if (notify) {LocklessRingBufferBase::m_notifier.wakeAll();} - return written; - } +protected: + ringbuffer_t m_buffer; + QWaitCondition m_notifier; }; diff --git a/include/lmms_basics.h b/include/lmms_basics.h index 77a24649820..f114f362c15 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -32,6 +32,7 @@ #ifdef LMMS_HAVE_STDINT_H #include +#include #endif @@ -127,12 +128,9 @@ const ch_cnt_t SURROUND_CHANNELS = -typedef sample_t sampleFrame[DEFAULT_CHANNELS]; -typedef sample_t surroundSampleFrame[SURROUND_CHANNELS]; +using sampleFrame = std::array; +using surroundSampleFrame = std::array; #define ALIGN_SIZE 16 -#if __GNUC__ -typedef sample_t sampleFrameA[DEFAULT_CHANNELS] __attribute__((__aligned__(ALIGN_SIZE))); -#endif #define STRINGIFY(s) STR(s) diff --git a/plugins/Delay/StereoDelay.cpp b/plugins/Delay/StereoDelay.cpp index d25f6b52b18..f240b93547b 100644 --- a/plugins/Delay/StereoDelay.cpp +++ b/plugins/Delay/StereoDelay.cpp @@ -55,7 +55,7 @@ StereoDelay::~StereoDelay() -void StereoDelay::tick( sampleFrame frame ) +void StereoDelay::tick( sampleFrame& frame ) { m_writeIndex = ( m_writeIndex + 1 ) % ( int )m_maxLength; int readIndex = m_writeIndex - m_length; diff --git a/plugins/Delay/StereoDelay.h b/plugins/Delay/StereoDelay.h index a0dbdc22064..afea59b9af1 100644 --- a/plugins/Delay/StereoDelay.h +++ b/plugins/Delay/StereoDelay.h @@ -45,7 +45,7 @@ class StereoDelay m_feedback = feedback; } - void tick( sampleFrame frame ); + void tick( sampleFrame& frame ); void setSampleRate( int sampleRate ); private: diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index c842977532f..f1e32669e4f 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -203,8 +203,8 @@ void Effect::resample( int _i, const sampleFrame * _src_buf, } m_srcData[_i].input_frames = _frames; m_srcData[_i].output_frames = Engine::mixer()->framesPerPeriod(); - m_srcData[_i].data_in = (float *) _src_buf[0]; - m_srcData[_i].data_out = _dst_buf[0]; + m_srcData[_i].data_in = const_cast(_src_buf[0].data()); + m_srcData[_i].data_out = _dst_buf[0].data (); m_srcData[_i].src_ratio = (double) _dst_sr / _src_sr; m_srcData[_i].end_of_input = 0; int error; diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index cd943638d4f..368600752f0 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -693,8 +693,8 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, // Generate output src_data.data_in = getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards, - loopStartFrame, loopEndFrame, endFrame )[0]; - src_data.data_out = _ab[0]; + loopStartFrame, loopEndFrame, endFrame )->data (); + src_data.data_out = _ab->data (); src_data.input_frames = fragment_size; src_data.output_frames = _frames; src_data.src_ratio = 1.0 / freq_factor; @@ -1196,8 +1196,8 @@ SampleBuffer * SampleBuffer::resample( const sample_rate_t _src_sr, { SRC_DATA src_data; src_data.end_of_input = 1; - src_data.data_in = data[0]; - src_data.data_out = dst_buf[0]; + src_data.data_in = data->data (); + src_data.data_out = dst_buf->data (); src_data.input_frames = frames; src_data.output_frames = dst_frames; src_data.src_ratio = (double) _dst_sr / _src_sr; diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index f794602299b..a57fa2bd636 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -194,8 +194,8 @@ fpp_t AudioDevice::resample( const surroundSampleFrame * _src, } m_srcData.input_frames = _frames; m_srcData.output_frames = _frames; - m_srcData.data_in = (float *) _src[0]; - m_srcData.data_out = _dst[0]; + m_srcData.data_in = const_cast(_src[0].data()); + m_srcData.data_out = _dst[0].data (); m_srcData.src_ratio = (double) _dst_sr / _src_sr; m_srcData.end_of_input = 0; int error; From 9e401828aac306be4a66d03adbe903b758bcc269 Mon Sep 17 00:00:00 2001 From: Spekular Date: Mon, 21 Sep 2020 16:50:55 +0200 Subject: [PATCH 089/180] Don't give clips a hidden default name (Fix #5528) (#5621) * Automatic formatting changes * Give clips an empty name by default, display all names - Stop giving clips the same name as their parent track on creation - Stop hiding clip names that match the parent track name - Never rename clips on track rename - Never clear clip name when a clip is copied to another track - Create an upgrade routine to clear default names from old projects (< 1.3.0-alpha-1) - Bump version to 1.3.0-alpha-1 * Revert now-unnecessary version bump * Merge with master and fix conflicts * Formatting changes from review * Change weird for loop conditions * Properly revert AutomationPatter.h changes * Only clear names that match our parent track, be more generous with use of legacyFileVersion Co-authored-by: Hyunjin Song --- include/DataFile.h | 2 +- include/ProjectVersion.h | 1 + src/core/DataFile.cpp | 106 ++++++++++++++++++--------------- src/core/Track.cpp | 7 --- src/tracks/BBTrack.cpp | 5 +- src/tracks/InstrumentTrack.cpp | 13 +--- src/tracks/Pattern.cpp | 10 +--- 7 files changed, 66 insertions(+), 78 deletions(-) diff --git a/include/DataFile.h b/include/DataFile.h index 38a1fa7631c..5d6ead5adb3 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -114,6 +114,7 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); void upgrade_1_3_0(); + void upgrade_noHiddenClipNames(); // List of all upgrade methods static const std::vector UPGRADE_METHODS; @@ -141,4 +142,3 @@ class LMMS_EXPORT DataFile : public QDomDocument #endif - diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index e938ccc81c7..94ee9c6ba89 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -45,6 +45,7 @@ class ProjectVersion ProjectVersion(QString version, CompareType c = All); ProjectVersion(const char * version, CompareType c = All); + const QString& getVersion() const { return m_version; } int getMajor() const { return m_major; } int getMinor() const { return m_minor; } int getPatch() const { return m_patch; } diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 2ee74c41890..c1b48af38c8 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -59,7 +59,7 @@ const std::vector DataFile::UPGRADE_METHODS = { &DataFile::upgrade_0_4_0_beta1 , &DataFile::upgrade_0_4_0_rc2, &DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0, &DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3, - &DataFile::upgrade_1_3_0 + &DataFile::upgrade_1_3_0 , &DataFile::upgrade_noHiddenClipNames }; // Vector of all versions that have upgrade routines. @@ -1355,6 +1355,35 @@ void DataFile::upgrade_1_3_0() } } +void DataFile::upgrade_noHiddenClipNames() +{ + QDomNodeList tracks = elementsByTagName("track"); + + auto clearDefaultNames = [](QDomNodeList clips, QString trackName) + { + for (int j = 0; j < clips.size(); ++j) + { + QDomElement clip = clips.item(j).toElement(); + QString clipName = clip.attribute("name", ""); + if (clipName == trackName) { clip.setAttribute("name", ""); } + } + }; + + for (int i = 0; i < tracks.size(); ++i) + { + QDomElement track = tracks.item(i).toElement(); + QString trackName = track.attribute("name", ""); + + QDomNodeList instClips = track.elementsByTagName("pattern"); + QDomNodeList autoClips = track.elementsByTagName("automationpattern"); + QDomNodeList bbClips = track.elementsByTagName("bbtco"); + + clearDefaultNames(instClips, trackName); + clearDefaultNames(autoClips, trackName); + clearDefaultNames(bbClips, trackName); + } +} + void DataFile::upgrade() { @@ -1430,62 +1459,45 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); - if( root.hasAttribute( "version" ) ) + if (!root.hasAttribute("version") || root.attribute("version")=="1.0") { - if( root.attribute( "version" ) == "1.0" ){ - // The file versioning is now a unsigned int, not maj.min, so we use - // legacyFileVersion() to retrieve the appropriate version - m_fileVersion = legacyFileVersion(); - } - else - { - bool success; - m_fileVersion = root.attribute( "version" ).toUInt( &success ); - if( !success ) qWarning("File Version conversion failure."); - } + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); } - if( root.hasAttribute( "creatorversion" ) ) + if (root.hasAttribute("creatorversion")) { // compareType defaults to All, so it doesn't have to be set here - ProjectVersion createdWith = root.attribute( "creatorversion" ); + ProjectVersion createdWith = root.attribute("creatorversion"); ProjectVersion openedWith = LMMS_VERSION; - if ( createdWith != openedWith ) - { - if( createdWith.setCompareType( ProjectVersion::Minor ) != - openedWith.setCompareType( ProjectVersion::Minor ) ) - { - if( gui != nullptr && root.attribute( "type" ) == "song" ) - { - TextFloat::displayMessage( - SongEditor::tr( "Version difference" ), - SongEditor::tr( - "This %1 was created with " - "LMMS %2." - ).arg( - _sourceFile.endsWith( ".mpt" ) ? - SongEditor::tr( "template" ) : - SongEditor::tr( "project" ) - ) - .arg( root.attribute( "creatorversion" ) ), - embed::getIconPixmap( "whatsthis", 24, 24 ), - 2500 - ); - } - } - - // the upgrade needs to happen after the warning as it updates the project version. - if( createdWith.setCompareType( ProjectVersion::Build ) - < openedWith ) - { - upgrade(); - } + if (createdWith < openedWith) { upgrade(); } + + if (createdWith.setCompareType(ProjectVersion::Minor) + != openedWith.setCompareType(ProjectVersion::Minor) + && gui != nullptr && root.attribute("type") == "song" + ){ + auto projectType = _sourceFile.endsWith(".mpt") ? + SongEditor::tr("template") : SongEditor::tr("project"); + + TextFloat::displayMessage( + SongEditor::tr("Version difference"), + SongEditor::tr("This %1 was created with LMMS %2") + .arg(projectType).arg(createdWith.getVersion()), + embed::getIconPixmap("whatsthis", 24, 24), + 2500 + ); } } - m_content = root.elementsByTagName( typeName( m_type ) ). - item( 0 ).toElement(); + m_content = root.elementsByTagName(typeName(m_type)).item(0).toElement(); } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index e52270fdd83..c7f6d62b0b5 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1865,13 +1865,6 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, { tco->selectViewOnCreate( true ); } - - //check tco name, if the same as source track name dont copy - QString sourceTrackName = outerTCOElement.attributeNode( "trackName" ).value(); - if( tco->name() == sourceTrackName ) - { - tco->setName( "" ); - } } AutomationPattern::resolveAllIDs(); diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index a779e2ea49b..ec6b4042004 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -290,10 +290,7 @@ void BBTCOView::openInBBEditor() -void BBTCOView::resetName() -{ - m_bbTCO->setName( m_bbTCO->getTrack()->name() ); -} +void BBTCOView::resetName() { m_bbTCO->setName(""); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 8d0cb24316e..5a51bb8aced 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -151,7 +151,7 @@ InstrumentTrack::~InstrumentTrack() autoAssignMidiDevice(false); s_autoAssignedTrack = NULL; } - + // kill all running notes and the iph silenceAllNotes( true ); @@ -530,17 +530,6 @@ void InstrumentTrack::deleteNotePluginData( NotePlayHandle* n ) void InstrumentTrack::setName( const QString & _new_name ) { - // when changing name of track, also change name of those patterns, - // which have the same name as the instrument-track - for( int i = 0; i < numOfTCOs(); ++i ) - { - Pattern* p = dynamic_cast( getTCO( i ) ); - if( ( p != NULL && p->name() == name() ) || p->name() == "" ) - { - p->setName( _new_name ); - } - } - Track::setName( _new_name ); m_midiPort.setName( name() ); m_audioPort.setName( name() ); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 52259c70733..e5de8b83b68 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -56,7 +56,6 @@ Pattern::Pattern( InstrumentTrack * _instrument_track ) : m_patternType( BeatPattern ), m_steps( MidiTime::stepsPerBar() ) { - setName( _instrument_track->name() ); if( _instrument_track->trackContainer() == Engine::getBBTrackContainer() ) { @@ -647,10 +646,7 @@ void PatternView::setGhostInPianoRoll() -void PatternView::resetName() -{ - m_pat->setName( m_pat->m_instrumentTrack->name() ); -} +void PatternView::resetName() { m_pat->setName(""); } @@ -885,8 +881,8 @@ void PatternView::paintEvent( QPaintEvent * ) // Check whether we will paint a text box and compute its potential height // This is needed so we can paint the notes underneath it. - bool const isDefaultName = m_pat->name() == m_pat->instrumentTrack()->name(); - bool const drawTextBox = !beatPattern && !isDefaultName; + bool const drawName = !m_pat->name().isEmpty(); + bool const drawTextBox = !beatPattern && drawName; // TODO Warning! This might cause problems if TrackContentObjectView::paintTextLabel changes int textBoxHeight = 0; From 454e0472914550dce5e80d976283d56faaa55832 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 11 Jun 2020 08:19:44 +0200 Subject: [PATCH 090/180] Add lv2 packages to macOS CI --- .circleci/config.yml | 2 +- .travis/osx..install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fe5b1f2fd7c..408fef721a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -181,7 +181,7 @@ jobs: command: | # uninstall Homebrew's python 2 to prevent errors on brew install brew uninstall python@2 || true - brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla + brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio lilv lv2 stk fluid-synth portaudio fltk qt5 carla - run: name: Install nodejs dependencies command: npm install -g appdmg diff --git a/.travis/osx..install.sh b/.travis/osx..install.sh index 93d478c40f4..42bf66acab4 100755 --- a/.travis/osx..install.sh +++ b/.travis/osx..install.sh @@ -2,7 +2,7 @@ set -e -PACKAGES="cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio node fltk qt carla" +PACKAGES="cmake pkg-config libogg libvorbis lame libsndfile libsamplerate lilv lv2 jack sdl libgig libsoundio stk fluid-synth portaudio node fltk qt carla" if "${TRAVIS}"; then PACKAGES="$PACKAGES ccache" From c3c80159fd286c0fdd4670c2343ac33787099272 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 24 Sep 2020 09:45:31 +0200 Subject: [PATCH 091/180] Add missing include --- include/Lv2UridMap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Lv2UridMap.h b/include/Lv2UridMap.h index f1ddb6253b7..39cfcc44fb2 100644 --- a/include/Lv2UridMap.h +++ b/include/Lv2UridMap.h @@ -31,6 +31,7 @@ #include #include // TODO: use semaphore, even though this is not realtime critical +#include #include #include From 6546857d213254b33feb2b075946292081ec2f52 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 24 Sep 2020 10:18:50 +0200 Subject: [PATCH 092/180] Move or remove unused slots --- plugins/Lv2Instrument/Lv2Instrument.cpp | 7 ------- plugins/Lv2Instrument/Lv2Instrument.h | 4 ---- src/gui/Lv2ViewBase.cpp | 7 +++++++ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 0f7534ac8c9..d5ba48787fb 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -271,13 +271,6 @@ void Lv2InsView::dropEvent(QDropEvent *_de) -void Lv2InsView::toggleUI() -{ -} - - - - void Lv2InsView::modelChanged() { Lv2ViewBase::modelChanged(castModel()); diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 6451d49cdcf..4fb2e1e34c3 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -111,10 +111,6 @@ class Lv2InsView : public InstrumentView, public Lv2ViewBase void dragEnterEvent(QDragEnterEvent *_dee) override; void dropEvent(QDropEvent *_de) override; -private slots: - void reloadPlugin(); - void toggleUI(); - private: void modelChanged() override; }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 9b9217e48b2..488705bcb66 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -202,6 +202,13 @@ Lv2ViewBase::~Lv2ViewBase() { +void Lv2ViewBase::toggleUI() +{ +} + + + + void Lv2ViewBase::toggleHelp(bool visible) { if (m_helpWindow) From e7adcbd8473a5c85ef3cbd6f8ebc249befcd1dec Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 27 Sep 2020 17:50:44 +0200 Subject: [PATCH 093/180] Update ringbuffer submodule --- src/3rdparty/ringbuffer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/ringbuffer b/src/3rdparty/ringbuffer index 82ed7cfb9ad..ea00e1fc2b8 160000 --- a/src/3rdparty/ringbuffer +++ b/src/3rdparty/ringbuffer @@ -1 +1 @@ -Subproject commit 82ed7cfb9ad40467421d8b14ca1af0350e92613c +Subproject commit ea00e1fc2b821a0e06d78e8cc13ac06e6b4f72f5 From 173b1fadf8f8e709167d9481799bb741aec39212 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 28 Sep 2020 17:58:14 +0200 Subject: [PATCH 094/180] Improve control flow in Lv2 classes No functional change --- src/core/lv2/Lv2Ports.cpp | 10 ++- src/core/lv2/Lv2Proc.cpp | 132 ++++++++++++++++++++------------------ 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index ae2d26d4971..48fe47f604d 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -125,6 +125,7 @@ std::vector Meta::get(const LilvPlugin *plugin, m_def = .0f; m_min = .0f; m_max = .0f; + m_type = Type::Unknown; if (isA(LV2_CORE__ControlPort)) { m_type = Type::Control; @@ -162,14 +163,17 @@ std::vector Meta::get(const LilvPlugin *plugin, } } else if (isA(LV2_CORE__AudioPort)) { m_type = Type::Audio; } - else if (isA(LV2_CORE__CVPort)) { + else if (isA(LV2_CORE__CVPort)) + { issue(badPortType, "cvPort"); m_type = Type::Cv; - } else { + } + + if(m_type == Type::Unknown) + { if (m_optional) { m_used = false; } else { issue(PluginIssueType::unknownPortType, portName); - m_type = Type::Unknown; } } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index ceb9b17123e..9046001ab13 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -311,78 +311,84 @@ void Lv2Proc::createPort(std::size_t portNum) const LilvPort* lilvPort = lilv_plugin_get_port_by_index(m_plugin, static_cast(portNum)); Lv2Ports::PortBase* port; - if (meta.m_type == Lv2Ports::Type::Control) + + switch (meta.m_type) { - Lv2Ports::Control* ctrl = new Lv2Ports::Control; - if (meta.m_flow == Lv2Ports::Flow::Input) + case Lv2Ports::Type::Control: { - AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); - QString dispName = lilv_node_as_string(node.get()); - switch (meta.m_vis) + Lv2Ports::Control* ctrl = new Lv2Ports::Control; + if (meta.m_flow == Lv2Ports::Flow::Input) { - case Lv2Ports::Vis::None: - { - // allow ~1000 steps - float stepSize = (meta.m_max - meta.m_min) / 1000.0f; - - // make multiples of 0.01 (or 0.1 for larger values) - float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; - stepSize -= fmodf(stepSize, minStep); - stepSize = std::max(stepSize, minStep); - - ctrl->m_connectedModel.reset( - new FloatModel(meta.m_def, meta.m_min, meta.m_max, - stepSize, nullptr, dispName)); - break; - } - case Lv2Ports::Vis::Integer: - ctrl->m_connectedModel.reset( - new IntModel(static_cast(meta.m_def), - static_cast(meta.m_min), - static_cast(meta.m_max), - nullptr, dispName)); - break; - case Lv2Ports::Vis::Enumeration: + AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); + QString dispName = lilv_node_as_string(node.get()); + switch (meta.m_vis) { - ComboBoxModel* comboModel - = new ComboBoxModel( - nullptr, dispName); - LilvScalePoints* sps = - lilv_port_get_scale_points(m_plugin, lilvPort); - LILV_FOREACH(scale_points, i, sps) + case Lv2Ports::Vis::None: { - const LilvScalePoint* sp = lilv_scale_points_get(sps, i); - ctrl->m_scalePointMap.push_back(lilv_node_as_float( - lilv_scale_point_get_value(sp))); - comboModel->addItem( - lilv_node_as_string( - lilv_scale_point_get_label(sp))); + // allow ~1000 steps + float stepSize = (meta.m_max - meta.m_min) / 1000.0f; + + // make multiples of 0.01 (or 0.1 for larger values) + float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; + stepSize -= fmodf(stepSize, minStep); + stepSize = std::max(stepSize, minStep); + + ctrl->m_connectedModel.reset( + new FloatModel(meta.m_def, meta.m_min, meta.m_max, + stepSize, nullptr, dispName)); + break; } - lilv_scale_points_free(sps); - ctrl->m_connectedModel.reset(comboModel); - break; + case Lv2Ports::Vis::Integer: + ctrl->m_connectedModel.reset( + new IntModel(static_cast(meta.m_def), + static_cast(meta.m_min), + static_cast(meta.m_max), + nullptr, dispName)); + break; + case Lv2Ports::Vis::Enumeration: + { + ComboBoxModel* comboModel + = new ComboBoxModel( + nullptr, dispName); + LilvScalePoints* sps = + lilv_port_get_scale_points(m_plugin, lilvPort); + LILV_FOREACH(scale_points, i, sps) + { + const LilvScalePoint* sp = lilv_scale_points_get(sps, i); + ctrl->m_scalePointMap.push_back(lilv_node_as_float( + lilv_scale_point_get_value(sp))); + comboModel->addItem( + lilv_node_as_string( + lilv_scale_point_get_label(sp))); + } + lilv_scale_points_free(sps); + ctrl->m_connectedModel.reset(comboModel); + break; + } + case Lv2Ports::Vis::Toggled: + ctrl->m_connectedModel.reset( + new BoolModel(static_cast(meta.m_def), + nullptr, dispName)); + break; } - case Lv2Ports::Vis::Toggled: - ctrl->m_connectedModel.reset( - new BoolModel(static_cast(meta.m_def), - nullptr, dispName)); - break; } + port = ctrl; + break; } - port = ctrl; - } - else if (meta.m_type == Lv2Ports::Type::Audio) - { - Lv2Ports::Audio* audio = - new Lv2Ports::Audio( - static_cast( - Engine::mixer()->framesPerPeriod()), - portIsSideChain(m_plugin, lilvPort), - portIsOptional(m_plugin, lilvPort) - ); - port = audio; - } else { - port = new Lv2Ports::Unknown; + case Lv2Ports::Type::Audio: + { + Lv2Ports::Audio* audio = + new Lv2Ports::Audio( + static_cast( + Engine::mixer()->framesPerPeriod()), + portIsSideChain(m_plugin, lilvPort), + portIsOptional(m_plugin, lilvPort) + ); + port = audio; + break; + } + default: + port = new Lv2Ports::Unknown; } // `meta` is of class `Lv2Ports::Meta` and `port` is of a child class From 31f79a22634d55bac42163ebb4600c075464ee7e Mon Sep 17 00:00:00 2001 From: Veratil Date: Mon, 16 Sep 2019 16:21:37 -0500 Subject: [PATCH 095/180] Add auto-highlight scale and key selection --- include/PianoRoll.h | 5 +- src/gui/editors/PianoRoll.cpp | 99 ++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 7a99e7b1f80..e3e4b312085 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -195,12 +195,13 @@ protected slots: void zoomingYChanged(); void quantizeChanged(); void noteLengthChanged(); + void keyChanged(); void quantizeNotes(); void updateSemiToneMarkerMenu(); void changeNoteEditMode( int i ); - void markSemiTone( int i ); + void markSemiTone(int i, bool fromMenu = true); void hidePattern( Pattern* pattern ); @@ -311,6 +312,7 @@ protected slots: ComboBoxModel m_zoomingYModel; ComboBoxModel m_quantizeModel; ComboBoxModel m_noteLenModel; + ComboBoxModel m_keyModel; ComboBoxModel m_scaleModel; ComboBoxModel m_chordModel; @@ -507,6 +509,7 @@ private slots: ComboBox * m_zoomingYComboBox; ComboBox * m_quantizeComboBox; ComboBox * m_noteLenComboBox; + ComboBox * m_keyComboBox; ComboBox * m_scaleComboBox; ComboBox * m_chordComboBox; QPushButton * m_clearGhostButton; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index c63225440dc..0eb049ddcae 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -374,6 +374,13 @@ PianoRoll::PianoRoll() : connect( &m_noteLenModel, SIGNAL( dataChanged() ), this, SLOT( noteLengthChanged() ) ); + // Set up key selection dropdown + m_keyModel.addItem(tr("No key")); + // Use piano roll note strings for key dropdown + for (int i = 0; i < 12; i++) { m_keyModel.addItem(s_noteStrings[i]); } + m_keyModel.setValue(0); // start with "No key" + connect(&m_keyModel, &ComboBoxModel::dataChanged, this, &PianoRoll::keyChanged); + // Set up scale model const InstrumentFunctionNoteStacking::ChordTable& chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance(); @@ -388,6 +395,8 @@ PianoRoll::PianoRoll() : } m_scaleModel.setValue( 0 ); + // connect scale change to key change so it auto-highlights with scale as well + connect(&m_scaleModel, &ComboBoxModel::dataChanged, this, &PianoRoll::keyChanged); // change can update m_semiToneMarkerMenu connect( &m_scaleModel, SIGNAL( dataChanged() ), this, SLOT( updateSemiToneMarkerMenu() ) ); @@ -485,11 +494,17 @@ void PianoRoll::changeNoteEditMode( int i ) } -void PianoRoll::markSemiTone( int i ) +void PianoRoll::markSemiTone(int i, bool fromMenu) { - const int key = m_pianoKeySelected; + const int key = fromMenu + ? getKey(mapFromGlobal(m_semiToneMarkerMenu->pos()).y()) + : m_keyModel.value() - 1; const InstrumentFunctionNoteStacking::Chord * chord = nullptr; + // if "No key" is selected, key is -1, unmark all semitones + // or if scale changed from toolbar to "No scale", unmark all semitones + if (!fromMenu && (key < 0 || m_scaleModel.value() == 0)) { i = stmaUnmarkAll; } + switch( static_cast( i ) ) { case stmaUnmarkAll: @@ -578,6 +593,8 @@ void PianoRoll::markSemiTone( int i ) std::sort( m_markedSemiTones.begin(), m_markedSemiTones.end(), std::greater() ); QList::iterator new_end = std::unique( m_markedSemiTones.begin(), m_markedSemiTones.end() ); m_markedSemiTones.erase( new_end, m_markedSemiTones.end() ); + // until we move the mouse the window won't update, force redraw + update(); } @@ -4139,6 +4156,10 @@ void PianoRoll::noteLengthChanged() update(); } +void PianoRoll::keyChanged() +{ + markSemiTone(stmaMarkCurrentScale, false); +} int PianoRoll::quantization() const { @@ -4380,6 +4401,12 @@ PianoRollWindow::PianoRollWindow() : m_noteLenComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_noteLenComboBox->setToolTip( tr( "Note length") ); + // setup key-stuff + m_keyComboBox = new ComboBox(m_toolBar); + m_keyComboBox->setModel(&m_editor->m_keyModel); + m_keyComboBox->setFixedSize(72, ComboBox::DEFAULT_HEIGHT); + m_keyComboBox->setToolTip(tr("Key")); + // setup scale-stuff QLabel * scale_lbl = new QLabel( m_toolBar ); scale_lbl->setPixmap( embed::getIconPixmap( "scale" ) ); @@ -4406,27 +4433,61 @@ PianoRollWindow::PianoRollWindow() : connect( m_clearGhostButton, SIGNAL( clicked() ), m_editor, SLOT( clearGhostPattern() ) ); connect( m_editor, SIGNAL( ghostPatternSet( bool ) ), this, SLOT( ghostPatternSet( bool ) ) ); - zoomAndNotesToolBar->addWidget( zoom_lbl ); - zoomAndNotesToolBar->addWidget( m_zoomingComboBox ); - - zoomAndNotesToolBar->addWidget(zoom_y_lbl); - zoomAndNotesToolBar->addWidget(m_zoomingYComboBox); - + // Wrap label icons and comboboxes in a single widget so when + // the window is resized smaller in width it hides both + QWidget * zoom_widget = new QWidget(); + QHBoxLayout * zoom_hbox = new QHBoxLayout(); + zoom_hbox->setContentsMargins(0, 0, 0, 0); + zoom_hbox->addWidget(zoom_lbl); + zoom_hbox->addWidget(m_zoomingComboBox); + zoom_widget->setLayout(zoom_hbox); + zoomAndNotesToolBar->addWidget(zoom_widget); + + QWidget * zoomY_widget = new QWidget(); + QHBoxLayout * zoomY_hbox = new QHBoxLayout(); + zoomY_hbox->setContentsMargins(0, 0, 0, 0); + zoomY_hbox->addWidget(zoom_y_lbl); + zoomY_hbox->addWidget(m_zoomingYComboBox); + zoomY_widget->setLayout(zoomY_hbox); + zoomAndNotesToolBar->addWidget(zoomY_widget); + + QWidget * quantize_widget = new QWidget(); + QHBoxLayout * quantize_hbox = new QHBoxLayout(); + quantize_hbox->setContentsMargins(0, 0, 0, 0); + quantize_hbox->addWidget(quantize_lbl); + quantize_hbox->addWidget(m_quantizeComboBox); + quantize_widget->setLayout(quantize_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( quantize_lbl ); - zoomAndNotesToolBar->addWidget( m_quantizeComboBox ); - + zoomAndNotesToolBar->addWidget(quantize_widget); + + QWidget * note_widget = new QWidget(); + QHBoxLayout * note_hbox = new QHBoxLayout(); + note_hbox->setContentsMargins(0, 0, 0, 0); + note_hbox->addWidget(note_len_lbl); + note_hbox->addWidget(m_noteLenComboBox); + note_widget->setLayout(note_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( note_len_lbl ); - zoomAndNotesToolBar->addWidget( m_noteLenComboBox ); - + zoomAndNotesToolBar->addWidget(note_widget); + + QWidget * scale_widget = new QWidget(); + QHBoxLayout * scale_hbox = new QHBoxLayout(); + scale_hbox->setContentsMargins(0, 0, 0, 0); + scale_hbox->addWidget(scale_lbl); + // Add the key selection between scale label and key + scale_hbox->addWidget(m_keyComboBox); + scale_hbox->addWidget(m_scaleComboBox); + scale_widget->setLayout(scale_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( scale_lbl ); - zoomAndNotesToolBar->addWidget( m_scaleComboBox ); - + zoomAndNotesToolBar->addWidget(scale_widget); + + QWidget * chord_widget = new QWidget(); + QHBoxLayout * chord_hbox = new QHBoxLayout(); + chord_hbox->setContentsMargins(0, 0, 0, 0); + chord_hbox->addWidget(chord_lbl); + chord_hbox->addWidget(m_chordComboBox); + chord_widget->setLayout(chord_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( chord_lbl ); - zoomAndNotesToolBar->addWidget( m_chordComboBox ); + zoomAndNotesToolBar->addWidget(chord_widget); zoomAndNotesToolBar->addSeparator(); zoomAndNotesToolBar->addWidget( m_clearGhostButton ); From cc87dfbb5256cd43d5c32efff8e3cd307e70168f Mon Sep 17 00:00:00 2001 From: Spekular Date: Wed, 30 Sep 2020 11:26:58 +0200 Subject: [PATCH 096/180] Fix relativeOrAbsolute bug with baseQDir in PathUtils.cpp (#5689) * Fix relativeOrAbsolute bug with baseQDir in PathUtils.cpp If `base` is `Absolute`, `baseQDir(base)` will point to the working directory. It will result in undefined behaviors. To fix this, update relativeOrAbsolute to explicitly return an absolute path when the target base is Absolute. Also make baseQDir return QDir::root() for Absolute base instead of QDir(""), because the latter represents the working directory. Co-authored-by: Hyunjin Song --- src/core/PathUtil.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp index ab81c49417c..5881db9f40f 100644 --- a/src/core/PathUtil.cpp +++ b/src/core/PathUtil.cpp @@ -36,7 +36,11 @@ namespace PathUtil return QDir::cleanPath(loc) + "/"; } - QDir baseQDir (const Base base) { return QDir(baseLocation(base)); } + QDir baseQDir (const Base base) + { + if (base == Base::Absolute) { return QDir::root(); } + return QDir(baseLocation(base)); + } QString basePrefix(const Base base) { @@ -123,6 +127,7 @@ namespace PathUtil { if (input.isEmpty()) { return input; } QString absolutePath = toAbsolute(input); + if (base == Base::Absolute) { return absolutePath; } QString relativePath = baseQDir(base).relativeFilePath(absolutePath); return relativePath.startsWith("..") ? absolutePath : relativePath; } From 89b13280de93ad2131c12dc234fa65cd10855996 Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Sat, 3 Oct 2020 07:05:55 +0200 Subject: [PATCH 097/180] LB302: Use consistent cutoff frequency on mulitple sample rates (#5618) --- plugins/lb302/lb302.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index cc671749db1..e9a518e3d0c 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -74,7 +74,7 @@ //#define engine::mixer()->processingSampleRate() 44100.0f - +const float sampleRateCutoff = 44100.0f; extern "C" { @@ -228,8 +228,11 @@ void lb302Filter3Pole::envRecalc() // e0 is adjusted for Hz and doesn't need ENVINC w = vcf_e0 + vcf_c0; k = (fs->cutoff > 0.975)?0.975:fs->cutoff; + // sampleRateCutoff should not be changed to anything dynamic that is outside the + // scope of lb302 (like e.g. the mixers sample rate) as this changes the filters cutoff + // behavior without any modification to its controls. kfco = 50.f + (k)*((2300.f-1600.f*(fs->envmod))+(w) * - (700.f+1500.f*(k)+(1500.f+(k)*(Engine::mixer()->processingSampleRate()/2.f-6000.f)) * + (700.f+1500.f*(k)+(1500.f+(k)*(sampleRateCutoff/2.f-6000.f)) * (fs->envmod)) ); //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 From e5f1007ebbec5b9ae6d576712bf2ca4676d09fbf Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Mon, 6 Nov 2017 22:29:10 +0200 Subject: [PATCH 098/180] SampleTrack: Fix TCO not being played on the first tick (if it starts on tick 0) --- src/tracks/SampleTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index d73b62cbaff..80d41adea7f 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -674,7 +674,7 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, if( _start >= sTco->startPosition() && _start < sTco->endPosition() ) { - if( sTco->isPlaying() == false && _start > sTco->startPosition() + sTco->startTimeOffset() ) + if( sTco->isPlaying() == false && _start >= (sTco->startPosition() + sTco->startTimeOffset()) ) { auto bufferFramesPerTick = Engine::framesPerTick (sTco->sampleBuffer ()->sampleRate ()); f_cnt_t sampleStart = bufferFramesPerTick * ( _start - sTco->startPosition() - sTco->startTimeOffset() ); From 109a7c47356485ea6126f17f13747043e0272236 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sat, 3 Oct 2020 21:31:13 +0200 Subject: [PATCH 099/180] Keyboard shortcuts to preview/add sounds from sidebar (#5427) - Extract file item preview start and end into new methods `previewFileItem` and `stopPreview`. - Add event handlers: - `keyPressEvent` to allow auto preview (on up/down arrow navigation), manual preview (space), and send to editors (enter) - `keyReleaseEvent` to end previews when preview key is released - `hideEvent` to end previews when switching sidebar tab or hiding sidebar - Functions that operate on a `FileItem` now take it as an argument instead of using a member variable - `getContextActions` provides menu items for sending clips to the song editor and BB editor with minimal duplicate code - Some formatting changes in affected code - Replace many instances of `NULL` with `nullptr` --- include/FileBrowser.h | 30 ++- src/gui/FileBrowser.cpp | 465 +++++++++++++++++++++++++--------------- 2 files changed, 314 insertions(+), 181 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index d36eacf0775..a890506bdf3 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -63,9 +63,9 @@ class FileBrowser : public SideBarWidget private slots: void reloadTree( void ); - void expandItems( QTreeWidgetItem * item=NULL, QList expandedDirs = QList() ); + void expandItems( QTreeWidgetItem * item=nullptr, QList expandedDirs = QList() ); // call with item=NULL to filter the entire tree - bool filterItems( const QString & filter, QTreeWidgetItem * item=NULL ); + bool filterItems( const QString & filter, QTreeWidgetItem * item=nullptr ); void giveFocusToFilter(); private: @@ -105,29 +105,38 @@ class FileBrowserTreeWidget : public QTreeWidget void mousePressEvent( QMouseEvent * me ) override; void mouseMoveEvent( QMouseEvent * me ) override; void mouseReleaseEvent( QMouseEvent * me ) override; + void keyPressEvent( QKeyEvent * ke ) override; + void keyReleaseEvent( QKeyEvent * ke ) override; + void hideEvent( QHideEvent * he ) override; private: + //! Start a preview of a file item + void previewFileItem(FileItem* file); + //! If a preview is playing, stop it. + void stopPreview(); + void handleFile( FileItem * fi, InstrumentTrack * it ); - void openInNewInstrumentTrack( TrackContainer* tc ); + void openInNewInstrumentTrack( TrackContainer* tc, FileItem* item ); bool m_mousePressed; QPoint m_pressPos; + //! This should only be accessed or modified when m_pphMutex is held PlayHandle* m_previewPlayHandle; QMutex m_pphMutex; - FileItem * m_contextMenuItem; + QList getContextActions(FileItem* item, bool songEditor); private slots: void activateListItem( QTreeWidgetItem * item, int column ); - void openInNewInstrumentTrackBBE( void ); - void openInNewInstrumentTrackSE( void ); - void sendToActiveInstrumentTrack( void ); + void openInNewInstrumentTrack( FileItem* item, bool songEditor ); + bool openInNewSampleTrack( FileItem* item ); + void sendToActiveInstrumentTrack( FileItem* item ); void updateDirectory( QTreeWidgetItem * item ); - void openContainingFolder(); + void openContainingFolder( FileItem* item ); } ; @@ -234,6 +243,11 @@ class FileItem : public QTreeWidgetItem return( m_handling ); } + inline bool isTrack( void ) const + { + return m_handling == LoadAsPreset || m_handling == LoadByPlugin; + } + QString extension( void ); static QString extension( const QString & file ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index b37bf03f207..0524146069e 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -50,6 +50,7 @@ #include "PluginFactory.h" #include "PresetPreviewPlayHandle.h" #include "SamplePlayHandle.h" +#include "SampleTrack.h" #include "Song.h" #include "StringPairDrag.h" #include "TextFloat.h" @@ -173,7 +174,7 @@ void FileBrowser::reloadTree( void ) { addItems( *it ); } - expandItems(NULL, expandedDirs); + expandItems(nullptr, expandedDirs); m_filterEdit->setText( text ); filterItems( text ); } @@ -240,7 +241,7 @@ void FileBrowser::addItems(const QString & path ) { Directory * d = dynamic_cast( m_fileBrowserTreeWidget->topLevelItem( i ) ); - if( d == NULL || cur_file < d->text( 0 ) ) + if( d == nullptr || cur_file < d->text( 0 ) ) { // insert before item, we're done Directory *dd = new Directory( cur_file, path, @@ -300,13 +301,12 @@ void FileBrowser::addItems(const QString & path ) void FileBrowser::keyPressEvent(QKeyEvent * ke ) { - if( ke->key() == Qt::Key_F5 ) - { - reloadTree(); - } - else - { - ke->ignore(); + switch( ke->key() ){ + case Qt::Key_F5: + reloadTree(); + break; + default: + ke->ignore(); } } @@ -321,9 +321,8 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) : QTreeWidget( parent ), m_mousePressed( false ), m_pressPos(), - m_previewPlayHandle( NULL ), - m_pphMutex( QMutex::Recursive ), - m_contextMenuItem( NULL ) + m_previewPlayHandle( nullptr ), + m_pphMutex( QMutex::Recursive ) { setColumnCount( 1 ); headerItem()->setHidden( true ); @@ -338,6 +337,9 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) : } + + + QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) const { int numChildren = item ? item->childCount() : topLevelItemCount(); @@ -362,59 +364,157 @@ QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) con return dirs; } -void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) + + + +void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke ) { - FileItem * f = dynamic_cast(itemAt(e->pos())); - if (f == nullptr) + // Shorter names for some commonly used properties of the event + const auto key = ke->key(); + const bool vertical = (key == Qt::Key_Up || key == Qt::Key_Down); + const bool horizontal = (key == Qt::Key_Left || key == Qt::Key_Right); + const bool insert = (key == Qt::Key_Enter || key == Qt::Key_Return); + const bool preview = (key == Qt::Key_Space); + + // First of all, forward all keypresses + QTreeWidget::keyPressEvent(ke); + // Then, ignore all autorepeats (they would spam new tracks or previews) + if (ke->isAutoRepeat()) { return; } + // We should stop any running previews before we do anything new + else if (vertical || horizontal || preview || insert) { stopPreview(); } + + // Try to get the currently selected item as a FileItem + FileItem * file = dynamic_cast(currentItem()); + // If it's null (folder, separator, etc.), there's nothing left for us to do + if (file == nullptr) { return; } + + // When moving to a new sound, preview it. Skip presets, they can play forever + if (vertical && file->type() == FileItem::SampleFile) { - return; + previewFileItem(file); + } + + // When enter is pressed, add the selected item... + if (insert) + { + // ...to the song editor by default, or to the BB editor if ctrl is held + bool songEditor = !(ke->modifiers() & Qt::ControlModifier); + // If shift is held, we send the item to a new sample track... + bool sampleTrack = ke->modifiers() & Qt::ShiftModifier; + // ...but only in the song editor. So, ctrl+shift enter does nothing + if (sampleTrack && songEditor){ openInNewSampleTrack(file); } + // Otherwise we send the item as a new instrument track + else if (!sampleTrack){ openInNewInstrumentTrack(file, songEditor); } } - if (f->handling() == FileItem::LoadAsPreset || f->handling() == FileItem::LoadByPlugin) + // When space is pressed, start a preview of the selected item + if (preview) { previewFileItem(file); } +} + + + + +void FileBrowserTreeWidget::keyReleaseEvent(QKeyEvent* ke) +{ + // Cancel previews when the space key is released + if (ke->key() == Qt::Key_Space && !ke->isAutoRepeat()) { stopPreview(); } +} + + + + +void FileBrowserTreeWidget::hideEvent(QHideEvent* he) +{ + // Cancel previews when the user switches tabs or hides the sidebar + stopPreview(); + QTreeWidget::hideEvent(he); +} + + + + +void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) +{ + FileItem * file = dynamic_cast( itemAt( e->pos() ) ); + if( file != nullptr && file->isTrack() ) { - // Set the member to the current FileItem so that it is available during the - // execution of the slots of the context menu we are about to create and execute. - m_contextMenuItem = f; - - QMenu contextMenu(this); - - contextMenu.addAction(tr("Send to active instrument-track"), - this, - SLOT(sendToActiveInstrumentTrack())); - contextMenu.addAction(tr("Open in new instrument-track/Song Editor"), - this, - SLOT(openInNewInstrumentTrackSE())); - contextMenu.addAction(tr("Open in new instrument-track/B+B Editor"), - this, - SLOT(openInNewInstrumentTrackBBE())); + QMenu contextMenu( this ); + + contextMenu.addAction( + tr( "Send to active instrument-track" ), + [=]{ sendToActiveInstrumentTrack(file); } + ); contextMenu.addSeparator(); - contextMenu.addAction(QIcon(embed::getIconPixmap("folder")), - tr("Open containing folder"), - this, - SLOT(openContainingFolder())); + contextMenu.addAction( + QIcon(embed::getIconPixmap("folder")), + tr("Open containing folder"), + [=]{ openContainingFolder(file); } + ); + + QAction* songEditorHeader = new QAction( tr("Song Editor"), nullptr ); + songEditorHeader->setDisabled(true); + contextMenu.addAction( songEditorHeader ); + contextMenu.addActions( getContextActions(file, true) ); - contextMenu.exec(e->globalPos()); + QAction* bbEditorHeader = new QAction( tr("BB Editor"), nullptr ); + bbEditorHeader->setDisabled(true); + contextMenu.addAction( bbEditorHeader ); + contextMenu.addActions( getContextActions(file, false) ); - // The context menu has been executed so we can reset this member back to nullptr. - m_contextMenuItem = nullptr; + // We should only show the menu if it contains items + if (!contextMenu.isEmpty()) { contextMenu.exec( e->globalPos() ); } } } -void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) +QList FileBrowserTreeWidget::getContextActions(FileItem* file, bool songEditor) { - QTreeWidget::mousePressEvent( me ); - if( me->button() != Qt::LeftButton ) + QList result = QList(); + const bool fileIsSample = file->type() == FileItem::SampleFile; + + QString instrumentAction = fileIsSample ? + tr("Send to new AudioFileProcessor instance") : + tr("Send to new instrument track"); + QString shortcutMod = songEditor ? "" : UI_CTRL_KEY + QString(" + "); + + QAction* toInstrument = new QAction( + instrumentAction + tr(" (%2Enter)").arg(shortcutMod), + nullptr + ); + connect(toInstrument, &QAction::triggered, + [=]{ openInNewInstrumentTrack(file, songEditor); }); + result.append(toInstrument); + + if (songEditor && fileIsSample) { - return; + QAction* toSampleTrack = new QAction( + tr("Send to new sample track (Shift + Enter)"), + nullptr + ); + connect(toSampleTrack, &QAction::triggered, + [=]{ openInNewSampleTrack(file); }); + result.append(toSampleTrack); } - QTreeWidgetItem * i = itemAt( me->pos() ); - if ( i ) + return result; +} + + + + +void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) +{ + // Forward the event + QTreeWidget::mousePressEvent(me); + // QTreeWidget handles right clicks for us, so we only care about left clicks + if(me->button() != Qt::LeftButton) { return; } + + QTreeWidgetItem * i = itemAt(me->pos()); + if (i) { // TODO: Restrict to visible selection // if ( _me->x() > header()->cellPos( header()->mapToActual( 0 ) ) @@ -428,68 +528,84 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) // } } - FileItem * f = dynamic_cast( i ); - if( f != NULL ) - { - m_pphMutex.lock(); - if( m_previewPlayHandle != NULL ) - { - Engine::mixer()->removePlayHandle( - m_previewPlayHandle ); - m_previewPlayHandle = NULL; - } + FileItem * f = dynamic_cast(i); + if(f != nullptr) { previewFileItem(f); } +} + + - // in special case of sample-files we do not care about - // handling() rather than directly creating a SamplePlayHandle - if( f->type() == FileItem::SampleFile ) + +void FileBrowserTreeWidget::previewFileItem(FileItem* file) +{ // TODO: We should do this work outside the event thread + // Lock the preview mutex + QMutexLocker previewLocker(&m_pphMutex); + // If something is already playing, stop it before we continue + stopPreview(); + + PlayHandle* newPPH = nullptr; + const QString fileName = file->fullName(); + const QString ext = file->extension(); + + // In special case of sample-files we do not care about + // handling() rather than directly creating a SamplePlayHandle + if (file->type() == FileItem::SampleFile) + { + TextFloat * tf = TextFloat::displayMessage( + tr("Loading sample"), + tr("Please wait, loading sample for preview..."), + embed::getIconPixmap("sample_file", 24, 24), 0); + // TODO: this can be removed once we do this outside the event thread + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + SamplePlayHandle* s = new SamplePlayHandle(fileName); + s->setDoneMayReturnTrue(false); + newPPH = s; + delete tf; + } + else if ( + (ext == "xiz" || ext == "sf2" || ext == "sf3" || + ext == "gig" || ext == "pat") + && !pluginFactory->pluginSupportingExtension(ext).isNull()) + { + const bool isPlugin = file->handling() == FileItem::LoadByPlugin; + newPPH = new PresetPreviewPlayHandle(fileName, isPlugin); + } + else if (file->type() != FileItem::VstPluginFile && file->isTrack()) + { + DataFile dataFile(fileName); + if (dataFile.validate(ext)) { - TextFloat * tf = TextFloat::displayMessage( - tr( "Loading sample" ), - tr( "Please wait, loading sample for " - "preview..." ), - embed::getIconPixmap( "sample_file", - 24, 24 ), 0 ); - qApp->processEvents( - QEventLoop::ExcludeUserInputEvents ); - SamplePlayHandle * s = new SamplePlayHandle( - f->fullName() ); - s->setDoneMayReturnTrue( false ); - m_previewPlayHandle = s; - delete tf; + const bool isPlugin = file->handling() == FileItem::LoadByPlugin; + newPPH = new PresetPreviewPlayHandle(fileName, isPlugin, &dataFile); } - else if ( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" -#ifdef LMMS_HAVE_LV2 - || f->extension() == "lv2" -#endif - ) && - ! pluginFactory->pluginSupportingExtension(f->extension()).info.isNull() ) + else { - m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin ); + QMessageBox::warning(0, tr ("Error"), + tr("%1 does not appear to be a valid %2 file") + .arg(fileName, ext), + QMessageBox::Ok, QMessageBox::NoButton); } - else if( f->type() != FileItem::VstPluginFile && - ( f->handling() == FileItem::LoadAsPreset || - f->handling() == FileItem::LoadByPlugin ) ) + } + + if (newPPH != nullptr) + { + if (Engine::mixer()->addPlayHandle(newPPH)) { - DataFile dataFile( f->fullName() ); - if( !dataFile.validate( f->extension() ) ) - { - QMessageBox::warning( 0, tr ( "Error" ), - tr( "%1 does not appear to be a valid %2 file" ).arg( f->fullName(), f->extension() ), - QMessageBox::Ok, QMessageBox::NoButton ); - m_pphMutex.unlock(); - return; - } - m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin, &dataFile ); + m_previewPlayHandle = newPPH; } - if( m_previewPlayHandle != NULL ) - { - if( !Engine::mixer()->addPlayHandle( - m_previewPlayHandle ) ) - { - m_previewPlayHandle = NULL; - } - } - m_pphMutex.unlock(); + else { m_previewPlayHandle = nullptr; } + } +} + + + + +void FileBrowserTreeWidget::stopPreview() +{ + QMutexLocker previewLocker(&m_pphMutex); + if (m_previewPlayHandle != nullptr) + { + Engine::mixer()->removePlayHandle(m_previewPlayHandle); + m_previewPlayHandle = nullptr; } } @@ -503,10 +619,10 @@ void FileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * me ) QApplication::startDragDistance() ) { // make sure any playback is stopped - mouseReleaseEvent( NULL ); + mouseReleaseEvent( nullptr ); FileItem * f = dynamic_cast( itemAt( m_pressPos ) ); - if( f != NULL ) + if( f != nullptr ) { switch( f->type() ) { @@ -556,36 +672,30 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me ) { m_mousePressed = false; - m_pphMutex.lock(); - if( m_previewPlayHandle != NULL ) + QMutexLocker previewLocker(&m_pphMutex); + + if (m_previewPlayHandle != nullptr) { - // if there're samples shorter than 3 seconds, we don't + // If less than 3 seconds remain of the sample, we don't // stop them if the user releases mouse-button... - if( m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle ) + if (m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle) { - SamplePlayHandle * s = dynamic_cast( - m_previewPlayHandle ); - if( s && s->totalFrames() - s->framesDone() <= - static_cast( Engine::mixer()-> - processingSampleRate() * 3 ) ) + SamplePlayHandle* s = dynamic_cast(m_previewPlayHandle); + auto second = static_cast(Engine::mixer()->processingSampleRate()); + if (s && s->totalFrames() - s->framesDone() <= second * 3) { - s->setDoneMayReturnTrue( true ); - m_previewPlayHandle = NULL; - m_pphMutex.unlock(); - return; + s->setDoneMayReturnTrue(true); } + else { stopPreview(); } } - Engine::mixer()->removePlayHandle( m_previewPlayHandle ); - m_previewPlayHandle = NULL; + else { stopPreview(); } } - m_pphMutex.unlock(); } - -void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) +void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it) { Engine::mixer()->requestChangeInModel(); switch( f->handling() ) @@ -601,7 +711,7 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) { const QString e = f->extension(); Instrument * i = it->instrument(); - if( i == NULL || + if( i == nullptr || !i->descriptor()->supportsFileType( e ) ) { PluginFactory::PluginInfoAndKey piakn = @@ -641,7 +751,7 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, int column ) { FileItem * f = dynamic_cast( item ); - if( f == NULL ) + if( f == nullptr ) { return; } @@ -649,7 +759,7 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, if( f->handling() == FileItem::LoadAsProject || f->handling() == FileItem::ImportAsProject ) { - handleFile( f, NULL ); + handleFile( f, nullptr ); } else if( f->handling() != FileItem::NotSupported ) { @@ -663,53 +773,66 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, -void FileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc ) +void FileBrowserTreeWidget::openInNewInstrumentTrack(TrackContainer* tc, FileItem* item) { - if( m_contextMenuItem->handling() == FileItem::LoadAsPreset || - m_contextMenuItem->handling() == FileItem::LoadByPlugin ) + if(item->isTrack()) { InstrumentTrack * it = dynamic_cast( - Track::create( Track::InstrumentTrack, tc ) ); - handleFile( m_contextMenuItem, it ); + Track::create(Track::InstrumentTrack, tc)); + handleFile(item, it); } } -void FileBrowserTreeWidget::openInNewInstrumentTrackBBE( void ) +void FileBrowserTreeWidget::openInNewInstrumentTrack(FileItem* item, bool songEditor) { - openInNewInstrumentTrack( Engine::getBBTrackContainer() ); + // Get the correct TrackContainer. Ternary doesn't compile here + TrackContainer* tc = Engine::getSong(); + if (!songEditor) { tc = Engine::getBBTrackContainer(); } + openInNewInstrumentTrack(tc, item); } -void FileBrowserTreeWidget::openInNewInstrumentTrackSE( void ) +bool FileBrowserTreeWidget::openInNewSampleTrack(FileItem* item) { - openInNewInstrumentTrack( Engine::getSong() ); + // Can't add non-samples to a sample track + if (item->type() != FileItem::SampleFile) { return false; } + + // Create a new sample track for this sample + SampleTrack* sampleTrack = static_cast( + Track::create(Track::SampleTrack, Engine::getSong())); + + // Add the sample clip to the track + Engine::mixer()->requestChangeInModel(); + SampleTCO* clip = static_cast(sampleTrack->createTCO(0)); + clip->setSampleFile(item->fullName()); + Engine::mixer()->doneChangeInModel(); + return true; } -void FileBrowserTreeWidget::openContainingFolder() + +void FileBrowserTreeWidget::openContainingFolder(FileItem* item) { - if (m_contextMenuItem) - { - // Delegate to QDesktopServices::openUrl with the directory of the selected file. Please note that - // this will only open the directory but not select the file as this is much more complicated due - // to different implementations that are needed for different platforms (Linux/Windows/MacOS). - - // Using QDesktopServices::openUrl seems to be the most simple cross platform way which uses - // functionality that's already available in Qt. - QFileInfo fileInfo(m_contextMenuItem->fullName()); - QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); - } + // Delegate to QDesktopServices::openUrl with the directory of the selected file. Please note that + // this will only open the directory but not select the file as this is much more complicated due + // to different implementations that are needed for different platforms (Linux/Windows/MacOS). + + // Using QDesktopServices::openUrl seems to be the most simple cross platform way which uses + // functionality that's already available in Qt. + QFileInfo fileInfo(item->fullName()); + QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); } -void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) + +void FileBrowserTreeWidget::sendToActiveInstrumentTrack( FileItem* item ) { // get all windows opened in the workspace QList pl = @@ -724,9 +847,9 @@ void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) InstrumentTrackWindow * itw = dynamic_cast( w.previous()->widget() ); - if( itw != NULL && itw->isHidden() == false ) + if( itw != nullptr && itw->isHidden() == false ) { - handleFile( m_contextMenuItem, itw->model() ); + handleFile( item, itw->model() ); break; } } @@ -738,7 +861,7 @@ void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) void FileBrowserTreeWidget::updateDirectory(QTreeWidgetItem * item ) { Directory * dir = dynamic_cast( item ); - if( dir != NULL ) + if( dir != nullptr ) { dir->update(); } @@ -749,9 +872,9 @@ void FileBrowserTreeWidget::updateDirectory(QTreeWidgetItem * item ) -QPixmap * Directory::s_folderPixmap = NULL; -QPixmap * Directory::s_folderOpenedPixmap = NULL; -QPixmap * Directory::s_folderLockedPixmap = NULL; +QPixmap * Directory::s_folderPixmap = nullptr; +QPixmap * Directory::s_folderOpenedPixmap = nullptr; +QPixmap * Directory::s_folderLockedPixmap = nullptr; Directory::Directory(const QString & filename, const QString & path, @@ -780,19 +903,19 @@ Directory::Directory(const QString & filename, const QString & path, void Directory::initPixmaps( void ) { - if( s_folderPixmap == NULL ) + if( s_folderPixmap == nullptr ) { s_folderPixmap = new QPixmap( embed::getIconPixmap( "folder" ) ); } - if( s_folderOpenedPixmap == NULL ) + if( s_folderOpenedPixmap == nullptr ) { s_folderOpenedPixmap = new QPixmap( embed::getIconPixmap( "folder_opened" ) ); } - if( s_folderLockedPixmap == NULL ) + if( s_folderLockedPixmap == nullptr ) { s_folderLockedPixmap = new QPixmap( embed::getIconPixmap( "folder_locked" ) ); @@ -870,7 +993,7 @@ bool Directory::addItems(const QString & path ) { Directory * d = dynamic_cast( child( i ) ); - if( d == NULL || cur_file < d->text( 0 ) ) + if( d == nullptr || cur_file < d->text( 0 ) ) { // insert before item, we're done insertChild( i, new Directory( cur_file, @@ -928,13 +1051,13 @@ bool Directory::addItems(const QString & path ) -QPixmap * FileItem::s_projectFilePixmap = NULL; -QPixmap * FileItem::s_presetFilePixmap = NULL; -QPixmap * FileItem::s_sampleFilePixmap = NULL; -QPixmap * FileItem::s_soundfontFilePixmap = NULL; -QPixmap * FileItem::s_vstPluginFilePixmap = NULL; -QPixmap * FileItem::s_midiFilePixmap = NULL; -QPixmap * FileItem::s_unknownFilePixmap = NULL; +QPixmap * FileItem::s_projectFilePixmap = nullptr; +QPixmap * FileItem::s_presetFilePixmap = nullptr; +QPixmap * FileItem::s_sampleFilePixmap = nullptr; +QPixmap * FileItem::s_soundfontFilePixmap = nullptr; +QPixmap * FileItem::s_vstPluginFilePixmap = nullptr; +QPixmap * FileItem::s_midiFilePixmap = nullptr; +QPixmap * FileItem::s_unknownFilePixmap = nullptr; FileItem::FileItem(QTreeWidget * parent, const QString & name, @@ -962,43 +1085,43 @@ FileItem::FileItem(const QString & name, const QString & path ) : void FileItem::initPixmaps( void ) { - if( s_projectFilePixmap == NULL ) + if( s_projectFilePixmap == nullptr ) { s_projectFilePixmap = new QPixmap( embed::getIconPixmap( "project_file", 16, 16 ) ); } - if( s_presetFilePixmap == NULL ) + if( s_presetFilePixmap == nullptr ) { s_presetFilePixmap = new QPixmap( embed::getIconPixmap( "preset_file", 16, 16 ) ); } - if( s_sampleFilePixmap == NULL ) + if( s_sampleFilePixmap == nullptr ) { s_sampleFilePixmap = new QPixmap( embed::getIconPixmap( "sample_file", 16, 16 ) ); } - if ( s_soundfontFilePixmap == NULL ) + if ( s_soundfontFilePixmap == nullptr ) { s_soundfontFilePixmap = new QPixmap( embed::getIconPixmap( "soundfont_file", 16, 16 ) ); } - if ( s_vstPluginFilePixmap == NULL ) + if ( s_vstPluginFilePixmap == nullptr ) { s_vstPluginFilePixmap = new QPixmap( embed::getIconPixmap( "vst_plugin_file", 16, 16 ) ); } - if( s_midiFilePixmap == NULL ) + if( s_midiFilePixmap == nullptr ) { s_midiFilePixmap = new QPixmap( embed::getIconPixmap( "midi_file", 16, 16 ) ); } - if( s_unknownFilePixmap == NULL ) + if( s_unknownFilePixmap == nullptr ) { s_unknownFilePixmap = new QPixmap( embed::getIconPixmap( "unknown_file" ) ); @@ -1111,7 +1234,3 @@ QString FileItem::extension(const QString & file ) { return QFileInfo( file ).suffix().toLower(); } - - - - From 4efe0c842e127909494112ba648113b4209f91b6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 2 Oct 2020 09:36:22 +0000 Subject: [PATCH 100/180] Haiku build fix. related to ringbuffer, matching cmake settings to disable mlock for this platform. Haiku does not support tls model as well. --- CMakeLists.txt | 4 ++-- src/3rdparty/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aaeec055f30..a3c83c1a4b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -487,9 +487,9 @@ If(WANT_GIG) ENDIF(WANT_GIG) # check for pthreads -IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) +IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD OR LMMS_BUILD_HAIKU) FIND_PACKAGE(Threads) -ENDIF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) +ENDIF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD OR LMMS_BUILD_HAIKU) # check for sndio (roaraudio won't work yet) IF(WANT_SNDIO) diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 808298e79ff..24d15609599 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -16,7 +16,7 @@ FILE(WRITE ${CMAKE_BINARY_DIR}/src/ringbuffer_export.h # Enable MLOCK support for ringbuffer if available INCLUDE(CheckIncludeFiles) CHECK_INCLUDE_FILES(sys/mman.h HAVE_SYS_MMAN) -IF(HAVE_SYS_MMAN) +IF(HAVE_SYS_MMAN AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku") SET(USE_MLOCK ON) ELSE() SET(USE_MLOCK OFF) From 7e986a83230b5f52a4cd3a917c99d69ac96f007a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 3 Oct 2020 12:07:38 +0200 Subject: [PATCH 101/180] Compile LMMS using C++14 This replaces `set(CMAKE_CXX_STANDARD 14)` by `set(CMAKE_CXX_STANDARD 11)` wherever it is required. Wherever it is superseded by parental `CMakeLists.txt`, this command is now removed. --- plugins/CMakeLists.txt | 4 ++-- plugins/LadspaEffect/CMakeLists.txt | 2 -- plugins/LadspaEffect/calf/CMakeLists.txt | 2 -- plugins/vst_base/RemoteVstPlugin/CMakeLists.txt | 2 +- src/CMakeLists.txt | 4 ++-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index a4e56921fea..dde913d56ef 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,8 +2,8 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") SET(CMAKE_DEBUG_POSTFIX "") -# Enable C++11 -SET(CMAKE_CXX_STANDARD 11) +# Enable C++14 +SET(CMAKE_CXX_STANDARD 14) IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") diff --git a/plugins/LadspaEffect/CMakeLists.txt b/plugins/LadspaEffect/CMakeLists.txt index 5bfbc3284c8..951615ad4d0 100644 --- a/plugins/LadspaEffect/CMakeLists.txt +++ b/plugins/LadspaEffect/CMakeLists.txt @@ -4,8 +4,6 @@ BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialo SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ladspa") -SET(CMAKE_CXX_STANDARD 11) - IF(WANT_CAPS) ADD_SUBDIRECTORY(caps) ENDIF(WANT_CAPS) diff --git a/plugins/LadspaEffect/calf/CMakeLists.txt b/plugins/LadspaEffect/calf/CMakeLists.txt index 4924169e410..f5fb632f6ad 100644 --- a/plugins/LadspaEffect/calf/CMakeLists.txt +++ b/plugins/LadspaEffect/calf/CMakeLists.txt @@ -1,8 +1,6 @@ # Note: # The last version of Calf that was LADSPA-capable is version 0.0.18.2 -SET(CMAKE_CXX_STANDARD 11) - # Parse version info from autoconf FILE(READ veal/configure.ac VERSION_FILE) STRING(REPLACE "[" ";" VERSION_FILE ${VERSION_FILE} ) diff --git a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt index f4023fd426c..aa77459b68a 100644 --- a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt @@ -55,7 +55,7 @@ if(WIN32) endif() if(IS_MINGW) - SET(CMAKE_REQUIRED_FLAGS "-std=c++11") + SET(CMAKE_REQUIRED_FLAGS "-std=c++14") CHECK_CXX_SOURCE_COMPILES(" #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3ebec349e60..0a63a28d5fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,8 +9,8 @@ SET(LMMS_UIS "") SET(CMAKE_AUTOMOC ON) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -# Enable C++11 -SET(CMAKE_CXX_STANDARD 11) +# Enable C++14 +SET(CMAKE_CXX_STANDARD 14) IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") From 16db33f2bf1f1a68bbefa455d09e81f9b77cd374 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 3 Oct 2020 12:13:06 +0200 Subject: [PATCH 102/180] Use STL version of `std::make_shared` now As all is compiled with C++14 now, no need to use `std::make_shared` from stdshims.h now. --- plugins/DualFilter/DualFilterControls.cpp | 91 +++++++++++------------ plugins/monstro/Monstro.h | 53 +++++++------ src/core/InstrumentFunctions.cpp | 17 ++--- src/core/InstrumentSoundShaping.cpp | 47 ++++++------ src/core/RenderManager.cpp | 3 +- src/core/lv2/Lv2ControlBase.cpp | 3 +- src/gui/editors/PianoRoll.cpp | 7 +- 7 files changed, 107 insertions(+), 114 deletions(-) diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index 0992d478cb1..35cbaab3a94 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -32,7 +32,6 @@ #include "embed.h" #include "Engine.h" #include "Song.h" -#include "stdshims.h" DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : EffectControls( effect ), @@ -52,51 +51,51 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_res2Model( 0.5, BasicFilters<>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 2" ) ), m_gain2Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 2" ) ) { - m_filter1Model.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "Hi-pass" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "Band-pass csg" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "Band-pass czpg" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "Notch" ), make_unique( "filter_notch" ) ); - m_filter1Model.addItem( tr( "All-pass" ), make_unique( "filter_ap" ) ); - m_filter1Model.addItem( tr( "Moog" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "2x Low-pass" ), make_unique( "filter_2lp" ) ); - m_filter1Model.addItem( tr( "RC Low-pass 12 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "RC Band-pass 12 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "RC High-pass 12 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "RC Low-pass 24 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "RC Band-pass 24 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "RC High-pass 24 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "Vocal Formant" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "2x Moog" ), make_unique( "filter_2lp" ) ); - m_filter1Model.addItem( tr( "SV Low-pass" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "SV Band-pass" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "SV High-pass" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "SV Notch" ), make_unique( "filter_notch" ) ); - m_filter1Model.addItem( tr( "Fast Formant" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "Tripole" ), make_unique( "filter_lp" ) ); - - m_filter2Model.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "Hi-pass" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "Band-pass csg" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "Band-pass czpg" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "Notch" ), make_unique( "filter_notch" ) ); - m_filter2Model.addItem( tr( "All-pass" ), make_unique( "filter_ap" ) ); - m_filter2Model.addItem( tr( "Moog" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "2x Low-pass" ), make_unique( "filter_2lp" ) ); - m_filter2Model.addItem( tr( "RC Low-pass 12 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "RC Band-pass 12 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "RC High-pass 12 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "RC Low-pass 24 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "RC Band-pass 24 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "RC High-pass 24 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "Vocal Formant" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "2x Moog" ), make_unique( "filter_2lp" ) ); - m_filter2Model.addItem( tr( "SV Low-pass" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "SV Band-pass" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "SV High-pass" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "SV Notch" ), make_unique( "filter_notch" ) ); - m_filter2Model.addItem( tr( "Fast Formant" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "Tripole" ), make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "Hi-pass" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "Band-pass csg" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "Band-pass czpg" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "Notch" ), std::make_unique( "filter_notch" ) ); + m_filter1Model.addItem( tr( "All-pass" ), std::make_unique( "filter_ap" ) ); + m_filter1Model.addItem( tr( "Moog" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "2x Low-pass" ), std::make_unique( "filter_2lp" ) ); + m_filter1Model.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "Vocal Formant" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "2x Moog" ), std::make_unique( "filter_2lp" ) ); + m_filter1Model.addItem( tr( "SV Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "SV Band-pass" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "SV High-pass" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "SV Notch" ), std::make_unique( "filter_notch" ) ); + m_filter1Model.addItem( tr( "Fast Formant" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "Tripole" ), std::make_unique( "filter_lp" ) ); + + m_filter2Model.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "Hi-pass" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "Band-pass csg" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "Band-pass czpg" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "Notch" ), std::make_unique( "filter_notch" ) ); + m_filter2Model.addItem( tr( "All-pass" ), std::make_unique( "filter_ap" ) ); + m_filter2Model.addItem( tr( "Moog" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "2x Low-pass" ), std::make_unique( "filter_2lp" ) ); + m_filter2Model.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "Vocal Formant" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "2x Moog" ), std::make_unique( "filter_2lp" ) ); + m_filter2Model.addItem( tr( "SV Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "SV Band-pass" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "SV High-pass" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "SV Notch" ), std::make_unique( "filter_notch" ) ); + m_filter2Model.addItem( tr( "Fast Formant" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "Tripole" ), std::make_unique( "filter_lp" ) ); connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateFilters() ) ); } diff --git a/plugins/monstro/Monstro.h b/plugins/monstro/Monstro.h index fc0cc13b06d..0bb1a8dccbb 100644 --- a/plugins/monstro/Monstro.h +++ b/plugins/monstro/Monstro.h @@ -39,7 +39,6 @@ #include "Oscillator.h" #include "lmms_math.h" #include "BandLimitedWave.h" -#include "stdshims.h" // // UI Macros @@ -310,35 +309,35 @@ class MonstroInstrument : public Instrument Q_OBJECT #define setwavemodel( name ) \ - name .addItem( tr( "Sine wave" ), make_unique( "sin" ) ); \ - name .addItem( tr( "Bandlimited Triangle wave" ), make_unique( "tri" ) ); \ - name .addItem( tr( "Bandlimited Saw wave" ), make_unique( "saw" ) ); \ - name .addItem( tr( "Bandlimited Ramp wave" ), make_unique( "ramp" ) ); \ - name .addItem( tr( "Bandlimited Square wave" ), make_unique( "sqr" ) ); \ - name .addItem( tr( "Bandlimited Moog saw wave" ), make_unique( "moog" ) ); \ - name .addItem( tr( "Soft square wave" ), make_unique( "sqrsoft" ) ); \ - name .addItem( tr( "Absolute sine wave" ), make_unique( "sinabs" ) ); \ - name .addItem( tr( "Exponential wave" ), make_unique( "exp" ) ); \ - name .addItem( tr( "White noise" ), make_unique( "noise" ) ); \ - name .addItem( tr( "Digital Triangle wave" ), make_unique( "tri" ) ); \ - name .addItem( tr( "Digital Saw wave" ), make_unique( "saw" ) ); \ - name .addItem( tr( "Digital Ramp wave" ), make_unique( "ramp" ) ); \ - name .addItem( tr( "Digital Square wave" ), make_unique( "sqr" ) ); \ - name .addItem( tr( "Digital Moog saw wave" ), make_unique( "moog" ) ); + name .addItem( tr( "Sine wave" ), std::make_unique( "sin" ) ); \ + name .addItem( tr( "Bandlimited Triangle wave" ), std::make_unique( "tri" ) ); \ + name .addItem( tr( "Bandlimited Saw wave" ), std::make_unique( "saw" ) ); \ + name .addItem( tr( "Bandlimited Ramp wave" ), std::make_unique( "ramp" ) ); \ + name .addItem( tr( "Bandlimited Square wave" ), std::make_unique( "sqr" ) ); \ + name .addItem( tr( "Bandlimited Moog saw wave" ), std::make_unique( "moog" ) ); \ + name .addItem( tr( "Soft square wave" ), std::make_unique( "sqrsoft" ) ); \ + name .addItem( tr( "Absolute sine wave" ), std::make_unique( "sinabs" ) ); \ + name .addItem( tr( "Exponential wave" ), std::make_unique( "exp" ) ); \ + name .addItem( tr( "White noise" ), std::make_unique( "noise" ) ); \ + name .addItem( tr( "Digital Triangle wave" ), std::make_unique( "tri" ) ); \ + name .addItem( tr( "Digital Saw wave" ), std::make_unique( "saw" ) ); \ + name .addItem( tr( "Digital Ramp wave" ), std::make_unique( "ramp" ) ); \ + name .addItem( tr( "Digital Square wave" ), std::make_unique( "sqr" ) ); \ + name .addItem( tr( "Digital Moog saw wave" ), std::make_unique( "moog" ) ); #define setlfowavemodel( name ) \ - name .addItem( tr( "Sine wave" ), make_unique( "sin" ) ); \ - name .addItem( tr( "Triangle wave" ), make_unique( "tri" ) ); \ - name .addItem( tr( "Saw wave" ), make_unique( "saw" ) ); \ - name .addItem( tr( "Ramp wave" ), make_unique( "ramp" ) ); \ - name .addItem( tr( "Square wave" ), make_unique( "sqr" ) ); \ - name .addItem( tr( "Moog saw wave" ), make_unique( "moog" ) ); \ - name .addItem( tr( "Soft square wave" ), make_unique( "sqrsoft" ) ); \ - name .addItem( tr( "Abs. sine wave" ), make_unique( "sinabs" ) ); \ - name .addItem( tr( "Exponential wave" ), make_unique( "exp" ) ); \ - name .addItem( tr( "Random" ), make_unique( "rand" ) ); \ - name .addItem( tr( "Random smooth" ), make_unique( "rand" ) ); + name .addItem( tr( "Sine wave" ), std::make_unique( "sin" ) ); \ + name .addItem( tr( "Triangle wave" ), std::make_unique( "tri" ) ); \ + name .addItem( tr( "Saw wave" ), std::make_unique( "saw" ) ); \ + name .addItem( tr( "Ramp wave" ), std::make_unique( "ramp" ) ); \ + name .addItem( tr( "Square wave" ), std::make_unique( "sqr" ) ); \ + name .addItem( tr( "Moog saw wave" ), std::make_unique( "moog" ) ); \ + name .addItem( tr( "Soft square wave" ), std::make_unique( "sqrsoft" ) ); \ + name .addItem( tr( "Abs. sine wave" ), std::make_unique( "sinabs" ) ); \ + name .addItem( tr( "Exponential wave" ), std::make_unique( "exp" ) ); \ + name .addItem( tr( "Random" ), std::make_unique( "rand" ) ); \ + name .addItem( tr( "Random smooth" ), std::make_unique( "rand" ) ); public: MonstroInstrument( InstrumentTrack * _instrument_track ); diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 70e2c5e806e..c8ec24d6e45 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -30,7 +30,6 @@ #include "InstrumentTrack.h" #include "Mixer.h" #include "PresetPreviewPlayHandle.h" -#include "stdshims.h" InstrumentFunctionNoteStacking::ChordTable::Init InstrumentFunctionNoteStacking::ChordTable::s_initTable[] = @@ -316,16 +315,16 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) : m_arpModel.addItem( chord_table[i].getName() ); } - m_arpDirectionModel.addItem( tr( "Up" ), make_unique( "arp_up" ) ); - m_arpDirectionModel.addItem( tr( "Down" ), make_unique( "arp_down" ) ); - m_arpDirectionModel.addItem( tr( "Up and down" ), make_unique( "arp_up_and_down" ) ); - m_arpDirectionModel.addItem( tr( "Down and up" ), make_unique( "arp_up_and_down" ) ); - m_arpDirectionModel.addItem( tr( "Random" ), make_unique( "arp_random" ) ); + m_arpDirectionModel.addItem( tr( "Up" ), std::make_unique( "arp_up" ) ); + m_arpDirectionModel.addItem( tr( "Down" ), std::make_unique( "arp_down" ) ); + m_arpDirectionModel.addItem( tr( "Up and down" ), std::make_unique( "arp_up_and_down" ) ); + m_arpDirectionModel.addItem( tr( "Down and up" ), std::make_unique( "arp_up_and_down" ) ); + m_arpDirectionModel.addItem( tr( "Random" ), std::make_unique( "arp_random" ) ); m_arpDirectionModel.setInitValue( ArpDirUp ); - m_arpModeModel.addItem( tr( "Free" ), make_unique( "arp_free" ) ); - m_arpModeModel.addItem( tr( "Sort" ), make_unique( "arp_sort" ) ); - m_arpModeModel.addItem( tr( "Sync" ), make_unique( "arp_sync" ) ); + m_arpModeModel.addItem( tr( "Free" ), std::make_unique( "arp_free" ) ); + m_arpModeModel.addItem( tr( "Sort" ), std::make_unique( "arp_sort" ) ); + m_arpModeModel.addItem( tr( "Sync" ), std::make_unique( "arp_sync" ) ); } diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 2c221cdcc01..b9e32427a87 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -33,7 +33,6 @@ #include "Instrument.h" #include "InstrumentTrack.h" #include "Mixer.h" -#include "stdshims.h" const float CUT_FREQ_MULTIPLIER = 6000.0f; @@ -78,28 +77,28 @@ InstrumentSoundShaping::InstrumentSoundShaping( tr( targetNames[i][2] ) ); } - m_filterModel.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "Hi-pass" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "Band-pass csg" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "Band-pass czpg" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "Notch" ), make_unique( "filter_notch" ) ); - m_filterModel.addItem( tr( "All-pass" ), make_unique( "filter_ap" ) ); - m_filterModel.addItem( tr( "Moog" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "2x Low-pass" ), make_unique( "filter_2lp" ) ); - m_filterModel.addItem( tr( "RC Low-pass 12 dB/oct" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "RC Band-pass 12 dB/oct" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "RC High-pass 12 dB/oct" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "RC Low-pass 24 dB/oct" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "RC Band-pass 24 dB/oct" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "RC High-pass 24 dB/oct" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "Vocal Formant" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "2x Moog" ), make_unique( "filter_2lp" ) ); - m_filterModel.addItem( tr( "SV Low-pass" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "SV Band-pass" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "SV High-pass" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "SV Notch" ), make_unique( "filter_notch" ) ); - m_filterModel.addItem( tr( "Fast Formant" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "Tripole" ), make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "Hi-pass" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "Band-pass csg" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "Band-pass czpg" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "Notch" ), std::make_unique( "filter_notch" ) ); + m_filterModel.addItem( tr( "All-pass" ), std::make_unique( "filter_ap" ) ); + m_filterModel.addItem( tr( "Moog" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "2x Low-pass" ), std::make_unique( "filter_2lp" ) ); + m_filterModel.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "Vocal Formant" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "2x Moog" ), std::make_unique( "filter_2lp" ) ); + m_filterModel.addItem( tr( "SV Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "SV Band-pass" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "SV High-pass" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "SV Notch" ), std::make_unique( "filter_notch" ) ); + m_filterModel.addItem( tr( "Fast Formant" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "Tripole" ), std::make_unique( "filter_lp" ) ); } @@ -161,7 +160,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, if( n->m_filter == nullptr ) { - n->m_filter = make_unique>( Engine::mixer()->processingSampleRate() ); + n->m_filter = std::make_unique>( Engine::mixer()->processingSampleRate() ); } n->m_filter->setFilterType( m_filterModel.value() ); diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 69255442cd4..62b2788888c 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -29,7 +29,6 @@ #include "Song.h" #include "BBTrackContainer.h" #include "BBTrack.h" -#include "stdshims.h" RenderManager::RenderManager( @@ -140,7 +139,7 @@ void RenderManager::renderProject() void RenderManager::render(QString outputPath) { - m_activeRenderer = make_unique( + m_activeRenderer = std::make_unique( m_qualitySettings, m_outputSettings, m_format, diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 3f50325e7d2..cc986dafed0 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -31,7 +31,6 @@ #include "Engine.h" #include "Lv2Manager.h" #include "Lv2Proc.h" -#include "stdshims.h" @@ -54,7 +53,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo while (channelsLeft > 0) { - std::unique_ptr newOne = make_unique(m_plugin, that); + std::unique_ptr newOne = std::make_unique(m_plugin, that); if (newOne->isValid()) { channelsLeft -= std::max( diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 0eb049ddcae..3ce684ecef8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -61,7 +61,6 @@ #include "MainWindow.h" #include "Pattern.h" #include "SongEditor.h" -#include "stdshims.h" #include "StepRecorderWidget.h" #include "TextFloat.h" #include "TimeLineWidget.h" @@ -352,7 +351,7 @@ PianoRoll::PianoRoll() : // Set up note length model m_noteLenModel.addItem( tr( "Last note" ), - make_unique( "edit_draw" ) ); + std::make_unique( "edit_draw" ) ); const QString pixmaps[] = { "whole", "half", "quarter", "eighth", "sixteenth", "thirtysecond", "triplethalf", "tripletquarter", "tripleteighth", @@ -360,12 +359,12 @@ PianoRoll::PianoRoll() : for( int i = 0; i < NUM_EVEN_LENGTHS; ++i ) { - auto loader = make_unique( "note_" + pixmaps[i] ); + auto loader = std::make_unique( "note_" + pixmaps[i] ); m_noteLenModel.addItem( "1/" + QString::number( 1 << i ), ::move(loader) ); } for( int i = 0; i < NUM_TRIPLET_LENGTHS; ++i ) { - auto loader = make_unique( "note_" + pixmaps[i+NUM_EVEN_LENGTHS] ); + auto loader = std::make_unique( "note_" + pixmaps[i+NUM_EVEN_LENGTHS] ); m_noteLenModel.addItem( "1/" + QString::number( (1 << i) * 3 ), ::move(loader) ); } m_noteLenModel.setValue( 0 ); From 783db3e457a112d2348facac96d151f3c4e1ed5d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 3 Oct 2020 12:14:12 +0200 Subject: [PATCH 103/180] Remove unused `make_shared` from stdshims.h --- include/stdshims.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/include/stdshims.h b/include/stdshims.h index c12254e1506..dc89a1b0933 100644 --- a/include/stdshims.h +++ b/include/stdshims.h @@ -4,33 +4,9 @@ #ifndef STDSHIMS_H #define STDSHIMS_H -#include #include #include -#if (__cplusplus >= 201402L || _MSC_VER) -#ifndef _MSC_VER -#warning "This part of this file should now be removed! The functions it provides are part of the C++14 standard." -#endif -using std::make_unique; - -#else - -/// Shim for http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique -template -std::unique_ptr make_unique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -//! Overload for the case a deleter should be specified -template -std::unique_ptr make_unique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} -#endif - #if (__cplusplus >= 201703L || _MSC_VER >= 1914) #ifndef _MSC_VER #warning "This part of this file should now be removed! The functions it provides are part of the C++17 standard." From 8939b149e38daa17073834c0255085bdff2470fa Mon Sep 17 00:00:00 2001 From: allejok96 Date: Sat, 3 Oct 2020 23:01:52 +0200 Subject: [PATCH 104/180] Add insert/remove bar buttons in Song editor (fix #5602) --- data/themes/default/insert_bar.png | Bin 0 -> 545 bytes data/themes/default/remove_bar.png | Bin 0 -> 559 bytes include/SongEditor.h | 3 +++ src/gui/editors/SongEditor.cpp | 7 +++++++ 4 files changed, 10 insertions(+) create mode 100644 data/themes/default/insert_bar.png create mode 100644 data/themes/default/remove_bar.png diff --git a/data/themes/default/insert_bar.png b/data/themes/default/insert_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b7ddc790764bbfff9a416bb31e0bc74e58521a GIT binary patch literal 545 zcmV++0^a?JP)EX>4Tx04R}tkv&MmKpe$iQ>9WW4rUN>$WWauh)QwPDionYs1;guFuC*#nzSS- zE{=k0!NHHks)LKOt`4q(Aou~|}?mh0_0YbCFbgO3q&<)#6 zClgXOwf;?j{slqVm!4L_2fw?u3R9C_QX~QNzBtauC=l8OS`EkfK6aee2@re+u8fYq+5~1kNpEzt z=n*im4P0DzG<6TS+yRE3YKp12Qjn%lC;;zg^i4Tn@D>QKdA+swaryvcsH@ZsaBv8W z6)Ah&=iPnXz5RQp-QN#&d~&Fq?Nwp`000JJOGiWi000000Qp0^e*gdgM@d9MR5;6H zV4yN!{Qv*|f2tYA$jHb*jDe)6VWPH|M-3b`kkmvyC|&p;3TPIFER?v6E_s=S(pZJL jjApS+b^!}>5zPSrZEGkI|9N*T00000NkvXXu0mjf^FQcB literal 0 HcmV?d00001 diff --git a/data/themes/default/remove_bar.png b/data/themes/default/remove_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..46845e5991cd0681691d368fb5d007eecf350f58 GIT binary patch literal 559 zcmV+~0?_@5P)EX>4Tx04R}tkv&MmKpe$iTSX}q2Q!Ft$WWauh>GZ@RVYG*P%E_RU~=gfG-*gu zTpR`0f`cE6RRhT)2yyBK+|nA zolJzBx-kgE(v zjs;YqL3aJ%fAG6ot1vO{B}EcI_lx6v3R-^Y&AJOM(_z?I(iR~x|eC+YRJ z7CQp^w}Ff6wx;X>mpj1VlOdb3D}`tV3kBf)jJ_!c4BP_YHLthUK29Hi40V;d0S*p< zks@WU`@Fliv$ucGwEFu2kl}K(fyyVU00006VoOIv00000008+zyMF)x010qNS#tmY z4c7nw4c7reD4Tcy002KpL_t(I%VS`mGGP4w|Nnog8OF%S$UuyNq^Mz{wwFf@95s;C xL_H+D%tDSPd|7dDxDXZrLo*#xzksDS002pJ7#&uIR?7eY002ovPDHLkV1iAR@p1qF literal 0 HcmV?d00001 diff --git a/include/SongEditor.h b/include/SongEditor.h index 7e0fe986a8c..a4394fc569c 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -210,6 +210,9 @@ protected slots: ComboBox * m_zoomingComboBox; ComboBox * m_snappingComboBox; QLabel* m_snapSizeLabel; + + QAction* m_insertBarAction; + QAction* m_removeBarAction; }; #endif diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 492de0e0092..8829bedfd22 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -955,6 +955,13 @@ SongEditorWindow::SongEditorWindow(Song* song) : DropToolBar *timeLineToolBar = addDropToolBarToTop(tr("Timeline controls")); m_editor->m_timeLine->addToolButtons(timeLineToolBar); + DropToolBar *insertActionsToolBar = addDropToolBarToTop(tr("Bar insert controls")); + m_insertBarAction = new QAction(embed::getIconPixmap("insert_bar"), tr("Insert bar"), this); + m_removeBarAction = new QAction(embed::getIconPixmap("remove_bar"), tr("Remove bar"), this); + insertActionsToolBar->addAction( m_insertBarAction ); + insertActionsToolBar->addAction( m_removeBarAction ); + connect(m_insertBarAction, SIGNAL(triggered()), song, SLOT(insertBar())); + connect(m_removeBarAction, SIGNAL(triggered()), song, SLOT(removeBar())); DropToolBar *zoomToolBar = addDropToolBarToTop(tr("Zoom controls")); From b558865ca40e0d8660e9ea833b5ac1eee3178e49 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 4 Oct 2020 21:45:00 +0200 Subject: [PATCH 105/180] PluginIssue: Add too MIDI in/out channels --- include/PluginIssue.h | 2 ++ src/core/PluginIssue.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/include/PluginIssue.h b/include/PluginIssue.h index c009458056e..00c90b756f1 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -36,6 +36,8 @@ enum PluginIssueType unknownPortType, tooManyInputChannels, tooManyOutputChannels, + tooManyMidiInputChannels, + tooManyMidiOutputChannels, noOutputChannel, portHasNoDef, portHasNoMin, diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 4a8b2ee5b4e..8e1938e5685 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -38,6 +38,10 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "too many audio input channels"; case tooManyOutputChannels: return "too many audio output channels"; + case tooManyMidiInputChannels: + return "too many MIDI input channels"; + case tooManyMidiOutputChannels: + return "too many MIDI output channels"; case noOutputChannel: return "no audio output channel"; case portHasNoDef: From 3fa4b98a9ec73a9ced691e08f71d46fd18c99760 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 4 Oct 2020 21:47:47 +0200 Subject: [PATCH 106/180] Remove redundant LV2Ports::Audio::m_optional The same info is already stored in the `Lv2Ports::Meta` base class. --- include/Lv2Ports.h | 2 +- src/core/lv2/Lv2Ports.cpp | 4 ++-- src/core/lv2/Lv2Proc.cpp | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index c9e6c16c855..567e1ddb585 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -162,7 +162,7 @@ struct Cv : public VisitablePort struct Audio : public VisitablePort { - Audio(std::size_t bufferSize, bool isSidechain, bool isOptional); + Audio(std::size_t bufferSize, bool isSidechain); //! Copy buffer passed by LMMS into our ports //! @param channel channel index into each sample frame diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 48fe47f604d..f2fac5744d0 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -201,8 +201,8 @@ QString PortBase::uri() const -Audio::Audio(std::size_t bufferSize, bool isSidechain, bool isOptional) - : m_buffer(bufferSize), m_sidechain(isSidechain), m_optional(isOptional) +Audio::Audio(std::size_t bufferSize, bool isSidechain) + : m_buffer(bufferSize), m_sidechain(isSidechain) { } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 9046001ab13..26593428b2c 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -56,8 +56,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, bool portMustBeUsed = !portIsSideChain(plugin, lilv_plugin_get_port_by_index(plugin, portNum)) && - !portIsOptional(plugin, - lilv_plugin_get_port_by_index(plugin, portNum)); + !meta.m_optional; if (meta.m_type == Lv2Ports::Type::Audio && portMustBeUsed) ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output ? outCount : inCount]; @@ -381,8 +380,7 @@ void Lv2Proc::createPort(std::size_t portNum) new Lv2Ports::Audio( static_cast( Engine::mixer()->framesPerPeriod()), - portIsSideChain(m_plugin, lilvPort), - portIsOptional(m_plugin, lilvPort) + portIsSideChain(m_plugin, lilvPort) ); port = audio; break; From c58c781f9331a646a1cdc4f4fc15b67c98214a4f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Tue, 6 Oct 2020 01:00:58 +0200 Subject: [PATCH 107/180] Update remote and version of submodule rpmalloc (#5696) This PR updates the remote URL and content of rpmalloc. Fixes #4752 Fixes #4806 (assumably) Helps #5694 You must now run ``` git submodule sync --recursive ``` once to reflect this change. --- .gitmodules | 2 +- src/3rdparty/rpmalloc/CMakeLists.txt | 6 ++++++ src/3rdparty/rpmalloc/rpmalloc | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8a9871b7a7b..ebc1502e265 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/Lukas-W/qt5-x11embed.git [submodule "src/3rdparty/rpmalloc/rpmalloc"] path = src/3rdparty/rpmalloc/rpmalloc - url = https://github.com/rampantpixels/rpmalloc.git + url = https://github.com/mjansson/rpmalloc.git [submodule "plugins/zynaddsubfx/zynaddsubfx"] path = plugins/zynaddsubfx/zynaddsubfx url = https://github.com/lmms/zynaddsubfx.git diff --git a/src/3rdparty/rpmalloc/CMakeLists.txt b/src/3rdparty/rpmalloc/CMakeLists.txt index 3b3afa6d329..047c32678c2 100644 --- a/src/3rdparty/rpmalloc/CMakeLists.txt +++ b/src/3rdparty/rpmalloc/CMakeLists.txt @@ -21,6 +21,12 @@ if (NOT LMMS_BUILD_WIN32) ) endif() +if(MINGW) + target_compile_definitions(rpmalloc + PRIVATE -D_WIN32_WINNT=0x600 + ) +endif() + if (CMAKE_BUILD_TYPE STREQUAL "Debug") # rpmalloc uses GCC builtin "__builtin_umull_overflow" with ENABLE_VALIDATE_ARGS, # which is only available starting with GCC 5 diff --git a/src/3rdparty/rpmalloc/rpmalloc b/src/3rdparty/rpmalloc/rpmalloc index b5bdc18051b..8d790d2b45e 160000 --- a/src/3rdparty/rpmalloc/rpmalloc +++ b/src/3rdparty/rpmalloc/rpmalloc @@ -1 +1 @@ -Subproject commit b5bdc18051bb74a22f0bde4bcc90b01cf590b496 +Subproject commit 8d790d2b45e1818e531c61bf649c5225556dd07a From 8c63582ee384af0ee627aa8278608fb5079aafcc Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Wed, 7 Oct 2020 13:37:08 +0100 Subject: [PATCH 108/180] Don't reload sample from disk when reversing (#5701) --- src/core/SampleBuffer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 368600752f0..ffe631ca44b 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -24,6 +24,8 @@ #include "SampleBuffer.h" +#include + #include #include #include @@ -1411,8 +1413,13 @@ void SampleBuffer::setAmplification( float _a ) void SampleBuffer::setReversed( bool _on ) { + Engine::mixer()->requestChangeInModel(); + m_varLock.lockForWrite(); + if (m_reversed != _on) { std::reverse(m_data, m_data + m_frames); } m_reversed = _on; - update( true ); + m_varLock.unlock(); + Engine::mixer()->doneChangeInModel(); + emit sampleUpdated(); } From 892aec318d0a78442dbf62d219e1137418d72b45 Mon Sep 17 00:00:00 2001 From: Firepal Date: Thu, 8 Oct 2020 15:42:34 +0200 Subject: [PATCH 109/180] Specify DirectConnection for a few plugins (#5695) --- .../audio_file_processor.cpp | 12 ++-- plugins/bit_invader/bit_invader.cpp | 2 +- plugins/monstro/Monstro.cpp | 60 +++++++++---------- plugins/nes/Nes.cpp | 6 +- .../triple_oscillator/TripleOscillator.cpp | 18 +++--- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index da0f17a3c9e..d71b111c3cb 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -87,17 +87,17 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : m_nextPlayBackwards( false ) { connect( &m_reverseModel, SIGNAL( dataChanged() ), - this, SLOT( reverseModelChanged() ) ); + this, SLOT( reverseModelChanged() ), Qt::DirectConnection ); connect( &m_ampModel, SIGNAL( dataChanged() ), - this, SLOT( ampModelChanged() ) ); + this, SLOT( ampModelChanged() ), Qt::DirectConnection ); connect( &m_startPointModel, SIGNAL( dataChanged() ), - this, SLOT( startPointChanged() ) ); + this, SLOT( startPointChanged() ), Qt::DirectConnection ); connect( &m_endPointModel, SIGNAL( dataChanged() ), - this, SLOT( endPointChanged() ) ); + this, SLOT( endPointChanged() ), Qt::DirectConnection ); connect( &m_loopPointModel, SIGNAL( dataChanged() ), - this, SLOT( loopPointChanged() ) ); + this, SLOT( loopPointChanged() ), Qt::DirectConnection ); connect( &m_stutterModel, SIGNAL( dataChanged() ), - this, SLOT( stutterModelChanged() ) ); + this, SLOT( stutterModelChanged() ), Qt::DirectConnection ); //interpolation modes m_interpolationModel.addItem( tr( "None" ) ); diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index 3bc5785ef64..f59f2bfd178 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -149,7 +149,7 @@ bitInvader::bitInvader( InstrumentTrack * _instrument_track ) : m_graph.setWaveToSine(); connect( &m_sampleLength, SIGNAL( dataChanged( ) ), - this, SLOT( lengthChanged( ) ) ); + this, SLOT( lengthChanged( ) ), Qt::DirectConnection ); connect( &m_graph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( samplesChanged( int, int ) ) ); diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index 8e0342377c3..816cfe5502d 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -957,52 +957,52 @@ MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : // updateVolumes - connect( &m_osc1Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolume1() ) ); - connect( &m_osc1Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolume1() ) ); - connect( &m_osc2Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolume2() ) ); - connect( &m_osc2Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolume2() ) ); - connect( &m_osc3Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolume3() ) ); - connect( &m_osc3Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolume3() ) ); + connect( &m_osc1Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolume1() ), Qt::DirectConnection ); + connect( &m_osc1Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolume1() ), Qt::DirectConnection ); + connect( &m_osc2Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolume2() ), Qt::DirectConnection ); + connect( &m_osc2Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolume2() ), Qt::DirectConnection ); + connect( &m_osc3Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolume3() ), Qt::DirectConnection ); + connect( &m_osc3Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolume3() ), Qt::DirectConnection ); // updateFreq - connect( &m_osc1Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ) ); - connect( &m_osc2Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ) ); - connect( &m_osc3Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq3() ) ); + connect( &m_osc1Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ), Qt::DirectConnection ); + connect( &m_osc2Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ), Qt::DirectConnection ); + connect( &m_osc3Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq3() ), Qt::DirectConnection ); - connect( &m_osc1Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ) ); - connect( &m_osc2Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ) ); + connect( &m_osc1Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ), Qt::DirectConnection ); + connect( &m_osc2Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ), Qt::DirectConnection ); - connect( &m_osc1Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ) ); - connect( &m_osc2Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ) ); + connect( &m_osc1Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ), Qt::DirectConnection ); + connect( &m_osc2Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ), Qt::DirectConnection ); // updatePO - connect( &m_osc1Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO1() ) ); - connect( &m_osc2Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO2() ) ); - connect( &m_osc3Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO3() ) ); + connect( &m_osc1Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO1() ), Qt::DirectConnection ); + connect( &m_osc2Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO2() ), Qt::DirectConnection ); + connect( &m_osc3Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO3() ), Qt::DirectConnection ); // updateEnvelope1 - connect( &m_env1Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); - connect( &m_env1Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); - connect( &m_env1Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); - connect( &m_env1Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); - connect( &m_env1Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); - connect( &m_env1Slope, SIGNAL( dataChanged() ), this, SLOT( updateSlope1() ) ); + connect( &m_env1Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ), Qt::DirectConnection ); + connect( &m_env1Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ), Qt::DirectConnection ); + connect( &m_env1Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ), Qt::DirectConnection ); + connect( &m_env1Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ), Qt::DirectConnection ); + connect( &m_env1Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ), Qt::DirectConnection ); + connect( &m_env1Slope, SIGNAL( dataChanged() ), this, SLOT( updateSlope1() ), Qt::DirectConnection ); // updateEnvelope2 - connect( &m_env2Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); - connect( &m_env2Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); - connect( &m_env2Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); - connect( &m_env2Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); - connect( &m_env2Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); - connect( &m_env2Slope, SIGNAL( dataChanged() ), this, SLOT( updateSlope2() ) ); + connect( &m_env2Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ), Qt::DirectConnection ); + connect( &m_env2Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ), Qt::DirectConnection ); + connect( &m_env2Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ), Qt::DirectConnection ); + connect( &m_env2Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ), Qt::DirectConnection ); + connect( &m_env2Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ), Qt::DirectConnection ); + connect( &m_env2Slope, SIGNAL( dataChanged() ), this, SLOT( updateSlope2() ), Qt::DirectConnection ); // updateLFOAtts - connect( &m_lfo1Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ) ); - connect( &m_lfo2Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ) ); + connect( &m_lfo1Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ), Qt::DirectConnection ); + connect( &m_lfo2Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ), Qt::DirectConnection ); // updateSampleRate diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index d7c97fd160e..802986304e1 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -537,9 +537,9 @@ NesInstrument::NesInstrument( InstrumentTrack * instrumentTrack ) : m_masterVol( 1.0f, 0.0f, 2.0f, 0.01f, this, tr( "Master volume" ) ), m_vibrato( 0.0f, 0.0f, 15.0f, 1.0f, this, tr( "Vibrato" ) ) { - connect( &m_ch1Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ) ); - connect( &m_ch2Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ) ); - connect( &m_ch3Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq3() ) ); + connect( &m_ch1Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq1() ), Qt::DirectConnection ); + connect( &m_ch2Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq2() ), Qt::DirectConnection ); + connect( &m_ch3Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq3() ), Qt::DirectConnection ); updateFreq1(); updateFreq2(); diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 9fdae48337f..8c05bb9956a 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -98,28 +98,28 @@ OscillatorObject::OscillatorObject( Model * _parent, int _idx ) : { // Connect knobs with Oscillators' inputs connect( &m_volumeModel, SIGNAL( dataChanged() ), - this, SLOT( updateVolume() ) ); + this, SLOT( updateVolume() ), Qt::DirectConnection ); connect( &m_panModel, SIGNAL( dataChanged() ), - this, SLOT( updateVolume() ) ); + this, SLOT( updateVolume() ), Qt::DirectConnection ); updateVolume(); connect( &m_coarseModel, SIGNAL( dataChanged() ), - this, SLOT( updateDetuningLeft() ) ); + this, SLOT( updateDetuningLeft() ), Qt::DirectConnection ); connect( &m_coarseModel, SIGNAL( dataChanged() ), - this, SLOT( updateDetuningRight() ) ); + this, SLOT( updateDetuningRight() ), Qt::DirectConnection ); connect( &m_fineLeftModel, SIGNAL( dataChanged() ), - this, SLOT( updateDetuningLeft() ) ); + this, SLOT( updateDetuningLeft() ), Qt::DirectConnection ); connect( &m_fineRightModel, SIGNAL( dataChanged() ), - this, SLOT( updateDetuningRight() ) ); + this, SLOT( updateDetuningRight() ), Qt::DirectConnection ); updateDetuningLeft(); updateDetuningRight(); connect( &m_phaseOffsetModel, SIGNAL( dataChanged() ), - this, SLOT( updatePhaseOffsetLeft() ) ); + this, SLOT( updatePhaseOffsetLeft() ), Qt::DirectConnection ); connect( &m_phaseOffsetModel, SIGNAL( dataChanged() ), - this, SLOT( updatePhaseOffsetRight() ) ); + this, SLOT( updatePhaseOffsetRight() ), Qt::DirectConnection ); connect( &m_stereoPhaseDetuningModel, SIGNAL( dataChanged() ), - this, SLOT( updatePhaseOffsetLeft() ) ); + this, SLOT( updatePhaseOffsetLeft() ), Qt::DirectConnection ); updatePhaseOffsetLeft(); updatePhaseOffsetRight(); From b64fe8e7c0afc33ead8c88761efbd71358656890 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sat, 10 Oct 2020 03:17:25 -0300 Subject: [PATCH 110/180] Refactor Clipboard methods (#5627) * Moves mimeType from StringPairDrag to Clipboard * Simplifies decodeKey and decodeValue methods * Adds method to copy a string to clipboard * Adds method to copy a string pair to the Clipboard * Adds convenience methods to Clipboard.h to retrieve the QMimeData from the clipboard and checking whether a particular mime type format is present on it * Uses only the TCOV copy/paste methods on the song editor To keep consistency on the behavior of the TCOV copy and paste operations, we now only use the TCOV::copy() and TCOV::paste() methods for copying both individual and multiple TCOs. * Removes obsolete TrackContentObject::cut() and merge copy() and paste() methods to single function TrackContentObject::copyStateTo() * Adds Clipboard::getString method to get data for particular mime type * Makes AutomatableModelView.cpp use the Clipboard class instead of calling Qt clipboard directly --- include/Clipboard.h | 43 +++-- include/StringPairDrag.h | 8 - include/Track.h | 6 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 8 +- .../audio_file_processor.cpp | 8 +- plugins/patman/patman.cpp | 8 +- plugins/vestige/vestige.cpp | 15 +- plugins/zynaddsubfx/ZynAddSubFx.cpp | 8 +- src/core/Clipboard.cpp | 70 +++++--- src/core/Track.cpp | 150 ++++++------------ src/gui/AutomatableModelView.cpp | 14 +- src/gui/StringPairDrag.cpp | 33 ++-- src/gui/editors/PianoRoll.cpp | 15 +- src/tracks/BBTrack.cpp | 4 +- 14 files changed, 201 insertions(+), 189 deletions(-) diff --git a/include/Clipboard.h b/include/Clipboard.h index fa4d5c5404e..a2dced9a6b7 100644 --- a/include/Clipboard.h +++ b/include/Clipboard.h @@ -29,25 +29,40 @@ #include -class JournallingObject; - -class Clipboard +namespace Clipboard { -public: - typedef QMap Map; - - static void copy( JournallingObject * _object ); - static const QDomElement * getContent( const QString & _node_name ); - - static const char * mimeType() + enum class MimeType { - return( "application/x-lmms-clipboard" ); - } + StringPair, + Default + }; + // Convenience Methods + const QMimeData * getMimeData(); + bool hasFormat( MimeType mT ); -private: - static Map content; + // Helper methods for String data + void copyString( const QString & str, MimeType mT ); + QString getString( MimeType mT ); + // Helper methods for String Pair data + void copyStringPair( const QString & key, const QString & value ); + QString decodeKey( const QMimeData * mimeData ); + QString decodeValue( const QMimeData * mimeData ); + + inline const char * mimeType( MimeType type ) + { + switch( type ) + { + case MimeType::StringPair: + return "application/x-lmms-stringpair"; + break; + case MimeType::Default: + default: + return "application/x-lmms-clipboard"; + break; + } + } } ; #endif diff --git a/include/StringPairDrag.h b/include/StringPairDrag.h index cebc3089a15..969a12eec4e 100644 --- a/include/StringPairDrag.h +++ b/include/StringPairDrag.h @@ -45,16 +45,8 @@ class LMMS_EXPORT StringPairDrag : public QDrag static bool processDragEnterEvent( QDragEnterEvent * _dee, const QString & _allowed_keys ); - static QString decodeMimeKey( const QMimeData * mimeData ); - static QString decodeMimeValue( const QMimeData * mimeData ); static QString decodeKey( QDropEvent * _de ); static QString decodeValue( QDropEvent * _de ); - - static const char * mimeType() - { - return( "application/x-lmms-stringpair" ); - } - } ; diff --git a/include/Track.h b/include/Track.h index 9362a838019..a4930832003 100644 --- a/include/Track.h +++ b/include/Track.h @@ -155,9 +155,10 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject MidiTime startTimeOffset() const; void setStartTimeOffset( const MidiTime &startTimeOffset ); + // Will copy the state of a TCO to another TCO + static void copyStateTo( TrackContentObject *src, TrackContentObject *dst ); + public slots: - void copy(); - void paste(); void toggleMute(); @@ -266,7 +267,6 @@ class TrackContentObjectView : public selectableObject, public ModelView public slots: virtual bool close(); - void cut(); void remove(); void update() override; diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index d5ba48787fb..76d9d017f9d 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -33,6 +33,7 @@ #include "Lv2SubPluginFeatures.h" #include "Mixer.h" #include "StringPairDrag.h" +#include "Clipboard.h" #include "embed.h" #include "plugin_export.h" @@ -238,12 +239,15 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : void Lv2InsView::dragEnterEvent(QDragEnterEvent *_dee) { + // For mimeType() and MimeType enum class + using namespace Clipboard; + void (QDragEnterEvent::*reaction)(void) = &QDragEnterEvent::ignore; - if (_dee->mimeData()->hasFormat(StringPairDrag::mimeType())) + if (_dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ))) { const QString txt = - _dee->mimeData()->data(StringPairDrag::mimeType()); + _dee->mimeData()->data( mimeType( MimeType::StringPair ) ); if (txt.section(':', 0, 0) == "pluginpresetfile") { reaction = &QDragEnterEvent::acceptProposedAction; } diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index d71b111c3cb..97b2759b2dd 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -44,6 +44,7 @@ #include "Song.h" #include "StringPairDrag.h" #include "ToolTip.h" +#include "Clipboard.h" #include "embed.h" #include "plugin_export.h" @@ -568,10 +569,13 @@ AudioFileProcessorView::~AudioFileProcessorView() void AudioFileProcessorView::dragEnterEvent( QDragEnterEvent * _dee ) { - if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) ) + // For mimeType() and MimeType enum class + using namespace Clipboard; + + if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) ) { QString txt = _dee->mimeData()->data( - StringPairDrag::mimeType() ); + mimeType( MimeType::StringPair ) ); if( txt.section( ':', 0, 0 ) == QString( "tco_%1" ).arg( Track::SampleTrack ) ) { diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index d65850edcd0..b694ee2a00f 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -41,6 +41,7 @@ #include "Song.h" #include "StringPairDrag.h" #include "ToolTip.h" +#include "Clipboard.h" #include "embed.h" @@ -580,10 +581,13 @@ void PatmanView::updateFilename( void ) void PatmanView::dragEnterEvent( QDragEnterEvent * _dee ) { - if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) ) + // For mimeType() and MimeType enum class + using namespace Clipboard; + + if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) ) { QString txt = _dee->mimeData()->data( - StringPairDrag::mimeType() ); + mimeType( MimeType::StringPair ) ); if( txt.section( ':', 0, 0 ) == "samplefile" ) { _dee->acceptProposedAction(); diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index dc1723db472..b2c15859464 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -58,6 +58,7 @@ #include "StringPairDrag.h" #include "TextFloat.h" #include "ToolTip.h" +#include "Clipboard.h" #include "embed.h" @@ -833,10 +834,13 @@ void VestigeInstrumentView::noteOffAll( void ) void VestigeInstrumentView::dragEnterEvent( QDragEnterEvent * _dee ) { - if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) ) + // For mimeType() and MimeType enum class + using namespace Clipboard; + + if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) ) { QString txt = _dee->mimeData()->data( - StringPairDrag::mimeType() ); + mimeType( MimeType::StringPair ) ); if( txt.section( ':', 0, 0 ) == "vstplugin" ) { _dee->acceptProposedAction(); @@ -1175,10 +1179,13 @@ void manageVestigeInstrumentView::syncParameterText() void manageVestigeInstrumentView::dragEnterEvent( QDragEnterEvent * _dee ) { - if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) ) + // For mimeType() and MimeType enum class + using namespace Clipboard; + + if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) ) { QString txt = _dee->mimeData()->data( - StringPairDrag::mimeType() ); + mimeType( MimeType::StringPair ) ); if( txt.section( ':', 0, 0 ) == "vstplugin" ) { _dee->acceptProposedAction(); diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 04f7bda0ff3..8446d36f65b 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -47,6 +47,7 @@ #include "LocalZynAddSubFx.h" #include "Mixer.h" #include "ControllerConnection.h" +#include "Clipboard.h" #include "embed.h" #include "plugin_export.h" @@ -578,10 +579,13 @@ ZynAddSubFxView::~ZynAddSubFxView() void ZynAddSubFxView::dragEnterEvent( QDragEnterEvent * _dee ) { - if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) ) + // For mimeType() and MimeType enum class + using namespace Clipboard; + + if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) ) { QString txt = _dee->mimeData()->data( - StringPairDrag::mimeType() ); + mimeType( MimeType::StringPair ) ); if( txt.section( ':', 0, 0 ) == "pluginpresetfile" ) { _dee->acceptProposedAction(); diff --git a/src/core/Clipboard.cpp b/src/core/Clipboard.cpp index 9b1191cdc0d..9b7cf2e775c 100644 --- a/src/core/Clipboard.cpp +++ b/src/core/Clipboard.cpp @@ -24,37 +24,71 @@ #include #include +#include #include "Clipboard.h" #include "JournallingObject.h" -Clipboard::Map Clipboard::content; +namespace Clipboard +{ + const QMimeData * getMimeData() + { + return QApplication::clipboard()->mimeData( QClipboard::Clipboard ); + } -void Clipboard::copy( JournallingObject * _obj ) -{ - QDomDocument doc; - QDomElement parent = doc.createElement( "Clipboard" ); - _obj->saveState( doc, parent ); - content[_obj->nodeName()] = parent.firstChild().toElement(); - - // Clear the QApplication clipboard, so we don't have any conflicts when LMMS has to - // decide between the QApplication clipboard and the internal clipboard data - QApplication::clipboard()->clear( QClipboard::Clipboard ); -} + bool hasFormat( MimeType mT ) + { + return getMimeData()->hasFormat( mimeType( mT ) ); + } -const QDomElement * Clipboard::getContent( const QString & _node_name ) -{ - if( content.find( _node_name ) != content.end() ) + + + void copyString( const QString & str, MimeType mT ) { - return &content[_node_name]; + QMimeData *content = new QMimeData; + + content->setData( mimeType( mT ), str.toUtf8() ); + QApplication::clipboard()->setMimeData( content, QClipboard::Clipboard ); + } + + + + + QString getString( MimeType mT ) + { + return QString( getMimeData()->data( mimeType( mT ) ) ); } - return NULL; -} + + void copyStringPair( const QString & key, const QString & value ) + { + QString finalString = key + ":" + value; + + QMimeData *content = new QMimeData; + content->setData( mimeType( MimeType::StringPair ), finalString.toUtf8() ); + QApplication::clipboard()->setMimeData( content, QClipboard::Clipboard ); + } + + + + + QString decodeKey( const QMimeData * mimeData ) + { + return( QString::fromUtf8( mimeData->data( mimeType( MimeType::StringPair ) ) ).section( ':', 0, 0 ) ); + } + + + + + QString decodeValue( const QMimeData * mimeData ) + { + return( QString::fromUtf8( mimeData->data( mimeType( MimeType::StringPair ) ) ).section( ':', 1, -1 ) ); + } +} diff --git a/src/core/Track.cpp b/src/core/Track.cpp index c7f6d62b0b5..343768b56ff 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -184,34 +184,25 @@ bool TrackContentObject::comparePosition(const TrackContentObject *a, const Trac -/*! \brief Copy this TrackContentObject to the clipboard. +/*! \brief Copies the state of a TrackContentObject to another TrackContentObject * - * Copies this track content object to the clipboard. + * This method copies the state of a TCO to another TCO */ -void TrackContentObject::copy() +void TrackContentObject::copyStateTo( TrackContentObject *src, TrackContentObject *dst ) { - Clipboard::copy( this ); -} - - + // If the node names match we copy the state + if( src->nodeName() == dst->nodeName() ){ + QDomDocument doc; + QDomElement parent = doc.createElement( "StateCopy" ); + src->saveState( doc, parent ); + const MidiTime pos = dst->startPosition(); + dst->restoreState( parent.firstChild().toElement() ); + dst->movePosition( pos ); -/*! \brief Pastes this TrackContentObject into a track. - * - * Pastes this track content object into a track. - * - * \param _je The journal entry to undo - */ -void TrackContentObject::paste() -{ - if( Clipboard::getContent( nodeName() ) != NULL ) - { - const MidiTime pos = startPosition(); - restoreState( *( Clipboard::getContent( nodeName() ) ) ); - movePosition( pos ); + AutomationPattern::resolveAllIDs(); + GuiApplication::instance()->automationEditor()->m_editor->updateAfterPatternChange(); } - AutomationPattern::resolveAllIDs(); - GuiApplication::instance()->automationEditor()->m_editor->updateAfterPatternChange(); } @@ -478,20 +469,6 @@ void TrackContentObjectView::remove() -/*! \brief Cut this trackContentObjectView from its track to the clipboard. - * - * Perform the 'cut' action of the clipboard - copies the track content - * object to the clipboard and then removes it from the track. - */ -void TrackContentObjectView::cut() -{ - m_tco->copy(); - remove(); -} - - - - /*! \brief Updates a trackContentObjectView's length * * If this track content object view has a fixed TCO, then we must @@ -1239,75 +1216,40 @@ void TrackContentObjectView::remove( QVector tcovs ) void TrackContentObjectView::copy( QVector tcovs ) { - // Checks if there are other selected TCOs and if so copy them as well - if( tcovs.size() > 1 ) - { - // Write the TCOs to a DataFile for copying - DataFile dataFile = createTCODataFiles( tcovs ); + // For copyStringPair() + using namespace Clipboard; - // Add the TCO type as a key to the final string - QString finalString = QString( "tco_%1:%2" ).arg( m_tco->getTrack()->type() ).arg( dataFile.toString() ); + // Write the TCOs to a DataFile for copying + DataFile dataFile = createTCODataFiles( tcovs ); - // Copy it to the clipboard - QMimeData *tco_content = new QMimeData; - tco_content->setData( StringPairDrag::mimeType(), finalString.toUtf8() ); - QApplication::clipboard()->setMimeData( tco_content, QClipboard::Clipboard ); - } - else - { - tcovs.at(0)->getTrackContentObject()->copy(); - } + // Copy the TCO type as a key and the TCO data file to the clipboard + copyStringPair( QString( "tco_%1" ).arg( m_tco->getTrack()->type() ), + dataFile.toString() ); } void TrackContentObjectView::cut( QVector tcovs ) { - // Checks if there are other selected TCOs and if so cut them as well - if( tcovs.size() > 1 ) - { - // Write the TCOs to a DataFile for copying - DataFile dataFile = createTCODataFiles( tcovs ); - - // Now that the dataFile is created we can delete the tracks, since we are cutting - // TODO: Is it safe to call tcov->remove(); on the current TCOV instance? - remove( tcovs ); - - // Add the TCO type as a key to the final string - QString finalString = QString( "tco_%1:%2" ).arg( m_tco->getTrack()->type() ).arg( dataFile.toString() ); + // Copy the selected TCOs + copy( tcovs ); - // Copy it to the clipboard - QMimeData *tco_content = new QMimeData; - tco_content->setData( StringPairDrag::mimeType(), finalString.toUtf8() ); - QApplication::clipboard()->setMimeData( tco_content, QClipboard::Clipboard ); - } - else - { - tcovs.at(0)->cut(); - } + // Now that the TCOs are copied we can delete them, since we are cutting + remove( tcovs ); } void TrackContentObjectView::paste() { - // NOTE: Because we give preference to the QApplication clipboard over the LMMS Clipboard class, we need to - // clear the QApplication Clipboard during the LMMS Clipboard copy operations (Clipboard::copy does that) + // For getMimeData() + using namespace Clipboard; - // If we have TCO data on the clipboard paste it. If not, do our regular TCO paste. - if( QApplication::clipboard()->mimeData( QClipboard::Clipboard )->hasFormat( StringPairDrag::mimeType() ) ) - { - // Paste the selection on the MidiTime of the selected Track - const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); - MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + // If possible, paste the selection on the MidiTime of the selected Track and remove it + MidiTime tcoPos = MidiTime( m_tco->startPosition() ); - TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); + TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); - if( tcw->pasteSelection( tcoPos, md ) == true ) - { - // If we succeed on the paste we delete the TCO we pasted on - remove(); - } - } - else + if( tcw->pasteSelection( tcoPos, getMimeData() ) ) { - getTrackContentObject()->paste(); + // If we succeed on the paste we delete the TCO we pasted on + remove(); } } @@ -1705,9 +1647,12 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d // Overloaded method to make it possible to call this method without a Drag&Drop event bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData* md , bool allowSameBar ) { + // For decodeKey() and decodeValue() + using namespace Clipboard; + Track * t = getTrack(); - QString type = StringPairDrag::decodeMimeKey( md ); - QString value = StringPairDrag::decodeMimeValue( md ); + QString type = decodeKey( md ); + QString value = decodeValue( md ); // We can only paste into tracks of the same type if( type != ( "tco_" + QString::number( t->type() ) ) || @@ -1791,14 +1736,17 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) // Overloaded method so we can call it without a Drag&Drop event bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck ) { + // For decodeKey() and decodeValue() + using namespace Clipboard; + // When canPasteSelection was already called before, skipSafetyCheck will skip this if( !skipSafetyCheck && canPasteSelection( tcoPos, md ) == false ) { return false; } - QString type = StringPairDrag::decodeMimeKey( md ); - QString value = StringPairDrag::decodeMimeValue( md ); + QString type = decodeKey( md ); + QString value = decodeValue( md ); getTrack()->addJournalCheckPoint(); @@ -1985,6 +1933,9 @@ MidiTime TrackContentWidget::endPosition( const MidiTime & posStart ) void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) { + // For hasFormat(), MimeType enum class and getMimeData() + using namespace Clipboard; + if( cme->modifiers() ) { return; @@ -1992,8 +1943,7 @@ void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) // If we don't have TCO data in the clipboard there's no need to create this menu // since "paste" is the only action at the moment. - const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); - if( !md->hasFormat( StringPairDrag::mimeType() ) ) + if( ! hasFormat( MimeType::StringPair ) ) { return; } @@ -2002,21 +1952,23 @@ void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } ); // If we can't paste in the current TCW for some reason, disable the action so the user knows - pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), md ) ? true : false ); + pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), getMimeData() ) ? true : false ); contextMenu.exec( QCursor::pos() ); } void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ) { + // For getMimeData() + using namespace Clipboard; + switch( action ) { case Paste: // Paste the selection on the MidiTime of the context menu event - const QMimeData *md = QApplication::clipboard()->mimeData( QClipboard::Clipboard ); MidiTime tcoPos = getPosition( cme->x() ); - pasteSelection( tcoPos, md ); + pasteSelection( tcoPos, getMimeData() ); break; } } diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index 71f3ff8a3bb..6a74ada90fa 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -23,7 +23,6 @@ */ #include -#include #include #include @@ -35,6 +34,7 @@ #include "GuiApplication.h" #include "MainWindow.h" #include "StringPairDrag.h" +#include "Clipboard.h" #include "AutomationEditor.h" @@ -255,8 +255,10 @@ void AutomatableModelViewSlots::unlinkAllModels() void AutomatableModelViewSlots::copyToClipboard() { - QClipboard* clipboard = QApplication::clipboard(); - clipboard->setText(QString::number(m_amv->value() * m_amv->getConversionFactor())); + // For copyString() and MimeType enum class + using namespace Clipboard; + + copyString( QString::number( m_amv->value() * m_amv->getConversionFactor() ), MimeType::Default ); } void AutomatableModelViewSlots::pasteFromClipboard() @@ -272,7 +274,9 @@ void AutomatableModelViewSlots::pasteFromClipboard() /// Attempt to parse a float from the clipboard static float floatFromClipboard(bool* ok) { - const QClipboard* clipboard = QApplication::clipboard(); - return clipboard->text().toFloat(ok); + // For getString() and MimeType enum class + using namespace Clipboard; + + return getString( MimeType::Default ).toFloat(ok); } diff --git a/src/gui/StringPairDrag.cpp b/src/gui/StringPairDrag.cpp index b2b3b0c4a4e..b08f4adc5f6 100644 --- a/src/gui/StringPairDrag.cpp +++ b/src/gui/StringPairDrag.cpp @@ -32,12 +32,16 @@ #include "StringPairDrag.h" #include "GuiApplication.h" #include "MainWindow.h" +#include "Clipboard.h" StringPairDrag::StringPairDrag( const QString & _key, const QString & _value, const QPixmap & _icon, QWidget * _w ) : QDrag( _w ) { + // For mimeType() and MimeType enum class + using namespace Clipboard; + if( _icon.isNull() && _w ) { setPixmap( _w->grab().scaled( @@ -51,7 +55,7 @@ StringPairDrag::StringPairDrag( const QString & _key, const QString & _value, } QString txt = _key + ":" + _value; QMimeData * m = new QMimeData(); - m->setData( mimeType(), txt.toUtf8() ); + m->setData( mimeType( MimeType::StringPair ), txt.toUtf8() ); setMimeData( m ); exec( Qt::LinkAction, Qt::LinkAction ); } @@ -75,11 +79,14 @@ StringPairDrag::~StringPairDrag() bool StringPairDrag::processDragEnterEvent( QDragEnterEvent * _dee, const QString & _allowed_keys ) { - if( !_dee->mimeData()->hasFormat( mimeType() ) ) + // For mimeType() and MimeType enum class + using namespace Clipboard; + + if( !_dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) ) { return( false ); } - QString txt = _dee->mimeData()->data( mimeType() ); + QString txt = _dee->mimeData()->data( mimeType( MimeType::StringPair ) ); if( _allowed_keys.split( ',' ).contains( txt.section( ':', 0, 0 ) ) ) { _dee->acceptProposedAction(); @@ -92,25 +99,9 @@ bool StringPairDrag::processDragEnterEvent( QDragEnterEvent * _dee, -QString StringPairDrag::decodeMimeKey( const QMimeData * mimeData ) -{ - return( QString::fromUtf8( mimeData->data( mimeType() ) ).section( ':', 0, 0 ) ); -} - - - - -QString StringPairDrag::decodeMimeValue( const QMimeData * mimeData ) -{ - return( QString::fromUtf8( mimeData->data( mimeType() ) ).section( ':', 1, -1 ) ); -} - - - - QString StringPairDrag::decodeKey( QDropEvent * _de ) { - return decodeMimeKey( _de->mimeData() ); + return Clipboard::decodeKey( _de->mimeData() ); } @@ -118,5 +109,5 @@ QString StringPairDrag::decodeKey( QDropEvent * _de ) QString StringPairDrag::decodeValue( QDropEvent * _de ) { - return decodeMimeValue( _de->mimeData() ); + return Clipboard::decodeValue( _de->mimeData() ); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 3ce684ecef8..8f1a827a95e 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3901,6 +3901,9 @@ void PianoRoll::updateYScroll() void PianoRoll::copyToClipboard( const NoteVector & notes ) const { + // For copyString() and MimeType enum class + using namespace Clipboard; + DataFile dataFile( DataFile::ClipboardData ); QDomElement note_list = dataFile.createElement( "note-list" ); dataFile.content().appendChild( note_list ); @@ -3913,10 +3916,7 @@ void PianoRoll::copyToClipboard( const NoteVector & notes ) const clip_note.saveState( dataFile, note_list ); } - QMimeData * clip_content = new QMimeData; - clip_content->setData( Clipboard::mimeType(), dataFile.toString().toUtf8() ); - QApplication::clipboard()->setMimeData( clip_content, - QClipboard::Clipboard ); + copyString( dataFile.toString(), MimeType::Default ); } @@ -3969,14 +3969,15 @@ void PianoRoll::cutSelectedNotes() void PianoRoll::pasteNotes() { + // For getString() and MimeType enum class + using namespace Clipboard; + if( ! hasValidPattern() ) { return; } - QString value = QApplication::clipboard() - ->mimeData( QClipboard::Clipboard ) - ->data( Clipboard::mimeType() ); + QString value = getString( MimeType::Default ); if( ! value.isEmpty() ) { diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index ec6b4042004..24d0fc9bdd9 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -525,8 +525,8 @@ void BBTrack::loadTrackSpecificSettings( const QDomElement & _this ) for( TrackContainer::TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) { - ( *it )->getTCO( src )->copy(); - ( *it )->getTCO( dst )->paste(); + TrackContentObject::copyStateTo( ( *it )->getTCO( src ), + ( *it )->getTCO( dst ) ); } setName( tr( "Clone of %1" ).arg( _this.parentNode().toElement().attribute( "name" ) ) ); From 9db671c7aed3ae6dfbb21d0afc5df58b9b035332 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 17 Oct 2020 20:00:13 +0200 Subject: [PATCH 111/180] Refactor writing MIDI events to buffers This moves out the code from the carla plugin into the core, because this code will be re-used for Lv2 MIDI handling soon. --- include/MidiEventToByteSeq.h | 47 ++++++++++++ plugins/carlabase/carla.cpp | 67 +---------------- src/core/CMakeLists.txt | 1 + src/core/midi/MidiEventToByteSeq.cpp | 107 +++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 63 deletions(-) create mode 100644 include/MidiEventToByteSeq.h create mode 100644 src/core/midi/MidiEventToByteSeq.cpp diff --git a/include/MidiEventToByteSeq.h b/include/MidiEventToByteSeq.h new file mode 100644 index 00000000000..fba8cdeb414 --- /dev/null +++ b/include/MidiEventToByteSeq.h @@ -0,0 +1,47 @@ +/* + * MidiEventToByteSeq.h - writeToByteSeq declaration + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef MIDIEVENTTOBYTESEQ_H +#define MIDIEVENTTOBYTESEQ_H + +#include +#include + +#include "lmms_export.h" + +/** + Write MIDI event into byte sequence. + + Conforming to http://lv2plug.in/ns/ext/midi#MidiEvent + + @param data Pointer to the target buffer for the byte sequence. Must + point to existing memory with at least 3 bytes size. + @param bufsize Available size of the target buffer. + @return Used size of the target buffer, or 0 if the MidiEvent could not + be converted. +*/ +std::size_t LMMS_EXPORT writeToByteSeq( const class MidiEvent& ev, + uint8_t* data, std::size_t bufsize ); + +#endif // MIDIEVENTTOBYTESEQ_H diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index a6faa4d275d..f437431271c 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -29,6 +29,7 @@ #include "gui_templates.h" #include "InstrumentPlayHandle.h" #include "InstrumentTrack.h" +#include "MidiEventToByteSeq.h" #include "Mixer.h" #include @@ -372,69 +373,9 @@ bool CarlaInstrument::handleMidiEvent(const MidiEvent& event, const MidiTime&, f nEvent.port = 0; nEvent.time = offset; - nEvent.data[0] = event.type() | (event.channel() & 0x0F); - - switch (event.type()) - { - case MidiNoteOn: - if (event.velocity() > 0) - { - if (event.key() < 0 || event.key() > MidiMaxKey) - break; - - nEvent.data[1] = event.key(); - nEvent.data[2] = event.velocity(); - nEvent.size = 3; - break; - } - else - { - nEvent.data[0] = MidiNoteOff | (event.channel() & 0x0F); - // nobreak - } - - case MidiNoteOff: - if (event.key() < 0 || event.key() > MidiMaxKey) - break; - - nEvent.data[1] = event.key(); - nEvent.data[2] = event.velocity(); - nEvent.size = 3; - break; - - case MidiKeyPressure: - nEvent.data[1] = event.key(); - nEvent.data[2] = event.velocity(); - nEvent.size = 3; - break; - - case MidiControlChange: - nEvent.data[1] = event.controllerNumber(); - nEvent.data[2] = event.controllerValue(); - nEvent.size = 3; - break; - - case MidiProgramChange: - nEvent.data[1] = event.program(); - nEvent.size = 2; - break; - - case MidiChannelPressure: - nEvent.data[1] = event.channelPressure(); - nEvent.size = 2; - break; - - case MidiPitchBend: - nEvent.data[1] = event.pitchBend() & 0x7f; - nEvent.data[2] = event.pitchBend() >> 7; - nEvent.size = 3; - break; - - default: - // unhandled - --fMidiEventCount; - break; - } + std::size_t written = writeToByteSeq(event, nEvent.data, sizeof(NativeMidiEvent::data)); + if(written) { nEvent.size = written; } + else { --fMidiEventCount; } return true; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b7685522c24..82fc4c63ec3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -104,6 +104,7 @@ set(LMMS_SRCS core/midi/MidiAlsaSeq.cpp core/midi/MidiClient.cpp core/midi/MidiController.cpp + core/midi/MidiEventToByteSeq.cpp core/midi/MidiJack.cpp core/midi/MidiOss.cpp core/midi/MidiSndio.cpp diff --git a/src/core/midi/MidiEventToByteSeq.cpp b/src/core/midi/MidiEventToByteSeq.cpp new file mode 100644 index 00000000000..98f0541d032 --- /dev/null +++ b/src/core/midi/MidiEventToByteSeq.cpp @@ -0,0 +1,107 @@ +/* + * MidiEventToByteSeq.cpp - writeToByteSeq implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "MidiEventToByteSeq.h" + +#include + +#include "MidiEvent.h" + + +std::size_t writeToByteSeq( + const MidiEvent& ev, uint8_t *data, std::size_t bufsize) +{ + Q_ASSERT(bufsize >= 3); + + std::size_t size = 0; + data[0] = ev.type() | (ev.channel() & 0x0F); + + switch (ev.type()) + { + case MidiNoteOn: + if (ev.velocity() > 0) + { + if (ev.key() < 0 || ev.key() > MidiMaxKey) + break; + + data[1] = ev.key(); + data[2] = ev.velocity(); + size = 3; + break; + } + else + { + // Lv2 MIDI specs: + // "Note On messages with velocity 0 are not allowed. + // These messages are equivalent to Note Off in standard + // MIDI streams, but here only proper Note Off messages + // are allowed." + data[0] = MidiNoteOff | (ev.channel() & 0x0F); + // nobreak + } + + case MidiNoteOff: + if (ev.key() < 0 || ev.key() > MidiMaxKey) + break; + data[1] = ev.key(); + data[2] = ev.velocity(); // release time + size = 3; + break; + + case MidiKeyPressure: + data[1] = ev.key(); + data[2] = ev.velocity(); + size = 3; + break; + + case MidiControlChange: + data[1] = ev.controllerNumber(); + data[2] = ev.controllerValue(); + size = 3; + break; + + case MidiProgramChange: + data[1] = ev.program(); + size = 2; + break; + + case MidiChannelPressure: + data[1] = ev.channelPressure(); + size = 2; + break; + + case MidiPitchBend: + data[1] = ev.pitchBend() & 0x7f; + data[2] = ev.pitchBend() >> 7; + size = 3; + break; + + default: + // unhandled + break; + } + + return size; +} + From 0d89d64f499cab7cdbef47bf2325306a6cb47098 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 18 Oct 2020 14:24:16 +0200 Subject: [PATCH 112/180] Implement unused Lv2UridCache --- include/Lv2Manager.h | 6 +++- include/Lv2UridCache.h | 52 ++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/lv2/Lv2Manager.cpp | 3 +- src/core/lv2/Lv2UridCache.cpp | 57 +++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 include/Lv2UridCache.h create mode 100644 src/core/lv2/Lv2UridCache.cpp diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 0b5fc692354..6261d70f6d7 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -34,6 +34,7 @@ #include #include "Lv2Basics.h" +#include "Lv2UridCache.h" #include "Lv2UridMap.h" #include "Plugin.h" @@ -123,7 +124,7 @@ class Lv2Manager }; UridMap& uridMap() { return m_uridMap; } - //! Return all + const Lv2UridCache& uridCache() const { return m_uridCache; } const std::set& supportedFeatureURIs() const { return m_supportedFeatureURIs; @@ -140,6 +141,9 @@ class Lv2Manager // feature data that are common for all Lv2Proc UridMap m_uridMap; + // URID cache for fast URID access + Lv2UridCache m_uridCache; + // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; diff --git a/include/Lv2UridCache.h b/include/Lv2UridCache.h new file mode 100644 index 00000000000..b4cfa59f33d --- /dev/null +++ b/include/Lv2UridCache.h @@ -0,0 +1,52 @@ +/* + * Lv2UridCache.h - Lv2UridCache definition + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2URIDCACHE_H +#define LV2URIDCACHE_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +//! Cached URIDs for fast access (for use in real-time code) +class Lv2UridCache +{ +public: + enum class Id //!< ID for m_uridCache array + { + midi_MidiEvent, //!< just an example, unused yet + size + }; + //! Return URID for a cache ID + uint32_t operator[](Id id) const; + + Lv2UridCache(class UridMap& mapper); +private: + uint32_t m_cache[static_cast(Id::size)]; +}; + +#endif // LMMS_HAVE_LV2 +#endif // LV2URIDCACHE_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 82fc4c63ec3..8dbee31d31b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -98,6 +98,7 @@ set(LMMS_SRCS core/lv2/Lv2Proc.cpp core/lv2/Lv2Manager.cpp core/lv2/Lv2SubPluginFeatures.cpp + core/lv2/Lv2UridCache.cpp core/lv2/Lv2UridMap.cpp core/midi/MidiAlsaRaw.cpp diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 69fbd0137c5..b1f03079bb6 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -44,7 +44,8 @@ -Lv2Manager::Lv2Manager() +Lv2Manager::Lv2Manager() : + m_uridCache(m_uridMap) { const char* dbgStr = getenv("LMMS_LV2_DEBUG"); m_debug = (dbgStr && *dbgStr); diff --git a/src/core/lv2/Lv2UridCache.cpp b/src/core/lv2/Lv2UridCache.cpp new file mode 100644 index 00000000000..5887a8e3906 --- /dev/null +++ b/src/core/lv2/Lv2UridCache.cpp @@ -0,0 +1,57 @@ +/* + * Lv2UridCache.cpp - Lv2UridCache implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2UridCache.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include + +#include "Lv2UridMap.h" + +uint32_t Lv2UridCache::operator[](Lv2UridCache::Id id) const +{ + Q_ASSERT(id != Id::size); + return m_cache[static_cast(id)]; +} + +Lv2UridCache::Lv2UridCache(UridMap &mapper) +{ + const uint32_t noIdYet = 0; + std::fill_n(m_cache, static_cast(Id::size), noIdYet); + + auto init = [this, &mapper](Id id, const char* uridStr) + { + m_cache[static_cast(id)] = mapper.map(uridStr); + }; + + init(Id::midi_MidiEvent, LV2_MIDI__MidiEvent); + + for(uint32_t urid : m_cache) { Q_ASSERT(urid != noIdYet); } +} + +#endif // LMMS_HAVE_LV2 + + From 4c559b91f83866e1a10faf3600e7613d47bc6a34 Mon Sep 17 00:00:00 2001 From: dj-pixus <65627470+dj-pixus@users.noreply.github.com> Date: Mon, 19 Oct 2020 19:42:39 +0200 Subject: [PATCH 113/180] Modifying factory presets to sound more friendly. (#5529) Modified some factory presets to sound more friendly. --- .../presets/AudioFileProcessor/Bass-Mania.xpf | 2 +- data/presets/AudioFileProcessor/Erazor.xpf | 2 +- .../AudioFileProcessor/Fat-Reversed-Kick.xpf | 2 +- .../Kick-4-your-Subwoofer.xpf | 9 ++- data/presets/AudioFileProcessor/SString.xpf | 2 +- data/presets/AudioFileProcessor/orion.xpf | 2 +- data/presets/BitInvader/alien_strings.xpf | 9 +++ data/presets/BitInvader/bell.xpf | 4 +- data/presets/BitInvader/drama.xpf | 9 +++ data/presets/BitInvader/pluck.xpf | 13 +++- data/presets/BitInvader/soft_pad.xpf | 4 +- data/presets/BitInvader/spacefx.xpf | 2 +- data/presets/BitInvader/subbass.xpf | 2 +- data/presets/BitInvader/toy_piano.xpf | 6 +- data/presets/BitInvader/wah_synth.xpf | 4 +- data/presets/Kicker/Clap.xpf | 2 +- data/presets/Kicker/HihatOpen.xpf | 2 +- data/presets/Kicker/Shaker.xpf | 2 +- data/presets/Kicker/SnareLong.xpf | 2 +- data/presets/Kicker/TrapKick.xpf | 2 +- data/presets/LB302/STrash.xpf | 22 +------ data/presets/Monstro/Growl.xpf | 64 +++++++++---------- data/presets/OpulenZ/Clarinet.xpf | 2 +- data/presets/OpulenZ/Combo_organ.xpf | 4 +- data/presets/OpulenZ/Epiano.xpf | 2 +- data/presets/OpulenZ/Organ_leslie.xpf | 2 +- data/presets/OpulenZ/Square.xpf | 2 +- data/presets/Organic/Rubberband.xpf | 14 +++- data/presets/Organic/organ_blues.xpf | 2 +- data/presets/Organic/organ_risingsun.xpf | 2 +- data/presets/Organic/puresine.xpf | 2 +- data/presets/Organic/sequencer_64.xpf | 2 +- data/presets/SID/Bass.xpf | 2 +- .../presets/TripleOscillator/AnalogDreamz.xpf | 4 +- data/presets/TripleOscillator/Arpeggio.xpf | 4 +- .../presets/TripleOscillator/ArpeggioPing.xpf | 2 +- .../presets/TripleOscillator/BlandModBass.xpf | 2 +- data/presets/TripleOscillator/BrokenToy.xpf | 6 +- .../presets/TripleOscillator/DetunedGhost.xpf | 3 +- data/presets/TripleOscillator/DirtyReece.xpf | 2 +- .../TripleOscillator/Drums_HardKick.xpf | 2 +- .../presets/TripleOscillator/Drums_HihatO.xpf | 2 +- data/presets/TripleOscillator/Drums_Kick.xpf | 29 +++++---- data/presets/TripleOscillator/Drums_Snare.xpf | 29 +++++---- .../TripleOscillator/FuzzyAnalogBass.xpf | 33 ++++++---- data/presets/TripleOscillator/GhostBoy.xpf | 4 +- .../TripleOscillator/Harp-of-a-Fairy.xpf | 2 +- data/presets/TripleOscillator/Jupiter.xpf | 4 +- data/presets/TripleOscillator/LFO-party.xpf | 2 +- data/presets/TripleOscillator/LovelyDream.xpf | 2 +- .../presets/TripleOscillator/MoogArpeggio.xpf | 2 +- .../TripleOscillator/OldComputerGames.xpf | 20 +----- data/presets/TripleOscillator/PMFMFTWbass.xpf | 4 +- .../TripleOscillator/PluckArpeggio.xpf | 2 +- data/presets/TripleOscillator/PluckBass.xpf | 25 -------- .../presets/TripleOscillator/PowerStrings.xpf | 6 +- data/presets/TripleOscillator/RaveBass.xpf | 4 +- data/presets/TripleOscillator/SawReso.xpf | 2 +- data/presets/TripleOscillator/SpaceBass.xpf | 4 +- data/presets/TripleOscillator/Square.xpf | 6 +- data/presets/TripleOscillator/TINTNpad.xpf | 2 +- data/presets/TripleOscillator/TheMaster.xpf | 15 +---- data/presets/TripleOscillator/TranceLead.xpf | 34 +++++----- data/presets/TripleOscillator/WarmStack.xpf | 2 +- data/presets/TripleOscillator/Whistle.xpf | 4 +- data/presets/Vibed/Harpsichord.xpf | 4 +- data/presets/Vibed/SadPad.xpf | 4 +- data/presets/Xpressive/Ambition.xpf | 2 +- data/presets/Xpressive/Creature.xpf | 2 +- data/presets/Xpressive/Low Battery.xpf | 2 +- data/presets/Xpressive/Rubber Bass.xpf | 2 +- data/presets/Xpressive/Untuned Bell.xpf | 4 +- 72 files changed, 233 insertions(+), 255 deletions(-) diff --git a/data/presets/AudioFileProcessor/Bass-Mania.xpf b/data/presets/AudioFileProcessor/Bass-Mania.xpf index 4a92c00289f..f6a0164350f 100644 --- a/data/presets/AudioFileProcessor/Bass-Mania.xpf +++ b/data/presets/AudioFileProcessor/Bass-Mania.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/AudioFileProcessor/Erazor.xpf b/data/presets/AudioFileProcessor/Erazor.xpf index f106c27f893..a0481bbc420 100644 --- a/data/presets/AudioFileProcessor/Erazor.xpf +++ b/data/presets/AudioFileProcessor/Erazor.xpf @@ -8,7 +8,7 @@ - + diff --git a/data/presets/AudioFileProcessor/Fat-Reversed-Kick.xpf b/data/presets/AudioFileProcessor/Fat-Reversed-Kick.xpf index f9f11ef8b75..2609144de1d 100644 --- a/data/presets/AudioFileProcessor/Fat-Reversed-Kick.xpf +++ b/data/presets/AudioFileProcessor/Fat-Reversed-Kick.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/AudioFileProcessor/Kick-4-your-Subwoofer.xpf b/data/presets/AudioFileProcessor/Kick-4-your-Subwoofer.xpf index 6a9da93ce1d..ac60499f793 100644 --- a/data/presets/AudioFileProcessor/Kick-4-your-Subwoofer.xpf +++ b/data/presets/AudioFileProcessor/Kick-4-your-Subwoofer.xpf @@ -3,16 +3,15 @@ - - + + - + - - + \ No newline at end of file diff --git a/data/presets/AudioFileProcessor/SString.xpf b/data/presets/AudioFileProcessor/SString.xpf index 1def1d71942..bcff41ef671 100644 --- a/data/presets/AudioFileProcessor/SString.xpf +++ b/data/presets/AudioFileProcessor/SString.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/AudioFileProcessor/orion.xpf b/data/presets/AudioFileProcessor/orion.xpf index 101286b298e..21b31dee0c9 100644 --- a/data/presets/AudioFileProcessor/orion.xpf +++ b/data/presets/AudioFileProcessor/orion.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/alien_strings.xpf b/data/presets/BitInvader/alien_strings.xpf index d29fdca1070..f8e6cf1a540 100644 --- a/data/presets/BitInvader/alien_strings.xpf +++ b/data/presets/BitInvader/alien_strings.xpf @@ -13,6 +13,15 @@ + + + + + + + + + diff --git a/data/presets/BitInvader/bell.xpf b/data/presets/BitInvader/bell.xpf index a5346e212d3..f142e64f80a 100644 --- a/data/presets/BitInvader/bell.xpf +++ b/data/presets/BitInvader/bell.xpf @@ -3,10 +3,10 @@ - + - + diff --git a/data/presets/BitInvader/drama.xpf b/data/presets/BitInvader/drama.xpf index fc564fa586e..b69f60c3050 100644 --- a/data/presets/BitInvader/drama.xpf +++ b/data/presets/BitInvader/drama.xpf @@ -13,6 +13,15 @@ + + + + + + + + + diff --git a/data/presets/BitInvader/pluck.xpf b/data/presets/BitInvader/pluck.xpf index b71974e4edf..db933579083 100644 --- a/data/presets/BitInvader/pluck.xpf +++ b/data/presets/BitInvader/pluck.xpf @@ -3,16 +3,25 @@ - + - + + + + + + + + + + diff --git a/data/presets/BitInvader/soft_pad.xpf b/data/presets/BitInvader/soft_pad.xpf index 63803acb874..f36ba5401a1 100644 --- a/data/presets/BitInvader/soft_pad.xpf +++ b/data/presets/BitInvader/soft_pad.xpf @@ -3,8 +3,8 @@ - - + + diff --git a/data/presets/BitInvader/spacefx.xpf b/data/presets/BitInvader/spacefx.xpf index b10f4737789..21b374b128f 100644 --- a/data/presets/BitInvader/spacefx.xpf +++ b/data/presets/BitInvader/spacefx.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/BitInvader/subbass.xpf b/data/presets/BitInvader/subbass.xpf index 50ebb46c6f7..8af395d75e4 100644 --- a/data/presets/BitInvader/subbass.xpf +++ b/data/presets/BitInvader/subbass.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/BitInvader/toy_piano.xpf b/data/presets/BitInvader/toy_piano.xpf index 0fed791d1c3..5928aa4ff71 100644 --- a/data/presets/BitInvader/toy_piano.xpf +++ b/data/presets/BitInvader/toy_piano.xpf @@ -3,10 +3,10 @@ - - + + - + diff --git a/data/presets/BitInvader/wah_synth.xpf b/data/presets/BitInvader/wah_synth.xpf index e8e68c53885..c6d77a04443 100644 --- a/data/presets/BitInvader/wah_synth.xpf +++ b/data/presets/BitInvader/wah_synth.xpf @@ -3,10 +3,10 @@ - + - + diff --git a/data/presets/Kicker/Clap.xpf b/data/presets/Kicker/Clap.xpf index d354051ce63..12a0989cb76 100644 --- a/data/presets/Kicker/Clap.xpf +++ b/data/presets/Kicker/Clap.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Kicker/HihatOpen.xpf b/data/presets/Kicker/HihatOpen.xpf index c0f0aa69235..e0ccd7a70f8 100644 --- a/data/presets/Kicker/HihatOpen.xpf +++ b/data/presets/Kicker/HihatOpen.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/Kicker/Shaker.xpf b/data/presets/Kicker/Shaker.xpf index 13eee953e90..0102f8caca1 100644 --- a/data/presets/Kicker/Shaker.xpf +++ b/data/presets/Kicker/Shaker.xpf @@ -8,7 +8,7 @@ - + diff --git a/data/presets/Kicker/SnareLong.xpf b/data/presets/Kicker/SnareLong.xpf index b8b2f9d884a..8c5202c054c 100644 --- a/data/presets/Kicker/SnareLong.xpf +++ b/data/presets/Kicker/SnareLong.xpf @@ -8,7 +8,7 @@ - + diff --git a/data/presets/Kicker/TrapKick.xpf b/data/presets/Kicker/TrapKick.xpf index a8ad8976da1..28827b9461d 100644 --- a/data/presets/Kicker/TrapKick.xpf +++ b/data/presets/Kicker/TrapKick.xpf @@ -8,7 +8,7 @@ - + diff --git a/data/presets/LB302/STrash.xpf b/data/presets/LB302/STrash.xpf index bd928e63cd4..a539be35ef5 100644 --- a/data/presets/LB302/STrash.xpf +++ b/data/presets/LB302/STrash.xpf @@ -3,23 +3,11 @@ - + - - - - - - - - - - - - @@ -31,14 +19,6 @@ - - - - - - - - diff --git a/data/presets/Monstro/Growl.xpf b/data/presets/Monstro/Growl.xpf index f553b16411f..307343d488c 100644 --- a/data/presets/Monstro/Growl.xpf +++ b/data/presets/Monstro/Growl.xpf @@ -1,22 +1,22 @@ - + - - + + - + - - - - + + + + - - - + + + - + @@ -30,39 +30,39 @@ - - + + - - - + + + - - + + - - + + - - - - - + + + + + - - + + - + @@ -70,11 +70,11 @@ - - + + - + diff --git a/data/presets/OpulenZ/Clarinet.xpf b/data/presets/OpulenZ/Clarinet.xpf index 3301318a282..5d516a215ee 100644 --- a/data/presets/OpulenZ/Clarinet.xpf +++ b/data/presets/OpulenZ/Clarinet.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/OpulenZ/Combo_organ.xpf b/data/presets/OpulenZ/Combo_organ.xpf index 21e2d8a62ec..06c9e06610f 100644 --- a/data/presets/OpulenZ/Combo_organ.xpf +++ b/data/presets/OpulenZ/Combo_organ.xpf @@ -3,9 +3,9 @@ - + - + diff --git a/data/presets/OpulenZ/Epiano.xpf b/data/presets/OpulenZ/Epiano.xpf index dea947763e3..3478d7e3e71 100644 --- a/data/presets/OpulenZ/Epiano.xpf +++ b/data/presets/OpulenZ/Epiano.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/OpulenZ/Organ_leslie.xpf b/data/presets/OpulenZ/Organ_leslie.xpf index a1c5c024e8b..5cf5ef983aa 100644 --- a/data/presets/OpulenZ/Organ_leslie.xpf +++ b/data/presets/OpulenZ/Organ_leslie.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/OpulenZ/Square.xpf b/data/presets/OpulenZ/Square.xpf index ad0aaca52f4..8fba89a8b33 100644 --- a/data/presets/OpulenZ/Square.xpf +++ b/data/presets/OpulenZ/Square.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/Organic/Rubberband.xpf b/data/presets/Organic/Rubberband.xpf index 1dd0a194146..9b328541e96 100644 --- a/data/presets/Organic/Rubberband.xpf +++ b/data/presets/Organic/Rubberband.xpf @@ -3,19 +3,27 @@ - + - + - + + + + + + + + + diff --git a/data/presets/Organic/organ_blues.xpf b/data/presets/Organic/organ_blues.xpf index a35bbfda855..c5cf9d04e2f 100644 --- a/data/presets/Organic/organ_blues.xpf +++ b/data/presets/Organic/organ_blues.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/Organic/organ_risingsun.xpf b/data/presets/Organic/organ_risingsun.xpf index b5dc7590639..311504acba0 100644 --- a/data/presets/Organic/organ_risingsun.xpf +++ b/data/presets/Organic/organ_risingsun.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/Organic/puresine.xpf b/data/presets/Organic/puresine.xpf index 0c77ca9ca42..9c5088cdd19 100644 --- a/data/presets/Organic/puresine.xpf +++ b/data/presets/Organic/puresine.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/Organic/sequencer_64.xpf b/data/presets/Organic/sequencer_64.xpf index c1f8fc22398..f2d5016fb5b 100644 --- a/data/presets/Organic/sequencer_64.xpf +++ b/data/presets/Organic/sequencer_64.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/SID/Bass.xpf b/data/presets/SID/Bass.xpf index c5f605c8fd7..24dd6ded217 100644 --- a/data/presets/SID/Bass.xpf +++ b/data/presets/SID/Bass.xpf @@ -8,7 +8,7 @@ - + diff --git a/data/presets/TripleOscillator/AnalogDreamz.xpf b/data/presets/TripleOscillator/AnalogDreamz.xpf index 0af114cb9dd..38865bf9ef3 100644 --- a/data/presets/TripleOscillator/AnalogDreamz.xpf +++ b/data/presets/TripleOscillator/AnalogDreamz.xpf @@ -3,9 +3,9 @@ - + - + diff --git a/data/presets/TripleOscillator/Arpeggio.xpf b/data/presets/TripleOscillator/Arpeggio.xpf index 32300966fc2..ad600d1383e 100644 --- a/data/presets/TripleOscillator/Arpeggio.xpf +++ b/data/presets/TripleOscillator/Arpeggio.xpf @@ -4,9 +4,9 @@ - + - + diff --git a/data/presets/TripleOscillator/ArpeggioPing.xpf b/data/presets/TripleOscillator/ArpeggioPing.xpf index 5da4c30d83f..e42604b8f62 100644 --- a/data/presets/TripleOscillator/ArpeggioPing.xpf +++ b/data/presets/TripleOscillator/ArpeggioPing.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/BlandModBass.xpf b/data/presets/TripleOscillator/BlandModBass.xpf index 3160bd09725..593d1f4a117 100644 --- a/data/presets/TripleOscillator/BlandModBass.xpf +++ b/data/presets/TripleOscillator/BlandModBass.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/TripleOscillator/BrokenToy.xpf b/data/presets/TripleOscillator/BrokenToy.xpf index ed0ac370e1d..6a8eea0dd31 100644 --- a/data/presets/TripleOscillator/BrokenToy.xpf +++ b/data/presets/TripleOscillator/BrokenToy.xpf @@ -3,12 +3,12 @@ - + - + - + diff --git a/data/presets/TripleOscillator/DetunedGhost.xpf b/data/presets/TripleOscillator/DetunedGhost.xpf index 4f180b6e380..d19280b28a6 100644 --- a/data/presets/TripleOscillator/DetunedGhost.xpf +++ b/data/presets/TripleOscillator/DetunedGhost.xpf @@ -9,8 +9,7 @@ - - + diff --git a/data/presets/TripleOscillator/DirtyReece.xpf b/data/presets/TripleOscillator/DirtyReece.xpf index d574c72997e..8a0dea3add5 100644 --- a/data/presets/TripleOscillator/DirtyReece.xpf +++ b/data/presets/TripleOscillator/DirtyReece.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/Drums_HardKick.xpf b/data/presets/TripleOscillator/Drums_HardKick.xpf index 64bd319493b..6b322280b38 100644 --- a/data/presets/TripleOscillator/Drums_HardKick.xpf +++ b/data/presets/TripleOscillator/Drums_HardKick.xpf @@ -94,7 +94,7 @@ - + diff --git a/data/presets/TripleOscillator/Drums_HihatO.xpf b/data/presets/TripleOscillator/Drums_HihatO.xpf index 19258c57a04..51b2b457a2a 100644 --- a/data/presets/TripleOscillator/Drums_HihatO.xpf +++ b/data/presets/TripleOscillator/Drums_HihatO.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/Drums_Kick.xpf b/data/presets/TripleOscillator/Drums_Kick.xpf index d18e0ed1756..7ef4d234ac8 100644 --- a/data/presets/TripleOscillator/Drums_Kick.xpf +++ b/data/presets/TripleOscillator/Drums_Kick.xpf @@ -1,21 +1,26 @@ - + - - + + - + - - - - + + + + - - - - + + + + + + + + + diff --git a/data/presets/TripleOscillator/Drums_Snare.xpf b/data/presets/TripleOscillator/Drums_Snare.xpf index 50731bf56ab..08059d7b16e 100644 --- a/data/presets/TripleOscillator/Drums_Snare.xpf +++ b/data/presets/TripleOscillator/Drums_Snare.xpf @@ -1,21 +1,26 @@ - + - - + + - + - - - - + + + + - - - - + + + + + + + + + diff --git a/data/presets/TripleOscillator/FuzzyAnalogBass.xpf b/data/presets/TripleOscillator/FuzzyAnalogBass.xpf index 78b176dfdca..c0cf170378a 100644 --- a/data/presets/TripleOscillator/FuzzyAnalogBass.xpf +++ b/data/presets/TripleOscillator/FuzzyAnalogBass.xpf @@ -1,21 +1,30 @@ - + - - + + - + + + - - - - + + + + - - - - + + + + + + + + + + + diff --git a/data/presets/TripleOscillator/GhostBoy.xpf b/data/presets/TripleOscillator/GhostBoy.xpf index 0742a1c3c35..bbf07b1562b 100644 --- a/data/presets/TripleOscillator/GhostBoy.xpf +++ b/data/presets/TripleOscillator/GhostBoy.xpf @@ -3,9 +3,9 @@ - + - + diff --git a/data/presets/TripleOscillator/Harp-of-a-Fairy.xpf b/data/presets/TripleOscillator/Harp-of-a-Fairy.xpf index c248b9c4811..f1c7360f20b 100644 --- a/data/presets/TripleOscillator/Harp-of-a-Fairy.xpf +++ b/data/presets/TripleOscillator/Harp-of-a-Fairy.xpf @@ -4,7 +4,7 @@ - + diff --git a/data/presets/TripleOscillator/Jupiter.xpf b/data/presets/TripleOscillator/Jupiter.xpf index 86b59aac599..3289f922258 100644 --- a/data/presets/TripleOscillator/Jupiter.xpf +++ b/data/presets/TripleOscillator/Jupiter.xpf @@ -3,9 +3,9 @@ - + - + diff --git a/data/presets/TripleOscillator/LFO-party.xpf b/data/presets/TripleOscillator/LFO-party.xpf index f5d9b1baa7a..30228d11746 100644 --- a/data/presets/TripleOscillator/LFO-party.xpf +++ b/data/presets/TripleOscillator/LFO-party.xpf @@ -8,7 +8,7 @@ - + diff --git a/data/presets/TripleOscillator/LovelyDream.xpf b/data/presets/TripleOscillator/LovelyDream.xpf index 589640975cd..52769caf83b 100644 --- a/data/presets/TripleOscillator/LovelyDream.xpf +++ b/data/presets/TripleOscillator/LovelyDream.xpf @@ -6,7 +6,7 @@ - + diff --git a/data/presets/TripleOscillator/MoogArpeggio.xpf b/data/presets/TripleOscillator/MoogArpeggio.xpf index d2f280c1b5b..987e7100d00 100644 --- a/data/presets/TripleOscillator/MoogArpeggio.xpf +++ b/data/presets/TripleOscillator/MoogArpeggio.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/OldComputerGames.xpf b/data/presets/TripleOscillator/OldComputerGames.xpf index 674bef67470..8a8534b4424 100644 --- a/data/presets/TripleOscillator/OldComputerGames.xpf +++ b/data/presets/TripleOscillator/OldComputerGames.xpf @@ -3,7 +3,7 @@ - + @@ -73,24 +73,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/data/presets/TripleOscillator/PMFMFTWbass.xpf b/data/presets/TripleOscillator/PMFMFTWbass.xpf index d81b5560166..a707d2460ab 100644 --- a/data/presets/TripleOscillator/PMFMFTWbass.xpf +++ b/data/presets/TripleOscillator/PMFMFTWbass.xpf @@ -5,10 +5,10 @@ - + - + diff --git a/data/presets/TripleOscillator/PluckArpeggio.xpf b/data/presets/TripleOscillator/PluckArpeggio.xpf index 44ce26c8e20..efc47b51604 100644 --- a/data/presets/TripleOscillator/PluckArpeggio.xpf +++ b/data/presets/TripleOscillator/PluckArpeggio.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/PluckBass.xpf b/data/presets/TripleOscillator/PluckBass.xpf index 51c5de329a2..4d4052dfa84 100644 --- a/data/presets/TripleOscillator/PluckBass.xpf +++ b/data/presets/TripleOscillator/PluckBass.xpf @@ -15,31 +15,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/TripleOscillator/PowerStrings.xpf b/data/presets/TripleOscillator/PowerStrings.xpf index 0ef3750976e..b5ab04178e4 100644 --- a/data/presets/TripleOscillator/PowerStrings.xpf +++ b/data/presets/TripleOscillator/PowerStrings.xpf @@ -3,7 +3,7 @@ - + @@ -16,10 +16,6 @@ - - - - diff --git a/data/presets/TripleOscillator/RaveBass.xpf b/data/presets/TripleOscillator/RaveBass.xpf index 21fc6e90c06..2285dc370b7 100644 --- a/data/presets/TripleOscillator/RaveBass.xpf +++ b/data/presets/TripleOscillator/RaveBass.xpf @@ -5,10 +5,10 @@ - + - + diff --git a/data/presets/TripleOscillator/SawReso.xpf b/data/presets/TripleOscillator/SawReso.xpf index 2c631d66372..d9873e5f813 100644 --- a/data/presets/TripleOscillator/SawReso.xpf +++ b/data/presets/TripleOscillator/SawReso.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/SpaceBass.xpf b/data/presets/TripleOscillator/SpaceBass.xpf index aeb43286db7..5a13266fd6a 100644 --- a/data/presets/TripleOscillator/SpaceBass.xpf +++ b/data/presets/TripleOscillator/SpaceBass.xpf @@ -3,8 +3,8 @@ - - + + diff --git a/data/presets/TripleOscillator/Square.xpf b/data/presets/TripleOscillator/Square.xpf index cbd455e0f52..49859adbc81 100644 --- a/data/presets/TripleOscillator/Square.xpf +++ b/data/presets/TripleOscillator/Square.xpf @@ -3,12 +3,12 @@ - + - + - + diff --git a/data/presets/TripleOscillator/TINTNpad.xpf b/data/presets/TripleOscillator/TINTNpad.xpf index 6f04ca4f221..9c4e27be05e 100644 --- a/data/presets/TripleOscillator/TINTNpad.xpf +++ b/data/presets/TripleOscillator/TINTNpad.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/TripleOscillator/TheMaster.xpf b/data/presets/TripleOscillator/TheMaster.xpf index c54787a19f6..2de7125277e 100644 --- a/data/presets/TripleOscillator/TheMaster.xpf +++ b/data/presets/TripleOscillator/TheMaster.xpf @@ -3,9 +3,9 @@ - + - + @@ -15,17 +15,6 @@ - - - - - - - - - - - diff --git a/data/presets/TripleOscillator/TranceLead.xpf b/data/presets/TripleOscillator/TranceLead.xpf index cd1d3526a97..003739d39a7 100644 --- a/data/presets/TripleOscillator/TranceLead.xpf +++ b/data/presets/TripleOscillator/TranceLead.xpf @@ -1,18 +1,22 @@ - - + + - - - - - - - - - + + + + + + + + + + + - - - - + + + + + + diff --git a/data/presets/TripleOscillator/WarmStack.xpf b/data/presets/TripleOscillator/WarmStack.xpf index fb903295c4e..d3ab54bf0ca 100644 --- a/data/presets/TripleOscillator/WarmStack.xpf +++ b/data/presets/TripleOscillator/WarmStack.xpf @@ -5,7 +5,7 @@ - + diff --git a/data/presets/TripleOscillator/Whistle.xpf b/data/presets/TripleOscillator/Whistle.xpf index 3d03f5e56a3..545abd3781e 100644 --- a/data/presets/TripleOscillator/Whistle.xpf +++ b/data/presets/TripleOscillator/Whistle.xpf @@ -3,8 +3,8 @@ - - + + diff --git a/data/presets/Vibed/Harpsichord.xpf b/data/presets/Vibed/Harpsichord.xpf index b7c617c9bb9..2d22169bc4b 100644 --- a/data/presets/Vibed/Harpsichord.xpf +++ b/data/presets/Vibed/Harpsichord.xpf @@ -3,8 +3,8 @@ - - + + diff --git a/data/presets/Vibed/SadPad.xpf b/data/presets/Vibed/SadPad.xpf index 2ef17698bb7..b702d1896f9 100644 --- a/data/presets/Vibed/SadPad.xpf +++ b/data/presets/Vibed/SadPad.xpf @@ -3,8 +3,8 @@ - - + + diff --git a/data/presets/Xpressive/Ambition.xpf b/data/presets/Xpressive/Ambition.xpf index dd64489779c..fa3bc736d16 100644 --- a/data/presets/Xpressive/Ambition.xpf +++ b/data/presets/Xpressive/Ambition.xpf @@ -10,7 +10,7 @@ - + diff --git a/data/presets/Xpressive/Creature.xpf b/data/presets/Xpressive/Creature.xpf index bee39f224fb..8ae8a2794ff 100644 --- a/data/presets/Xpressive/Creature.xpf +++ b/data/presets/Xpressive/Creature.xpf @@ -10,7 +10,7 @@ - + diff --git a/data/presets/Xpressive/Low Battery.xpf b/data/presets/Xpressive/Low Battery.xpf index 78f1fc78f72..009c036cd74 100644 --- a/data/presets/Xpressive/Low Battery.xpf +++ b/data/presets/Xpressive/Low Battery.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Xpressive/Rubber Bass.xpf b/data/presets/Xpressive/Rubber Bass.xpf index 4b1409e224f..db4026c5ceb 100644 --- a/data/presets/Xpressive/Rubber Bass.xpf +++ b/data/presets/Xpressive/Rubber Bass.xpf @@ -10,7 +10,7 @@ - + diff --git a/data/presets/Xpressive/Untuned Bell.xpf b/data/presets/Xpressive/Untuned Bell.xpf index 5dd61ec18d1..53de2358bdb 100644 --- a/data/presets/Xpressive/Untuned Bell.xpf +++ b/data/presets/Xpressive/Untuned Bell.xpf @@ -10,7 +10,7 @@ - + @@ -19,4 +19,4 @@ - + \ No newline at end of file From acd0e4d4309e6bf997b71de9d80cd25914d29883 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Mon, 19 Oct 2020 22:18:21 +0200 Subject: [PATCH 114/180] Feature: Glue notes (#5721) Tool to glue selected notes together. Selected notes that are adjacent to each other or overlapping are transformed into one note stretching over the combined notes on/off times. Key command: + G Partially fixes: #746 which is part of #4877 Co-authored-by: Hyunjin Song Co-authored-by: IanCaio Co-authored-by: Spekular Co-authored-by: Kevin Zander Co-authored-by: Oskar Wallgren --- data/themes/default/glue.png | Bin 0 -> 407 bytes data/themes/default/tool.png | Bin 0 -> 466 bytes include/PianoRoll.h | 1 + src/gui/editors/PianoRoll.cpp | 83 ++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 data/themes/default/glue.png create mode 100644 data/themes/default/tool.png diff --git a/data/themes/default/glue.png b/data/themes/default/glue.png new file mode 100644 index 0000000000000000000000000000000000000000..de611b4bc34e7acdac92bd8117304971f08dc40f GIT binary patch literal 407 zcmV;I0cie-P)4!gYOmB-F?4z_kGV#ujlD`K2Jmsp@dy5^x`z4{W$X5 z8>aF=M{(nSj~MC&ox+pmouJqaI)|4QXuWEu=rX=iP^e)s)3LE05nB;a%CNi;5g%z_ z5!)F~S??7qX=nup9iV5JNJB?(nGyWPhAh*7)r{yZrc=R&yS;7$*06(0W;B7v@X!K% z#(YO$f)3+8K~9r9thbNBZqNZ-;S*~pV9D=qYd=P9(0-ib0+Wp!c8ivAh$_lmAx+S6 zY+^PS@DF^PJj_hq@OuIu=IQoD#9l002ovPDHLkV1k+} BqZR-F literal 0 HcmV?d00001 diff --git a/data/themes/default/tool.png b/data/themes/default/tool.png new file mode 100644 index 0000000000000000000000000000000000000000..6d818a53efc2acb81a295ebecaa6987068fa1ba2 GIT binary patch literal 466 zcmV;@0WJQCP)>W#1sZa@v}H1jwf^A%$)qyY}V#u?Y$S1 zvX@TCg+91}HrriWVCGnMZXNse`) z*OF{ER&mw;*}-^OyNRFp&=eiPvcuWJVrBif33?psHE4JCak^jhB%bwwzQb&i6n&sm zNj8(5sVT1{d6Lc}zDlxD6vg*`Iz5N?J-S;$^QqFA*S#9>H|{kCOLQIwHRvzgYKz{` znZ@pW!~vfC4`>z4;Wfs}D_O^Cd6L0`>-dbfc!=SF(F%SS3rFSs0pbo5)?qZQrvLx|07*qo IM6N<$f-z>s4gdfE literal 0 HcmV?d00001 diff --git a/include/PianoRoll.h b/include/PianoRoll.h index e3e4b312085..a876e03a013 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -208,6 +208,7 @@ protected slots: void selectRegionFromPixels( int xStart, int xEnd ); void clearGhostPattern(); + void glueNotes(); signals: diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 8f1a827a95e..27198ce8b19 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #ifndef __USE_XOPEN #define __USE_XOPEN @@ -644,6 +645,73 @@ void PianoRoll::clearGhostPattern() } +void PianoRoll::glueNotes() +{ + if (hasValidPattern()) + { + NoteVector selectedNotes = getSelectedNotes(); + if (selectedNotes.empty()) + { + TextFloat::displayMessage( tr( "Glue notes failed" ), + tr( "Please select notes to glue first." ), + embed::getIconPixmap( "glue", 24, 24 ), + 3000 ); + return; + } + + // Make undo possible + m_pattern->addJournalCheckPoint(); + + // Sort notes on key and then pos. + std::sort(selectedNotes.begin(), selectedNotes.end(), + [](const Note * note, const Note * compareNote) -> bool + { + if (note->key() == compareNote->key()) + { + return note->pos() < compareNote->pos(); + } + return note->key() < compareNote->key(); + }); + + QList noteToRemove; + + NoteVector::iterator note = selectedNotes.begin(); + auto nextNote = note+1; + NoteVector::iterator end = selectedNotes.end(); + + while (note != end && nextNote != end) + { + // key and position match for glue. The notes are already + // sorted so we don't need to test that nextNote is the same + // position or next in sequence. + if ((*note)->key() == (*nextNote)->key() + && (*nextNote)->pos() <= (*note)->pos() + + qMax(MidiTime(0), (*note)->length())) + { + (*note)->setLength(qMax((*note)->length(), + MidiTime((*nextNote)->endPos() - (*note)->pos()))); + noteToRemove.push_back(*nextNote); + ++nextNote; + } + // key or position doesn't match + else + { + note = nextNote; + nextNote = note+1; + } + } + + // Remove old notes + for (int i = 0; i < noteToRemove.count(); ++i) + { + m_pattern->removeNote(noteToRemove[i]); + } + + update(); + } +} + + void PianoRoll::loadMarkedSemiTones(const QDomElement & de) { // clear marked semitones to prevent leftover marks @@ -4361,6 +4429,21 @@ PianoRollWindow::PianoRollWindow() : DropToolBar *timeLineToolBar = addDropToolBarToTop( tr( "Timeline controls" ) ); m_editor->m_timeLine->addToolButtons( timeLineToolBar ); + // -- Note modifier tools + // Toolbar + QToolButton * noteToolsButton = new QToolButton(m_toolBar); + noteToolsButton->setIcon(embed::getIconPixmap("tool")); + noteToolsButton->setPopupMode(QToolButton::InstantPopup); + + // Glue + QAction * glueAction = new QAction(embed::getIconPixmap("glue"), + tr("Glue"), noteToolsButton); + connect(glueAction, SIGNAL(triggered()), m_editor, SLOT(glueNotes())); + glueAction->setShortcut( Qt::SHIFT | Qt::Key_G ); + + noteToolsButton->addAction(glueAction); + + notesActionsToolBar->addWidget(noteToolsButton); addToolBarBreak(); From 8b2902c27a03d5678b9571ae10977b75987d44ce Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 2 Jun 2019 19:23:31 +0200 Subject: [PATCH 115/180] Enable Lv2 Atom ports All Atom ports are now being created and connected. Currently only MIDI input ports are supplied with data. Major contribution to #562 ("lv2 support"). --- include/Lv2ControlBase.h | 9 +- include/Lv2Evbuf.h | 149 +++++++++++++++ include/Lv2Ports.h | 23 ++- include/Lv2Proc.h | 31 ++- include/Lv2UridCache.h | 2 +- plugins/Lv2Effect/Lv2Effect.cpp | 1 + plugins/Lv2Instrument/Lv2Instrument.cpp | 9 +- plugins/Lv2Instrument/Lv2Instrument.h | 2 +- src/core/CMakeLists.txt | 1 + src/core/lv2/Lv2ControlBase.cpp | 27 +++ src/core/lv2/Lv2Evbuf.cpp | 193 +++++++++++++++++++ src/core/lv2/Lv2Ports.cpp | 27 ++- src/core/lv2/Lv2Proc.cpp | 240 ++++++++++++++++++++++-- 13 files changed, 684 insertions(+), 30 deletions(-) create mode 100644 include/Lv2Evbuf.h create mode 100644 src/core/lv2/Lv2Evbuf.cpp diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index d6591a50d7b..d5d83f37800 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -100,8 +100,12 @@ class Lv2ControlBase : public LinkedModelGroups /* utils for the run thread */ - //! Copy values from all connected models into the respective ports + //! Copy values from the LMMS core (connected models, MIDI events, ...) into + //! the respective ports void copyModelsFromLmms(); + //! Bring values from all ports to the LMMS core + void copyModelsToLmms() const; + //! Copy buffer passed by LMMS into our ports void copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames); //! Copy our ports into buffers passed by LMMS @@ -123,6 +127,9 @@ class Lv2ControlBase : public LinkedModelGroups */ std::size_t controlCount() const; QString nodeName() const { return "lv2controls"; } + bool hasNoteInput() const; + void handleMidiInputEvent(const class MidiEvent &event, + const class MidiTime &time, f_cnt_t offset); private: //! Return the DataFile settings type diff --git a/include/Lv2Evbuf.h b/include/Lv2Evbuf.h new file mode 100644 index 00000000000..9cf6a6801fe --- /dev/null +++ b/include/Lv2Evbuf.h @@ -0,0 +1,149 @@ +/* + * lv2_evbuf.h - Lv2 event buffer definitions + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +/* + * The original code was written by David Robillard + * Original version: 6f22ee0 from https://github.com/drobilla/jalv.git + * Minor changes have been done, but no functional changes. + * Considering this as an "external library", the identifiers do not need to + * match the LMMS coding conventions. + */ + +#ifndef LV2_EVBUF_H +#define LV2_EVBUF_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +/** + An abstract/opaque LV2 event buffer. +*/ +typedef struct LV2_Evbuf_Impl LV2_Evbuf; + +/** + An iterator over an LV2_Evbuf. +*/ +typedef struct { + LV2_Evbuf* evbuf; + uint32_t offset; +} LV2_Evbuf_Iterator; + +/** + Allocate a new, empty event buffer. + URIDs for atom:Chunk and atom:Sequence must be passed for LV2_EVBUF_ATOM. +*/ +LV2_Evbuf* +lv2_evbuf_new(uint32_t capacity, uint32_t atom_Chunk, uint32_t atom_Sequence); + +/** + Free an event buffer allocated with lv2_evbuf_new. +*/ +void +lv2_evbuf_free(LV2_Evbuf* evbuf); + +/** + Clear and initialize an existing event buffer. + The contents of buf are ignored entirely and overwritten, except capacity + which is unmodified. + If input is false and this is an atom buffer, the buffer will be prepared + for writing by the plugin. This MUST be called before every run cycle. +*/ +void +lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input); + +/** + Return the total padded size of the events stored in the buffer. +*/ +uint32_t +lv2_evbuf_get_size(LV2_Evbuf* evbuf); + +/** + Return the actual buffer implementation. + The format of the buffer returned depends on the buffer type. +*/ +void* +lv2_evbuf_get_buffer(LV2_Evbuf* evbuf); + +/** + Return an iterator to the start of `evbuf`. +*/ +LV2_Evbuf_Iterator +lv2_evbuf_begin(LV2_Evbuf* evbuf); + +/** + Return an iterator to the end of `evbuf`. +*/ +LV2_Evbuf_Iterator +lv2_evbuf_end(LV2_Evbuf* evbuf); + +/** + Check if `iter` is valid. + @return True if `iter` is valid, otherwise false (past end of buffer) +*/ +bool +lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter); + +/** + Advance `iter` forward one event. + `iter` must be valid. + @return True if `iter` is valid, otherwise false (reached end of buffer) +*/ +LV2_Evbuf_Iterator +lv2_evbuf_next(LV2_Evbuf_Iterator iter); + +/** + Dereference an event iterator (i.e. get the event currently pointed to). + `iter` must be valid. + `type` Set to the type of the event. + `size` Set to the size of the event. + `data` Set to the contents of the event. + @return True on success. +*/ +bool +lv2_evbuf_get( LV2_Evbuf_Iterator iter, + uint32_t* frames, + uint32_t* type, + uint32_t* size, + uint8_t** data); + +/** + Write an event at `iter`. + The event (if any) pointed to by `iter` will be overwritten, and `iter` + incremented to point to the following event (i.e. several calls to this + function can be done in sequence without twiddling iter in-between). + @return True if event was written, otherwise false (buffer is full). +*/ +bool +lv2_evbuf_write( LV2_Evbuf_Iterator* iter, + uint32_t frames, + uint32_t type, + uint32_t size, + const uint8_t* data); + +#endif // LMMS_HAVE_LV2 + +#endif // LV2_EVBUF_H diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 567e1ddb585..8241b3e8af4 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -37,6 +37,7 @@ #include "PluginIssue.h" struct ConnectPortVisitor; +typedef struct LV2_Evbuf_Impl LV2_Evbuf; namespace Lv2Ports { @@ -53,7 +54,7 @@ enum class Type { Unknown, Control, Audio, - Event, //!< TODO: unused, describe + AtomSeq, Cv //!< TODO: unused, describe }; @@ -74,6 +75,7 @@ struct ControlPortBase; struct Control; struct Audio; struct Cv; +struct AtomSeq; struct Unknown; struct ConstVisitor @@ -82,6 +84,7 @@ struct ConstVisitor virtual void visit(const Lv2Ports::Control& ) {} virtual void visit(const Lv2Ports::Audio& ) {} virtual void visit(const Lv2Ports::Cv& ) {} + virtual void visit(const Lv2Ports::AtomSeq& ) {} virtual void visit(const Lv2Ports::Unknown& ) {} virtual ~ConstVisitor(); @@ -93,6 +96,7 @@ struct Visitor virtual void visit(Lv2Ports::Control& ) {} virtual void visit(Lv2Ports::Audio& ) {} virtual void visit(Lv2Ports::Cv& ) {} + virtual void visit(Lv2Ports::AtomSeq& ) {} virtual void visit(Lv2Ports::Unknown& ) {} virtual ~Visitor(); @@ -192,6 +196,23 @@ struct Audio : public VisitablePort friend struct ::ConnectPortVisitor; }; +struct AtomSeq : public VisitablePort +{ + enum FlagType + { + None = 0, + Midi = 1 + }; + unsigned flags = FlagType::None; + + struct Lv2EvbufDeleter + { + void operator()(LV2_Evbuf* n); + }; + using AutoLv2Evbuf = std::unique_ptr; + AutoLv2Evbuf m_buf; +}; + struct Unknown : public VisitablePort { }; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 1c1cc11d8ac..81e25f785f5 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -36,15 +36,18 @@ #include "Lv2Basics.h" #include "Lv2Features.h" #include "LinkedModelGroups.h" +#include "MidiEvent.h" +#include "MidiTime.h" #include "Plugin.h" #include "PluginIssue.h" - +#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" // forward declare port structs/enums namespace Lv2Ports { struct Audio; struct PortBase; + struct AtomSeq; enum class Type; enum class Flow; @@ -106,8 +109,11 @@ class Lv2Proc : public LinkedModelGroup /* utils for the run thread */ - //! Copy values from all connected models into the respective ports + //! Copy values from the LMMS core (connected models, MIDI events, ...) into + //! the respective ports void copyModelsFromCore(); + //! Bring values from all ports to the LMMS core + void copyModelsToCore(); /** * Copy buffer passed by the core into our ports * @param buf buffer of sample frames, each sample frame is something like @@ -137,11 +143,15 @@ class Lv2Proc : public LinkedModelGroup //! Run the Lv2 plugin instance for @param frames frames void run(fpp_t frames); + void handleMidiInputEvent(const class MidiEvent &event, + const MidiTime &time, f_cnt_t offset); + /* misc */ class AutomatableModel *modelAtPort(const QString &uri); // unused currently std::size_t controlCount() const { return LinkedModelGroup::modelNum(); } + bool hasNoteInput() const; protected: /* @@ -159,8 +169,25 @@ class Lv2Proc : public LinkedModelGroup LilvInstance* m_instance; Lv2Features m_features; + // full list of ports std::vector> m_ports; + // quick reference to specific, unique ports StereoPortRef m_inPorts, m_outPorts; + Lv2Ports::AtomSeq *m_midiIn = nullptr, *m_midiOut = nullptr; + + // MIDI + // many things here may be moved into the `Instrument` class + constexpr const static std::size_t m_maxMidiInputEvents = 1024; + //! spinlock for the MIDI ringbuffer (for MIDI events going to the plugin) + std::atomic_flag m_ringLock = ATOMIC_FLAG_INIT; + + //! MIDI ringbuffer (for MIDI events going to the plugin) + ringbuffer_t m_midiInputBuf; + //! MIDI ringbuffer reader + ringbuffer_reader_t m_midiInputReader; + + // other + static std::size_t minimumEvbufSize() { return 1 << 15; /* ardour uses this*/ } //! models for the controls, sorted by port symbols std::map m_connectedModels; diff --git a/include/Lv2UridCache.h b/include/Lv2UridCache.h index b4cfa59f33d..81dd1346cf7 100644 --- a/include/Lv2UridCache.h +++ b/include/Lv2UridCache.h @@ -37,7 +37,7 @@ class Lv2UridCache public: enum class Id //!< ID for m_uridCache array { - midi_MidiEvent, //!< just an example, unused yet + midi_MidiEvent, size }; //! Return URID for a cache ID diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 36e9df46ba7..4f84104bbe9 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -74,6 +74,7 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) m_controls.run(frames); // m_pluginMutex.unlock(); + m_controls.copyModelsToLmms(); m_controls.copyBuffersToLmms(m_tmpOutputSmps.data(), frames); double outSum = .0; diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 76d9d017f9d..ca918d2ed04 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -131,11 +131,9 @@ void Lv2Instrument::loadFile(const QString &file) bool Lv2Instrument::handleMidiEvent( const MidiEvent &event, const MidiTime &time, f_cnt_t offset) { - // this function can be called from GUI threads while the plugin is running, - // so this requires caching, e.g. in ringbuffers - (void)time; - (void)offset; - (void)event; + // this function can be called from GUI threads while the plugin is running + // handleMidiInputEvent will use a thread-safe ringbuffer + handleMidiInputEvent(event, time, offset); return true; } #endif @@ -161,6 +159,7 @@ void Lv2Instrument::play(sampleFrame *buf) run(fpp); + copyModelsToLmms(); copyBuffersToLmms(buf, fpp); instrumentTrack()->processAudioBuffer(buf, fpp, nullptr); diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 4fb2e1e34c3..a35830952f9 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -63,7 +63,7 @@ class Lv2Instrument : public Instrument, public Lv2ControlBase /* realtime funcs */ - bool hasNoteInput() const override { return false; /* not supported yet */ } + bool hasNoteInput() const override { return Lv2ControlBase::hasNoteInput(); } #ifdef LV2_INSTRUMENT_USE_MIDI bool handleMidiEvent(const MidiEvent &event, const MidiTime &time = MidiTime(), f_cnt_t offset = 0) override; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8dbee31d31b..22bb1195103 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -93,6 +93,7 @@ set(LMMS_SRCS core/lv2/Lv2Basics.cpp core/lv2/Lv2ControlBase.cpp + core/lv2/Lv2Evbuf.cpp core/lv2/Lv2Features.cpp core/lv2/Lv2Ports.cpp core/lv2/Lv2Proc.cpp diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index cc986dafed0..00326eeb88c 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -26,6 +26,7 @@ #ifdef LMMS_HAVE_LV2 +#include #include #include "Engine.h" @@ -113,6 +114,14 @@ void Lv2ControlBase::copyModelsFromLmms() { +void Lv2ControlBase::copyModelsToLmms() const +{ + for (auto& c : m_procs) { c->copyModelsToCore(); } +} + + + + void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { unsigned firstChan = 0; // tell the procs which channels they shall read from for (auto& c : m_procs) { @@ -187,4 +196,22 @@ std::size_t Lv2ControlBase::controlCount() const { +bool Lv2ControlBase::hasNoteInput() const +{ + return std::any_of(m_procs.begin(), m_procs.end(), + [](const auto& c) { return c->hasNoteInput(); }); +} + + + + +void Lv2ControlBase::handleMidiInputEvent(const MidiEvent &event, + const MidiTime &time, f_cnt_t offset) +{ + for (auto& c : m_procs) { c->handleMidiInputEvent(event, time, offset); } +} + + + + #endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Evbuf.cpp b/src/core/lv2/Lv2Evbuf.cpp new file mode 100644 index 00000000000..335dfd36224 --- /dev/null +++ b/src/core/lv2/Lv2Evbuf.cpp @@ -0,0 +1,193 @@ +/* + * lv2_evbuf.cpp - Lv2 event buffer implementation + * + * Copyright (c) 2019-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +/* + * The original code was written by David Robillard + */ + +#include "Lv2Evbuf.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include + +#include + +struct LV2_Evbuf_Impl { + uint32_t capacity; + uint32_t atom_Chunk; + uint32_t atom_Sequence; + LV2_Atom_Sequence buf; +}; + +static inline uint32_t +lv2_evbuf_pad_size(uint32_t size) +{ + return (size + 7) & (~7); +} + +LV2_Evbuf* +lv2_evbuf_new(uint32_t capacity, uint32_t atom_Chunk, uint32_t atom_Sequence) +{ + // FIXME: memory must be 64-bit aligned + LV2_Evbuf* evbuf = (LV2_Evbuf*)malloc( + sizeof(LV2_Evbuf) + sizeof(LV2_Atom_Sequence) + capacity); + evbuf->capacity = capacity; + evbuf->atom_Chunk = atom_Chunk; + evbuf->atom_Sequence = atom_Sequence; + lv2_evbuf_reset(evbuf, true); + return evbuf; +} + +void +lv2_evbuf_free(LV2_Evbuf* evbuf) +{ + free(evbuf); +} + +void +lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input) +{ + if (input) { + evbuf->buf.atom.size = sizeof(LV2_Atom_Sequence_Body); + evbuf->buf.atom.type = evbuf->atom_Sequence; + } else { + evbuf->buf.atom.size = evbuf->capacity; + evbuf->buf.atom.type = evbuf->atom_Chunk; + } +} + +uint32_t +lv2_evbuf_get_size(LV2_Evbuf* evbuf) +{ + assert(evbuf->buf.atom.type != evbuf->atom_Sequence + || evbuf->buf.atom.size >= sizeof(LV2_Atom_Sequence_Body)); + return evbuf->buf.atom.type == evbuf->atom_Sequence + ? evbuf->buf.atom.size - sizeof(LV2_Atom_Sequence_Body) + : 0; +} + +void* +lv2_evbuf_get_buffer(LV2_Evbuf* evbuf) +{ + return &evbuf->buf; +} + +LV2_Evbuf_Iterator +lv2_evbuf_begin(LV2_Evbuf* evbuf) +{ + LV2_Evbuf_Iterator iter = { evbuf, 0 }; + return iter; +} + +LV2_Evbuf_Iterator +lv2_evbuf_end(LV2_Evbuf* evbuf) +{ + const uint32_t size = lv2_evbuf_get_size(evbuf); + const LV2_Evbuf_Iterator iter = { evbuf, lv2_evbuf_pad_size(size) }; + return iter; +} + +bool +lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter) +{ + return iter.offset < lv2_evbuf_get_size(iter.evbuf); +} + +LV2_Evbuf_Iterator +lv2_evbuf_next(LV2_Evbuf_Iterator iter) +{ + if (!lv2_evbuf_is_valid(iter)) { + return iter; + } + + LV2_Evbuf* evbuf = iter.evbuf; + uint32_t offset = iter.offset; + uint32_t size; + size = ((LV2_Atom_Event*) + ((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, &evbuf->buf.atom) + + offset))->body.size; + offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size); + + LV2_Evbuf_Iterator next = { evbuf, offset }; + return next; +} + +bool +lv2_evbuf_get(LV2_Evbuf_Iterator iter, + uint32_t* frames, + uint32_t* type, + uint32_t* size, + uint8_t** data) +{ + *frames = *type = *size = 0; + *data = NULL; + + if (!lv2_evbuf_is_valid(iter)) { + return false; + } + + LV2_Atom_Sequence* aseq = &iter.evbuf->buf; + LV2_Atom_Event* aev = (LV2_Atom_Event*)( + (char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter.offset); + + *frames = aev->time.frames; + *type = aev->body.type; + *size = aev->body.size; + *data = (uint8_t*)LV2_ATOM_BODY(&aev->body); + + return true; +} + +bool +lv2_evbuf_write(LV2_Evbuf_Iterator* iter, + uint32_t frames, + uint32_t type, + uint32_t size, + const uint8_t* data) +{ + LV2_Atom_Sequence* aseq = &iter->evbuf->buf; + if (iter->evbuf->capacity - sizeof(LV2_Atom) - aseq->atom.size < + sizeof(LV2_Atom_Event) + size) { + return false; + } + + LV2_Atom_Event* aev = (LV2_Atom_Event*)( + (char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter->offset); + + aev->time.frames = frames; + aev->body.type = type; + aev->body.size = size; + memcpy(LV2_ATOM_BODY(&aev->body), data, size); + + size = lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size); + aseq->atom.size += size; + iter->offset += size; + + return true; +} + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index f2fac5744d0..cc8ecf6ca43 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -27,9 +27,12 @@ #ifdef LMMS_HAVE_LV2 +#include + #include "Engine.h" #include "Lv2Basics.h" #include "Lv2Manager.h" +#include "Lv2Evbuf.h" namespace Lv2Ports { @@ -57,7 +60,7 @@ const char *toStr(Type pt) case Type::Unknown: return "unknown"; case Type::Control: return "control"; case Type::Audio: return "audio"; - case Type::Event: return "event"; + case Type::AtomSeq: return "atom-sequence"; case Type::Cv: return "cv"; } return ""; @@ -168,6 +171,23 @@ std::vector Meta::get(const LilvPlugin *plugin, issue(badPortType, "cvPort"); m_type = Type::Cv; } + else if (isA(LV2_ATOM__AtomPort)) + { + AutoLilvNode uriAtomSequence(Engine::getLv2Manager()->uri(LV2_ATOM__Sequence)); + AutoLilvNode uriAtomBufferType(Engine::getLv2Manager()->uri(LV2_ATOM__bufferType)); + AutoLilvNodes bufferTypes(lilv_port_get_value(plugin, lilvPort, uriAtomBufferType.get())); + + if (lilv_nodes_contains(bufferTypes.get(), uriAtomSequence.get())) + { + // we accept all kinds of atom sequence ports, even if they take or + // offer atom types that we do not support: + // * atom input ports only say what *can* be input, but not what is + // required as input + // * atom output ports only say what *can* be output, but not what must + // be evaluated + m_type = Type::AtomSeq; + } + } if(m_type == Type::Unknown) { @@ -245,6 +265,11 @@ void Audio::copyBuffersToCore(sampleFrame *lmmsBuf, +void AtomSeq::Lv2EvbufDeleter::operator()(LV2_Evbuf *n) { lv2_evbuf_free(n); } + + + + // make the compiler happy, give each class with virtuals // a function (the destructor here) which is in a cpp file PortBase::~PortBase() {} diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 26593428b2c..0922ac242ed 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -27,6 +27,11 @@ #ifdef LMMS_HAVE_LV2 #include +#include +#include +#include +#include +#include #include "AutomatableModel.h" #include "ComboBoxModel.h" @@ -34,17 +39,31 @@ #include "Lv2Features.h" #include "Lv2Manager.h" #include "Lv2Ports.h" +#include "Lv2Evbuf.h" +#include "MidiEventToByteSeq.h" #include "Mixer.h" +// container for everything required to store MIDI events going to the plugin +struct MidiInputEvent +{ + MidiEvent ev; + MidiTime time; + f_cnt_t offset; +}; + + + + Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, std::vector& issues, bool printIssues) { unsigned maxPorts = lilv_plugin_get_num_ports(plugin); enum { inCount, outCount, maxCount }; - unsigned audioChannels[maxCount] = { 0, 0 }; // input and output count + unsigned audioChannels[maxCount] = { 0, 0 }; // audio input and output count + unsigned midiChannels[maxCount] = { 0, 0 }; // MIDI input and output count for (unsigned portNum = 0; portNum < maxPorts; ++portNum) { @@ -58,8 +77,15 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, lilv_plugin_get_port_by_index(plugin, portNum)) && !meta.m_optional; if (meta.m_type == Lv2Ports::Type::Audio && portMustBeUsed) + { ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output ? outCount : inCount]; + } + else if(meta.m_type == Lv2Ports::Type::AtomSeq && portMustBeUsed) + { + ++midiChannels[meta.m_flow == Lv2Ports::Flow::Output + ? outCount : inCount]; + } } if (audioChannels[inCount] > 2) @@ -71,6 +97,13 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, issues.emplace_back(tooManyOutputChannels, std::to_string(audioChannels[outCount])); + if (midiChannels[inCount] > 1) + issues.emplace_back(tooManyMidiInputChannels, + std::to_string(midiChannels[inCount])); + if (midiChannels[outCount] > 1) + issues.emplace_back(tooManyMidiOutputChannels, + std::to_string(midiChannels[outCount])); + AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); LILV_FOREACH (nodes, itr, reqFeats.get()) { @@ -104,7 +137,9 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : LinkedModelGroup(parent), - m_plugin(plugin) + m_plugin(plugin), + m_midiInputBuf(m_maxMidiInputEvents), + m_midiInputReader(m_midiInputBuf) { initPlugin(); } @@ -149,29 +184,79 @@ void Lv2Proc::copyModelsFromCore() { void visit(Lv2Ports::Control& ctrl) override { - if (ctrl.m_flow == Lv2Ports::Flow::Input) - { - FloatFromModelVisitor ffm; - ffm.m_scalePointMap = &ctrl.m_scalePointMap; - ctrl.m_connectedModel->accept(ffm); - ctrl.m_val = ffm.m_res; - } + FloatFromModelVisitor ffm; + ffm.m_scalePointMap = &ctrl.m_scalePointMap; + ctrl.m_connectedModel->accept(ffm); + ctrl.m_val = ffm.m_res; } void visit(Lv2Ports::Cv& cv) override { - if (cv.m_flow == Lv2Ports::Flow::Input) + FloatFromModelVisitor ffm; + ffm.m_scalePointMap = &cv.m_scalePointMap; + cv.m_connectedModel->accept(ffm); + // dirty fix, needs better interpolation + std::fill(cv.m_buffer.begin(), cv.m_buffer.end(), ffm.m_res); + } + void visit(Lv2Ports::AtomSeq& atomPort) override + { + lv2_evbuf_reset(atomPort.m_buf.get(), true); + } + } copy; + + // feed each input port with the respective data from the LMMS core + for (const std::unique_ptr& port : m_ports) + { + if (port->m_flow == Lv2Ports::Flow::Input) + { + port->accept(copy); + } + } + + // send pending MIDI events to atom port + if(m_midiIn) + { + LV2_Evbuf_Iterator iter = lv2_evbuf_begin(m_midiIn->m_buf.get()); + // MIDI events waiting to go to the plugin? + while(m_midiInputReader.read_space() > 0) + { + const MidiInputEvent ev = m_midiInputReader.read(1)[0]; + uint32_t atomStamp = + ev.time.frames(Engine::framesPerTick()) + ev.offset; + uint32_t type = Engine::getLv2Manager()-> + uridCache()[Lv2UridCache::Id::midi_MidiEvent]; + uint8_t buf[4]; + std::size_t bufsize = writeToByteSeq(ev.ev, buf, sizeof(buf)); + if(bufsize) { - FloatFromModelVisitor ffm; - ffm.m_scalePointMap = &cv.m_scalePointMap; - cv.m_connectedModel->accept(ffm); - // dirty fix, needs better interpolation - std::fill(cv.m_buffer.begin(), cv.m_buffer.end(), ffm.m_res); + lv2_evbuf_write(&iter, atomStamp, type, bufsize, buf); } } + } +} + + + + +void Lv2Proc::copyModelsToCore() +{ + struct Copy : public Lv2Ports::Visitor + { + void visit(Lv2Ports::AtomSeq& atomPort) override + { + // we currently don't copy anything, but we need to clear the buffer + // for the plugin to write again + lv2_evbuf_reset(atomPort.m_buf.get(), false); + } } copy; - for (const std::unique_ptr& port : m_ports) { - port->accept(copy); } + // fetch data from each output port and bring it to the LMMS core + for (const std::unique_ptr& port : m_ports) + { + if (port->m_flow == Lv2Ports::Flow::Output) + { + port->accept(copy); + } + } } @@ -229,6 +314,41 @@ void Lv2Proc::run(fpp_t frames) +// in case there will be a PR which removes this callback and instead adds a +// `ringbuffer_t` to `class Instrument`, this +// function (and the ringbuffer and its reader in `Lv2Proc`) will simply vanish +void Lv2Proc::handleMidiInputEvent(const MidiEvent &event, const MidiTime &time, f_cnt_t offset) +{ + if(m_midiIn) + { + // ringbuffer allows only one writer at a time + // however, this function can be called by multiple threads + // (different RT and non-RT!) at the same time + // for now, a spinlock looks like the most safe/easy compromise + + // source: https://en.cppreference.com/w/cpp/atomic/atomic_flag + while (m_ringLock.test_and_set(std::memory_order_acquire)) // acquire lock + ; // spin + + MidiInputEvent ev { event, time, offset }; + std::size_t written = m_midiInputBuf.write(&ev, 1); + if(written != 1) + { + qWarning("MIDI ringbuffer is too small! Discarding MIDI event."); + } + + m_ringLock.clear(std::memory_order_release); + } + else + { + qWarning() << "Warning: Caught MIDI event for an Lv2 instrument" + << "that can not hande MIDI... Ignoring"; + } +} + + + + AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) { // unused currently @@ -284,6 +404,18 @@ void Lv2Proc::shutdownPlugin() +bool Lv2Proc::hasNoteInput() const +{ + return m_midiIn; + // we could additionally check for + // http://lv2plug.in/ns/lv2core#InstrumentPlugin + // however, jalv does not do that, too + // so, if there's any MIDI input, we just assume we can send notes there +} + + + + void Lv2Proc::initPluginSpecificFeatures() { // nothing yet @@ -385,6 +517,46 @@ void Lv2Proc::createPort(std::size_t portNum) port = audio; break; } + case Lv2Ports::Type::AtomSeq: + { + Lv2Ports::AtomSeq* atomPort = new Lv2Ports::AtomSeq; + + { + AutoLilvNode uriAtomSupports(Engine::getLv2Manager()->uri(LV2_ATOM__supports)); + AutoLilvNodes atomSupports(lilv_port_get_value(m_plugin, lilvPort, uriAtomSupports.get())); + AutoLilvNode uriMidiEvent(Engine::getLv2Manager()->uri(LV2_MIDI__MidiEvent)); + + LILV_FOREACH (nodes, itr, atomSupports.get()) + { + if(lilv_node_equals(lilv_nodes_get(atomSupports.get(), itr), uriMidiEvent.get())) + { + atomPort->flags |= Lv2Ports::AtomSeq::FlagType::Midi; + } + } + } + + int minimumSize = minimumEvbufSize(); + + Lv2Manager* mgr = Engine::getLv2Manager(); + + // check for alternative minimum size + { + AutoLilvNode rszMinimumSize = mgr->uri(LV2_RESIZE_PORT__minimumSize); + AutoLilvNodes minSizeV(lilv_port_get_value(m_plugin, lilvPort, rszMinimumSize.get())); + LilvNode* minSize = minSizeV ? lilv_nodes_get_first(minSizeV.get()) : nullptr; + if (minSize && lilv_node_is_int(minSize)) { + minimumSize = std::max(minimumSize, lilv_node_as_int(minSize)); + } + } + + atomPort->m_buf.reset( + lv2_evbuf_new(static_cast(minimumSize), + mgr->uridMap().map(LV2_ATOM__Chunk), + mgr->uridMap().map(LV2_ATOM__Sequence))); + + port = atomPort; + break; + } default: port = new Lv2Ports::Unknown; } @@ -445,6 +617,33 @@ void Lv2Proc::createPorts() else if (!portRef->m_right) { portRef->m_right = &audio; } } } + + void visit(Lv2Ports::AtomSeq& atomPort) override + { + if(atomPort.m_flow == Lv2Ports::Flow::Input) + { + if(atomPort.flags & Lv2Ports::AtomSeq::FlagType::Midi) + { + // take any MIDI input, prefer mandatory MIDI input + // (Lv2Proc::check() assures there are <=1 mandatory MIDI + // input ports) + if(!m_proc->m_midiIn || !atomPort.m_optional) + m_proc->m_midiIn = &atomPort; + } + } + else if(atomPort.m_flow == Lv2Ports::Flow::Output) + { + if(atomPort.flags & Lv2Ports::AtomSeq::FlagType::Midi) + { + // take any MIDI output, prefer mandatory MIDI output + // (Lv2Proc::check() assures there are <=1 mandatory MIDI + // output ports) + if(!m_proc->m_midiOut || !atomPort.m_optional) + m_proc->m_midiOut = &atomPort; + } + } + else { Q_ASSERT(false); } + } }; std::size_t maxPorts = lilv_plugin_get_num_ports(m_plugin); @@ -472,10 +671,15 @@ struct ConnectPortVisitor : public Lv2Ports::Visitor { std::size_t m_num; LilvInstance* m_instance; - void connectPort(void* location) { + void connectPort(void* location) + { lilv_instance_connect_port(m_instance, static_cast(m_num), location); } + void visit(Lv2Ports::AtomSeq& atomSeq) override + { + connectPort(lv2_evbuf_get_buffer(atomSeq.m_buf.get())); + } void visit(Lv2Ports::Control& ctrl) override { connectPort(&ctrl.m_val); } void visit(Lv2Ports::Audio& audio) override { From f5d0524b16c3bfef0ad7bc4604a16b7590bcf295 Mon Sep 17 00:00:00 2001 From: Kumar Date: Tue, 20 Oct 2020 22:56:22 +0530 Subject: [PATCH 116/180] Enable track-wide color coding (#5573) * Enable track-wide color coding * Add support for automation tracks * Allow saving & loading track colors * Allow track color to be reset to default * Partially migrate common settings to Track.cpp, fix bug * Completely migrate local TCO color functions to TCO class, fix bug * Set QColorDialog colors to better colors * Color the side of the track according to TCO colors * Disable color gradient when muted * Change selection color to depend on TCO color * Fix breaking builds * Bug fix * Fix another bug where BB track colors wouldn't load * Restore changed demo to original state * Fix BB Editor bug * Fix breaking builds * Allow random color picking * Fix copy-paste bug * Change how color is painted on a track * Cleanup, and implement per-pattern colors * Cleanup comments * Migrate some functions * Remove redundant function * Rename some functions * Migrate duplicates to superclass * Use ColorChooser and reorder some includes * Change how colors are saved * Fix some formatting * Fix some code * Change how clip colors work * Fix some unexpected behaviors * Fix note border coloring being green on colored tracks * Change name of an option * Remove redundant code * Fix ghost changes * Remove colorRgb * Rename backgroundColor, remove some variables we don't use * Remove a redundant variable * Migrate some duplicates to superclass * Check for nullpointer * Remove redundant variable * Update some logic * Change how muted colors are displayed * Change how dark muted tracks become * Place setModified() in appropriate places * Make getColorForDisplay() function * Change how colors are organised and saved * Remove m_useStyleColor * Remove a comment * Quick changes * Change how colors are saved * Remove redundant stuff * Remove redundant stuff pt. 2 * Change how colors are copied * Fixes pt. 3 * Fixes pt. 4 * Change spaces to tabs * Fix pseudochanges * Remove s_lastTCOColor * Fix pseudochanges pt. 2 * Fix breaking builds * Add files via upload * Add comments (again) --- ...- Krem Kaakkuja (Second Flight Remix).mmpz | Bin 46081 -> 46520 bytes include/AutomationPatternView.h | 5 +- include/BBTrack.h | 52 ----- include/Track.h | 53 ++++- src/core/AutomationPattern.cpp | 11 + src/core/Track.cpp | 205 +++++++++++++++++- src/gui/AutomationPatternView.cpp | 8 +- src/tracks/BBTrack.cpp | 128 ++--------- src/tracks/Pattern.cpp | 33 ++- src/tracks/SampleTrack.cpp | 34 ++- 10 files changed, 337 insertions(+), 192 deletions(-) diff --git a/data/projects/demos/Greippi - Krem Kaakkuja (Second Flight Remix).mmpz b/data/projects/demos/Greippi - Krem Kaakkuja (Second Flight Remix).mmpz index 9ea29b34d3f1aaaf6fe86afe1e0bf10a162abe9a..d08ca6e04c176c2516b85769f6f8bf4f3f0c96fb 100644 GIT binary patch literal 46520 zcmbSyWmFx@wk}R^cXxNPaCi6M?(R--*93QG;qI;>xVr{-2<~vnK6{^g-o5YrdE*~ljCqE=jHz}w zGhRCFmGL8Hn1PJI5*!8oS5OZsp$0+a8A-OY_4IIZ+{M9aycqGF`S;hRPZI9F4KQCu zFZ902oc(xzJ$U!JeeZnT8@JD??j{O}`mt|BQ2BO6#p13175@j4^?N&3#=O&8etgii z5qpFWlb%?T&QX!CVf*=L>-j?$&-J+F_v3b~3jv>po9&!;Yj!W!6Yp6ieEJtT=6B~~ zCiN(TG$4y!wA}(+a+17$H0B27U4bjIf?c*d-*pd%atfcWw2_7 z_g>$@?;MY;nheHF8c!%PZwwj`e$TVyquK(gn}eI3H8%HRm3f=-9Yh-06~P9YB&~3O zUb#am{qrEpvE%)viKvE#j*e*)jH{>Dok14L!Qj2x7`CSd^gH7thNU>B-rXp1M)hmY zHnNWJj%q6>Ul>^Gy$lTB{`8JTTm}bLH zf_ms#GX|+A*+r&Xtb((_YFCa@bPYPz@}}l2sB%r@r)U|3uJoLa-_>OMW==5U5<>Ozj6|!7!8?! z?=}=*95*w6B*BZG_Bsx;gae!$+Tpm>g1f@W*Q3ztRm#_YjPQCPVyej32=1Sn8;G~Fq5#cjt(t5u3 z{I=Wct3XI;xZ-*mAQKD^yEMBmQ!~*m8&r~BvT^h{YUb`a-FDZ>XtA`~ilshMzOQbL z{f=n^c)ZAuxD90pw5-t+yR^Xcxh^iZ?bsM}_iagsU`gP?3f>{|{@i+-JZhsuOQV8) zPCs_q=>FQovqhZSZl$tS+E>%;%@s7{GepoVRhIzl0$*;0g+HkVLan~C{I-YCUd zuGYRG&6tQC6;t-w|K$4l+MErjYl91}C$=Qorp8~%#|V}jNKes9|N49OVs|npmHts> z_C^L!*|PR~u>#(RMYF|7fZhn(b$XD;X+Ttn7R^~x>ky3$5VZ802j^=7PrFh*k;ss) zH8Pj|6R}qcVt(;p%|^jQ&H3!D;cGEZ|I_4D*lo@h{E0vU;)%d4;esC~!Gd2hU&Xu4 zihnx1;vE7DW8Nngz9SSP5^>7# zMX6u|Er{HcH$@WRLLXR$WD%{GTU$AoiNg;1Ks}mqxRj%PJJaiIUuw`L_8!I$-*bgH zjaZE;=1G@v(Ry`#uL0~2q^@J{ObNL$iNbs!V&ld<5H<8Df3NIs!_-o3i&jCxk2Rg( z_fo+IHG=c2mH)%EC8)xUbJ8Jui+7c8(q>dRuCnVUoaGOASO&MiW3XrA2w9*$U`93A zqAJpE{vWnl7Wb$B+%BAPiP_0vA0$ z8uA)g&%!yE;vpNP<1cO)zja!ig5s{VQHoL^`6?+*j zrR6Vru^o*-E$w9sfv~17+~QMxZ;n=7v015Is*;pHjCVmfW+He69N5`aQbp^qIGa2W ziidvY^Gpr4T&xY_`~{Mk3-Dg?fmwJ=u|!rwo9E&4TRvsH>$>Y)(0Ow^|B-Z6_Bf9_ z*bSK>_ttJ<)M*%HfLo!iG}d#JA)sr!a|`IY?w=We_lYme=Fl8RWG+^b6EU35Mua$=ew|09j2!+x~$b7R{N7? zZ{!>7dvrPM!zoH&S@ey~G3xrwLiF(J+_+k4`Pm)(UZoX!&l@RqQkFs$rK8-YNtzIz z)5&6Y9fm5djk<$Tw%Al))9uTJTvXMX7Zxc^w!qG;aXLVMN0=&wk)jeY*P<`#OoPAs zX{9Oi*6A{^&8MFQ}9@3=tjsA}R!^RS#UD`c>30F)NfSQGav`lf%TgtkF-Vig zRf6-x3Xwd0lz&qEsJTj3_)ysnkp?A_eD@)0`vxKQfFbyaEUOr`?^B;-+=zfC_ErEF zvN*Q_MKDV6cfuqAKWrrpK>h7_t|=R%kAlTk)V?5GRSKh&4nF@qCz5!x}odyL9zLt#6BoCP&p*Z z`*)~|D9#7c7*TNN5nMX}owwTXlS^oF%)G|-#J#_xHbfRJHu-v{j*9uG3(@xzjdrfuAWvrdAW(hEDl5lz!OeWx9Zg?p1{$k#E zO0GNDY^i^Un^9V@q<^(lF&WIM@le3SwB}SNLu&XiKd%2qP6$~6P2WEwJ6q{qs<;Kt zPH8JTLulmyRJEAu%9xE)k4*NxI+v*K92ZZM2;8HLZG!W|DW=%#;C}|Rny&YxV-38d zB)|S~YQ=uy+3#F?#!q~Ava{iM>ofj@W11MVK?gTh)&T=-M5jma{?X=(HrueUDmT`m zZ>(9%Rl5<2_s*X>*qq3NcO{idk!u+^-tUx85v<2LGQVjVT2E{;|6G&)Bn4Msz@=XS zB!2{GdQJ~gVWi)1RLiHUXoAM8`;RMJ1n_Zn5Cuk5zq4i(wXQ>1>&d6yXzYgZpEOjc zRiqzPpMEZ#8_%&4D?Re2bbrubluTFAGGz!WVz`jpc*x9L2)7VU6tI*kPvEwe%9;gO zC*7$(AF2D*Yk7@Ega(=+wpvGvUb^dZfXY~8vcv+ScvZ^w8Uw3>kC6Vh&Y;)IG~8z+Xq zBCP9($u>(=HiNd&BOL2}z6=!8_0%M^YO!AD9(SU&H)S@9FeNs;;N7r^#p zUS;**5qe2y##&YW5&0F@4{Fbr-zgh%sSzo=auJWi1=MH%bE|=7WMCWa$p+{Ly1tp> ziq4DP%1`P8+M_zFzgB-v>CWhDl z6MCq1*HXLVMvJtGT{?v*klKoZ@$l2QHHAJ3=&sn&)h)84n=&|o^xm9ln6l@Q^i8sJ zI4H{=N_QmT=iNruOdvlK3WLL<=yJ! zLM%u&Kn$nm_6Tm6mY4I~Z0)G_vg%6Dg!kut<9h2F#)0WFSt2eZVVt4Vnu&pAX()d= zky03R=5?XZigi}#*EJMDU>f&ZOjE2Vx~)QbkT{@G`LJ9DG~l_l#L%bT1&<)rK|%o- zSN%h@8AC`arE{>@-ve~ZNEva37P7&a-Fr;Kh!S~V8R;VKLV@mDL9QgVa&APGBcBd~ z6`u{rYM3B$uM|UQM^N}a(c@gTN5F!ns*gDG-2hL8qEuQX;;Qhw%8(vFwjvrcY` zlp4ronB5;R3#Qndq6=Pu6gZs{nFdKNg5=7jLEi)46~+6)3ks-8IvfwU6&NC&a@#!+ zI_l9=%ZCO?N8D<3Zd+V|q|;(1Cb0K#s`~C=Cb>8Eg?7MxW`2ab1pQX{5hKbAsZHxzEXUeAIxw03<*R$9lRDb`>9b6HNC)|qmhcb=zR z%|7mxifhZ_^(Sw>s-m-GwwA?N=jt%USr*4?=zA}8%KVd?WzC+>?4V)Ve3Zv6ow;5G zOV)U@|oLIIgj<@K-~@cG|{YM z_nZ+zJ|(Q3lcdwRGkooO<_kuKJeYG{{cRW%@9nW2JE<9keBAc4j+1vK7Kwy%-L{dN zW0ji3jmcd>$3DB)OQo5*ii60tQ_6M6;p6}Rl#A}LtMV--7@!cCJ<~&Ig6YQmvprcT zmK|o#*!33zfiOes4yHj}iDK|3oS2Za2UEaAAQ^oIe5K8yc|d;qrF93U|AsxeGc+MMnsL# zP8S)q5O7Y+2wy-GbI&>FopFqqgy*TGUOg1~2yu(RY(B&%&jC+rWouLgDbm6sb zQhgx@SSl0TS4{iFeZ>j+1}7II1qs%9J?q(ul6_LHrsb9V?>`lgwS&D%ao4G2y&@4T z|FEmw-GZ3fd(JfVH|3FVQug4DvoFKD${MZb3j@_-{n|A+z1xMXW0wuJaO&P#THni; z498H#u`1r??rB72?ohLVAe*>h4&@E=su;TpV zUuuivQ97^=aY6KGaMSNFNAdKBT)R}t6&NQVl1XV#<=-J78ddzQ#K)7+YD%Ny3UR9N zi~q~#Yn6E7VAPW%dlQzsuU>gkC6%{MPXj;pkC*_u#WWkrgT3=+GR=hhO|UbWH{ST5 zprCp&)>uj1(%Iq(YC{(>?>}RVWuu4TK=B(o6a+l)3l=xb*q%1+^UwAda^7<2FRlHW zB3~5fbI5NgpOs3bcFJzRMjx_HD)2PIAX(mvOuHka4iio`JnOr>4Obqo#@kGGn${oS z;_}oIzb{DCC>wowmSeW9!}9(S>i4mIudK?QLdwL@^;h7oI_zvh&psr7Ey7J9&;(1T zT;ipt(h?rxPHQZE;u&|q46g7Z`l2GR^&B2^VDy<{xOsRa3cz1FRoLz6I+Hm9`>g{^ z#c!LhhAPZml=<>j->ja!dDUQi6efW&>SfBjGr2KRjqNq5lQOC8QKgdl^H;x%^~+wJ zZ|ublBU+5^O&w&OJI+p8Wce^kZ-o?<^2ZevuJWrDYAgC@#rm(~R!0m}zVQH(f?OS7 zKXs6RQv6@rw`{5At}}Bhdxq>$SVe)vi3u~8qV2-0fF~9`D*p`XU|c*uVxOo{*uO$Z z`B{32$8vjk^LiV*MKNDql;OJUy5+`C$$Dcf{;sp+W>X$1vuq1v-T<=*_zqvHVEUrFGAgL!1`%0?C84T;m*>(OMC-PpY9_Yzr zlX~Offks*1!U~ODeP!X*^I7UhI~o#(m17Nd?U^L;>QlYGJ;yi8{rb^Olb(<}E zE=NrnCWAAib$z$7AtzOK+G}me7**O84q%uY%A#L9?lpz+!@j{*ms&*{-me&;s(MJbO`RzOnlJ~J;{I&b5AXkgBbUc#BK#=OT{-A5xNi1=>N_?#s*SUNv4Va# zn+hOFr|r)lp*orvcX^ET=BbmEssU{RFWG=Pdfjt&OU2SjRV5loB-1^R%4e&18h3EI zEIk|RRBw8A`_SWtV{Wo)=z!bN`r(CCl|Nl=F50y=OQ zAu2kSZFm~FTiP7GRXWe4f2!}=E|Og6Lpn+BeM8ilcI#XWrcu~%xmUI?cXzAGLUfabXo42+)M^_;Qc5rAFo->V6>{Iyhq z%12BQeBA7@rlD&l7uBC$w4Dh#w@G-IWewgZ987Pxud*h9%aQeQL7`leoiW9o8EFY({Dvt8nIbDK{`mx#Iih3HP)RelljY+ z#_l%jZ5}`9Ftk9Nuy8^$}DVkY#Glwy~l8c&1AguHmcIWuxHtla89vi zOE3%J@h-6}EF2=M4KiOqv76J0E*=-F++}Ra7nIH+HFmmmEnLY4@5U6{n7BU?gOj`e zF!`nI(tMmbl(ISa6v@zIZunXlyZ_rNLN-VLQtS(C%0VEr{|-bp+WNc=?`c37AF31Y zY2Hx7HZDAp_YLLGe!{Z(k3xd?RA^~xTWDH$TznnW6?%?9(Ot2e_ZYE+hZkwR7j)Y< zpIk^PFdl*eMjADdoQER9mgeDLhZbEuuY@P}+`lbIdQZt+aD7Yj5HXg-y-w(#Nd!MC zVQxUN^)0RwL`F5v;)pizKePU)jprd^Ro|n}pEdss|5*Iv9se_IQpi0wy%PBt{9iZZ zq1D!(Jt3lEG}14d0{x4I7#~rELFOFA2blLlJ-<7xNXT?ZKoMK-{pjgS0!8y#Rroer zQ<6ZM!MVA|FSPf?qvZuH8cWW#?3b#Op#f*N@79a#EdLAw021j)Lc(G_SAq8Mq3h|# z6goh65^>8_Vry{yfODls&0w-kKmGm-AE?^00|ppMR-U{Ss^`F#gbrA0PmJaPTp_R$ z`TI6k*$5^H7506)R!@w232igwSuQPF4E{;oPYKl|25?TPx9B8@QBWI&$rgPZxvluR zp9wzIgO0m$UFdOtB>~i@-I?ATcUr1GvHef`lQM%f8>3*ZKU8__NQ3_K0=~(epB9&LS`C#d({+Mn zb~dQ*VFM+GV`>~e0#?8BQ>g~=b>0j$I1j5jALwA`d!RNq1G~-&Tz+7Y%jqzEGOoMI zk5Q;=dAQ%mntYa#p9f-Pn_Ek=0Zbgi~1HXVjE!bi?OO!#Tbo9 zvpJr<>@+RuID2UnG!Z47wbO>k3yPSWY>N^VRVaz@ivR6D$}d^FU6>0f+nLYi1c2v^ zriiOV84Xl~6x_}ssZ?73lYF>%5)Qk>{762?^RHD2^A|JXU{h9W1Z-mCnJ=$ZMZI{C z#+&k)Mt6To?yw!kPaA+wJHMh|UfYlOeB6aCCKbc@IpJc&o}(6vK7qBbuFCQXOExN# zJjOYzB9Y>xkeaDrdDwS|+e$L3{;m$}Gn!TXzLi~=*@0XYyLwQ%jBWmc4&crk$G}z} zTnC=9v?(ct%)YX<{?DP2=)Vt*&>mz68DEOr(ag4qJypP*uc$|O|5rX1t`)v(`G4hO zYwsH`8uzc7otdf<)yJzBnBN$dY_2xN&`uDvA!ZFaBaNU`HeZgc@T!|HvxurFg zdZLtSOsXlHH=NR9WGGB&`#j2S(#mPRH%of4epNxPY(C1OqpO-{)JbcohB7HWMVYi^C1!BEjC$F@JDr8bvsOeWDH6Ci0u!|2h$`nt* zs!(W2#?Pi~9>#0C>I9IItsCH;&KiYKaaa5%eK*5CuA_}Pa=YIMGnPDtBQM&Ept4+d zzQ=q=s?LMs0Rgx1?uS~|s1H3?sQ<@}LwB|)uTdlm6&h|?aHQGkE)!l=Bn)GyqbKd2 ztZ4WF*0dH%V5?v)Ad`Z}2RiEhgT&mS==h0Gz4B|-7Tjl7j8j*gwL2EkrYRq^I&$me3hQNZCC9A6JCS?>f-#{qF~kD-lEBnorFY< zdj6|Bmg5xCrC@G3J0@VdO@xlD0kI@;!wuhz2MZS99HKdEhfR)YX|K5CDJ@j)S*Xw& zNSd&K>*Yt?zcM?q-^c9+{T4f2fm3K#s0Qu$8b*MPq{@j&L>klG*x9pFHkL1=9U z89>&+rv7UDRSVy279hjVc0PMAYv5W6zB2(H^(6lL=P^8*?>a z#Soi@Eb1ISNi3zZ!k&N(V1<jN;hU`SCgWC z7Z)bdoYfO)tCQeI?1ehg3l#b!_gDBAmPc1LyAAbrcvug*O#AjZov;bV*nkO%;v%9zmeC1yAqcVo zJ{kn2aqrWI-g*CTj^+%eJ%<)?C8U8}ZhuOW<_S$PRcBIOnM+79aGs0d@?8VZY-6Yv zi|o`9(2zoI<0SA%$=e21L<*wS9q3XpVf!o&DaQDU6lXIFh$(^)-1L zE|SoNG{LkMMVWcQ6Zte1&Vrh>oZDBgZt zah}N?-6`c3TOqxhmPBih)0Ds7>?6o5YIgUk`^cvhl-JpV6q*Sy>dRj?wa<=^*ONwl zb#(o9CkN-}Gs;uc)vK$k4%F3i9snU%h4vJik2ZUakL{Ot^Z^ z6nIyf>E4+nih8L=eaacRN_gpJI!Q^qNW4{8ao2CS7|#^&qgi?9_I%U0nVeTk=fCJK zZ+MUQv)9Q!jOE+xUVLCv5a`~yxxjgidrA9x@OXWY6Mx|S<};b&w?Ap0aD(XA<<)=k zd>*^_&R70^VvxD}bMe0_RO0p1-l5-Kfb{P_3;9i7s~)ahHT_~1-$jvk@7sa)SO6-(RJ(RhByNQPct8v7=5c75M=o!;%|DZ@Z9vYs za!>9FRd=-aF#1pi=0RIc^AOA(b0@I_>RgWlZ$l!*m#oU3&erz@q|Pvjz8?iLOQ8sj z+%9esZY%_|EV9KWsWR)epg_xE0fwcck>Yf@aI+N1Re3fYvC3b`FSCKyo^!tPjACq% zW3=p{Ut*b*lW;|5>$GHmbp9>VwLjs5SY>HY)`+V-mlZ3HQuF=Hj%Mk)mHS5QF}kP3kf!5lcHuPv0bpMR)wsWhljKLj`>$s>p_6!d5@BAW z5OeJEX`myiZ9D_i1qOgmAmdc6FRKpXahbw+^uXd z2<8&1eg8_U0}CUND}h1lhfLU5y0$1JgrWq-@2>-SPwWJ_6tpA+di&&`PqRj8J5do3=$g&qgoil$$}Dt6g^)ZrC0Ib^I3&e zLf*_H>=jtPJBTG!&Zud$qOP7F#=>lt;hHgsRtqX$uqo`TdC8Eqe|DHXPM-h}MwSTD z%Xg{+Iz9QO=TzOY8&cK}vQOA6H&JuKcO^pJ~DwB8Az6cJS+5?4i3r=-(~%2hc5N3H7X+ackiW8H1*#6$qSZ4G zR)u*-zJ{!b@;B}a^R|^ZU|h4%Vt>holR3_P2VwN@OAvbISB$d4E3++W%3BGw(*BT; zxp<0t@Zz)t;VVG7wc;XiAjGy?g6JuP(FGJ#LHOfwE7^;6V%&b2(^j%yPstAmkNg{L z)$?Gaw12}2OLV@}M`3M9jv6caJ_DoPy+6Lx^d=O$hK(xBC^r4EMi1NQXCe;BHL1Nn zi5(c`jo{F99VnRRUe$=6_cyB>ar39n%Mg*G1>zo?N@7bj%GI7$s8kEk`uh0XFBaV7 zC2I0zCCqciFkVSd?fp0Sl1hrRGaw8KA@}sT@2ZEOVj@Ma+y~MQ#K^%#!YcBI99S6~&~$A>v1*$ONZpdA*)!wPCZTr2q@U0DbaFQG6|sSIpFhzB(RV*kC?&Mk2#!Qz+sRsZ0{;{s0}uxl)_^No6dhT)N*8H7km z1dj~*B?bAhk=(S$B_#!JLaM8EyV?eM@-BKOjI>TZCWxbt)12 z_dBy9sw&~jg7e?64HZurBz|XqIiWX>GQINWgk^=i9*My=$c8K)9O zgnB}$Q!yV}4vl}b92|O_c&~)?#`foN8&7%n%wB%CceA(i0`HHLsbax8*8G`T@s27{CDJJ=IMbr_H&z~S zl?{*ESdOaT+_;D*q55X5JLAu@Gyj;)ET8+;e9k|rB-b&JeOR*c!ni$4IPV)(k*~t< zdEdM5cC+t>A;&R7S7Fc^QE=9$KWOQABz+lQiDSM=*}O@C6$kZK28z_4OM$_XV`K2( zWXwgOOh?=9LqM`x1N;z>=rKl(S#|v*APKej$UtrUm4RZY90066-1o9~nG%(nasIfc zzwCF42b!#prmMbY0aGDK%Gbi?6O+b)Ug-1(QS*u9o0N1+D-VUj32*yJmdOfNSH*7w z-UbSY7a<1QC*5OamUhj(=_zQGuNaSclWi3hW>0k9aMPj4ITJI1ZVmhzTK zjXp}1Oxk-I@%Ydjkr}RKe_z!>fY0 zS^0Ze@$iyqhDL^n zonq;aXSA6>&DbmT%_Z64x>jARM^hE1VXu+>1yT(!mYlbW6iOD6>h{d+OS6qy9HoV* zf2p!*@LFETCjExJs7IXb-O7mS^t=GJretsUD^FIL@&PODFez9GLkH*uyIXzwUL1y$ zCh;5;yHs)ciZ}U<0y)r9gs1nrhNwW@pM))n;}GEgKPp9o|EW?G@A{)sv~oIONLg-C zRR;EbFP6?}#YOXsDx{j_fF$vDCi*-5#L(R6x z%V!9AtH+q(ykf5&y!D&~x;E~P?kb?K)tHI{>{~tPwR}FjVQbrT)e{FEq-4nTYWS`E zj3#c_B!g}G-te~Y(VB|3ZL11L+8nPI7;+xOx=O7i>=$&0GnHl9u)~;%8*iuVnh0Ly zQmyi*7MZed<+sBmZ3AFyQoFi+DY{2*3e^l>DV!c->wDDPO#H9D(Akhyr3b-+UJ6gvX2n4xNkNjA<81Y+BeRo7|b3A*TLxN*rDGQ zj=zHai1X%nSKptC$1Ke6H< zz)U-qP?&5>D!XRR-A(XY37fu~y|FWzdYwcT@ComBIAX^BGlb*@JMOA$Mj|KgH;39s zbDg$iyCCm|x|P|T#q9J@Io%VnA25*T zT8=LXp}GbDaL`WM)Q$OB!9&ZAMBTNFf2bEe<5V0QGRxVE>#Xd2I<hc?CcJc~lrDb%wkja6bZ)2|T?fphyAc6Rdt1aPse`(y;Wj$1l=Q-vT zAa6k(w-WF6O`@tlQ6^C$jw;S&+(P>{Gnv;&7%jX$4(zA`5_MYVQ8Q9P?2Q~VrqCz^ zZ#ZP@1_sJ<+VDnZ-tK1>xI70|!y4oE|#9A@DaO0~%89Z1D z3r|+5*MXwH{yR6d90hflGQ^FY7(am=NMP?n`cn^lr0;2l5DS_2Vh=&8Q12ubCCI`W z)7OK-^Q<5~&M;-nyNO<$^dP000vHowh%C#r#jkACK-EonY5$81(4*|$F7oaw(EAlk z2gu&0z_lYOed!I~o1*>cgbjkFHzOU;R3)?wJ~qWZtIz8N_D~a3@Y0llH%5|XRwB+d ziNEyZuI{VA2{QW?TdkMC*=oWT3~GJ>pv@Io(Y}FGv#s}XX^1#eFRFH)2!`z|2I7(5 z`U~zQ$NvgtRYYA=EOGg2fsjbvEsfT5>mUC#%L6T98Gv-`Abmngfl(%d=slZ@Jp&u)-lIm-9{qI{$c>cKRCTQ=}>xW;TiQG0CBs9eN$_B5Ncm%**F}*Eb!x($f zANIe=)bz^qNa*z;Y_#IGK;U--Jqw({cbx9oeL@) zFit;4WHLrlb_v?0tR)e5Z!>oIr_4tPU} zJIn#m(^ z*j??fTJFgJ_(cV}9=_M5K^P8Q>@UrJ&B$coUTd~&OA)_^Y*o=3h$6sI408=Cwzo;-}R!b_gmg}7{$ZeSY$z-V~71fu!>pLaoQge?NdchEH zIdwe=?=W2fzIFH(DhpJW_@%y$MFFLz=f|ig>7`12q0O9!jx!KENcQ16kkOb1&{ra{ zjJ88k`IrW5^iq=a?_5b!d|1${H`@jVBCt%G*IQ#nT42>$Ijc;~pMTv4r&XP-6n1QU z)3N&Hs_WfOH8xdJb)uzNb8bcDsDRyHfk_lZs_7`VR0O3{E>lVe&7GbEv zEzw3BjeMN$Gp($1D(-rX+NOfY1h+bCYFNP5b)0hO-FlK@(d0PQ?i=Sok|Fi+ScZbY z7I8Z6cJFDl-{b9);tKP8Oq1%(S_ums*YG=%>f`8lM)(!kL1MaA4dmPd25H=g3q8F~ z^u=mj+3|}-KjdQCdpo{-g-lZ7xa*aCMe4 zG)r=-yt}kRtN9vUNlAz?K+6a}K0H@yQZ8h(G9HgW+{0z7+&-S4vt&pgk*_HyP5zop zQCW5O$u*Ar5`xl_pfn3thv|45Kb7_tg%Jhw{xyO)pyykD;HDUd*R*{;S!c%^MWCBg z{)sr!oW6K>HC-x$?L96`%J5eNMJU@-jR<%QiqQ3fDC0>vdJ<@Jt?9Ycj{dq3Iy6C#v@KSO39j45tOiSpsqK-CM{!i1bjuw;vJORGGMGUmVY)0p3T3_r@dgaz>BeO zHG1wtfmmhS)dJ8VhNE6{){9*;->^=*m)>FC3m|Q(F_{mxDZ?c`yx&LGr%k%XlRq4l(=iMAGrN^kBSHw~URIqB|Q z>E}MlRn|-AsOPjPI~jXGIAS1@1^2I3xkZKsBf?Wso#$fQ?J)Xl1+?zY(N<6tf7zn{ zXjJdcc^i~B5vMU4v`NT-rB(-WM42h_%DV!B_W0j4LGkH|YDufia=O-kk^|eC&si8a zf^6jbPx>|3Ue$!7caal-GN z%hkhxuyji&GgL!UXy*mbGvx$kAbna*O?9#Jx)07eJCW)GAnsKSkKUi-eqC<(((4qc zyPk-j(r33aFIQU4dpBF@rSDpUW8`f!jr_MFP z$@Vtfu@eR;mzCIQ1l~3rsBW%gQvZAQ9}y{Z#KHqN-*YyFuZH|gVuiJ4P+9l&&>S!P zS{S(=0p4+VsejZwI|8I`@J~jInavK?@hd64x49e-B=U$)zRj?dU2mf{$@^}26`ov{ z(Q`vcUzuREvi@5>%8>V~2zZfz+*NKNRHnvA>R_Kh-DoD2J=i`PVZjiUNLyN;JlFc^ zeB5<_wFZ#q>D&F3F+Za%??FONRs=(14{7NVmFzC_WmJo1y@B`dIl)$E8Ad$$m{?p5Y7Y=# zSV*77`BO7&g0#02A0vACNY)T!(sJU#+?-UiFO)c~>NP62R|VLbe!r7LS(-l)_$2LL z14j2-nHLFz?zG$Xe>j_HnH+r?SXk4@b;B&j7!)Dc`v1|N4wb`99o^1ZXbc&=K2o zf%ApevC}-o2PF*3&oSk2s-*K|G7r43cC}lzzKg0F?q9;WI)QHed4;&ChGF&Vv=f%g zg<4x){tE5XrfF0HvL);@Q#L!KwDuuL_y**NdTz1YBGl)n{MWf7wf8{1FdYxI!@R8& zeLl?w{pwBdjzvquAtd*$h91jH+2RnP(ieIJdALl>NoTFYwPEC9$UlwGmWGzf=BAmL zoDA6~4Gw#T4vt#C8a#lreRH-tPCE@Al(T`<3UJ!K^!OhJQN^DbL@h71|H^z8PpC8S z@J!K)aGH*^0zJuPS44zADY8UzO!f9v#c#2f=1uiq$rT0SbIMc9b#lU~DVB>iK*B-? z{RGh2hwt`WYOz2_$Xxc!VIsQVg$g2$+k|Y>fKI5vfivBFRWP7F%5c{dp2y-L>cdE? zT?}Yp5x#W|ICG_bi3Ho^Fo4Xy++I3CobrTq-k1VeP)WV{rl?}Ux0$A#b0>U)P%N{H zU+bX)?qz>Jz?ShdfM>$H-ZMadwUBv#=%-{CHANLe;#A_Am1Nzuv z{?el#*VHGjE^!hE$Ll#FP>epVcrV@7-vzL~r@}<~7~6xAl@`(F6lu?0%$Nsru7$5* zMlyr}c6>qQyQ{m4 zrAOhAken_yS7qyd<9JN%cv`bni3tqW(~jBT?F>j-no7caYuoi^ZQBOu5HnFJyJ>ihpKEy(^QC-*pGm5{?KyHAWcs@OJBE$ay=7K` z1X#fkp?D{@6WRqG8ZyRTTVxVb|M7)pO?CbzsgMJlii)B=n-)cLl^ zl~cuXmf8+3G38DicG}rFJ7-zn9js&P58|y9{)Mwhrjt_cR_N-|S_JZfD-6A;1Y__OflA^9R5*{zQd!&WZhrDD(T=f9=sTC#48$@w;;7PKSaGmq=qck-Jm~ zl!i1FCnPb|&*y)kJop;}M`s7O?D&!@sG8DL*3N?WRQL?wD<>+BZcryG9aLej4boAs z4`qZ4uCd05st#&>y+0u%70;8j^>gbNspuGxIwN3Vo!PVogCPr3o4Z%aLJ{1`TYC8K zspvTLpJ7H|p4rM;X8gP2Vd#Dkc1GBJKfoz<96$z4Ds%3CO{PzcDuP9`@6Z{92%G04 ztMO)0LoPOKpv}P_hxj^-F1a??C{@3fQV`p?YQrHn-Uxe%YM$eNC;B=7y+$4;kr82+ za1f76f{gyx@+OecWBnssofuO|If6_?u5@ypIfhhfCj3LS4yeb!B)G#Q59k?M17lAO-Uu}d0RoA^Wmp5K^#l*kA)#us{6KgMt1s|7(5AP14h?;2%FtF zZJ%t;pD-)GDO925XmtnoFZoViYGZPCcty_D^Gw4t$ShOHi#0N;TxgV)YfAmu7$NLP zX1*8Y+W4~(L1bcMCFu4RyU}Duksw|mXlvP@h4#Tg_))NY&0B%26RnLl)S|r`j$xP^ zk_|;#@1f=TZ?~8cV?pLHS6iMV&omKYFy*FzV~>zQ&%361u^Q97()ywt1sxLt28()A z0p6&S#mTRTIhP!y&c34XonI3q*QX$=-&tSs%*G(EC_xX)XTjoal1|X28D@TA?0C2& zF&p?JLrs5N@VwD8W*Y@NCsNZ$n!VphW2HGydsP+_8qR?Zb*eri<$8RXL^c2A-gg9T znK8`usXy-4mo&u(as)4|^4E$ckX2$0?iWvH`y{DXOmmeCgydXG|3nJj7i&|Mc)=V*#ic5wn0~L=^|N4-Z(#sTS<>hcifE1NJ5s`{}97Z`SX-z0OaVPeDh#1S<$0(p2;l zf!4Aa-x+3=fGtSta>@IdKh!tXI?HZPk>_#TG<#foFV8zKc~!`#&Jj7+yQOX3u&-P? zVGzMhu(dVwv?y+^{)MQGxudQHDXG!;>E>BN`&p7l=2_fBDNv1*M!~0c*qBIY$Y^;L z$iNbvd7GX>v!U`&0f!9HcjE7Y8YIAy%CzfPN86G!7JawOY_n(2WaJVf^kgGCjYGXu9Gg@2yIpRP^_QIJIb4U4x z^29k*Nz5_tJS6_Er`^S|^AsCVr3r_V$J29uKG;<#rBoc{bAmg zXzMgdVR$aM>G;4s#rWDRissV%!$%R77~#AtbkQ{{B_A02C(Nymhd_a!wD~x#J95i{2rU*c<3j-l!9*gL z@^Sj&r^0mZ0uf+{_@~HiwwTYoKa@eqZF_|pvGREF%d*3QXY=rIc| zV=XbtG^o-GGF;%;6_y)f8(-<6jKM|zTJi)kS}aHe7f%L{T-Wd{eVPNiqd{PCa7Uz+ zGP`3dEJ)kxh(LQ=;xT@t3s}PFx^vy z0t^w2TexB*KaI=C8D>vQjnLyA)tUI#y~UY$mllePBkY+d-5(Yg($ukDUu4_$(Hs;m z-d{=yt4*?85zSmz$$OsYW7x~(ExIZb-rE6Kw3p%n-LvG?WMn^yfwmVOKpZ&GkB5oc zG^ZE6Ka%N9iJ$lzD56!Bf35r0pSHnaz4o>JLKDF$bG4b`^=&PulK2y~K)UO%HcegN ze^dsF4%~kLRR(0*DZMu`g|uK=Hvb^RkH$$ZXq9N{m9_<7&+W6O znSFqRlvIz#FlxD+CEuk#>nbCD+DvaaGJL_kEG9w5I278SNI`Z@SvPa85|4Xoeu=JP zAJ6lh*VRgt2VyvE<`S&oK*Vt;9{3@asH@%BfndsiI3n}rZl(rJ^i z2io)UF=m{rB#KHpYJab-4G4C-cb6>JN1LLv0rBpX=$N<0>%qgj?~nN$)s?@5$)qCQ zOn^~73k@Z|1m{FD$woD4?AT12iO}4s#>0`tt=HX!$j({oK58kr=)0Z^UTPQBny;OPHCjc*0Av&G)grj$B6ps#_7Y$q@ai+JJ7=L_|rsHR(*+CF2kiF{jl4Z z2ko$x-D?q1gPtd|sSeo>!N!MG_Ba3K<^d4DCy$?|6dmmk?!LF}v+nzDCrKlN55WUS z#;kmQ{vi&~?><+#Y5D(=a`?Z+xNrsO)nmbd+B8P*|B|LTyZM`Pc=vx% zrqQ9@H~$y;J1#lcNy;Qeg6A8~xJA62VkT+5X5)F~0aH@#O34~ug`OI-ADdBzh|{jT z$V$c+21WZyRGm{A-xK9J0F~ef4ghJ-g{(QXJ4cvoo@)WpW=4DubEkjbS zP?!*57Nz;6=FtR!a`{E(B}c2T)M#b(m9X7et%EC7ng1 z>lXrY_<*uHXIEXKl#8Ft3JNU91FXAnT0VKVm>xLdx_pYIIjReM3osA=Lj}o$9OoCC zOCzT-kFuI%Yk_T9TLMXGmAAkzDo*2VNm-e}*=LTCWZZtjlCJz0%`Tw_^hGrRA2OMT zMhi*zWZ!RxNMn=w|68hO0^xtAU>=D4Ck0dS+%s}E)^EZ37HnH}r_PZ1R`7FLnI)+F zIdb5KIai7hh0+Goc)qU$%bM*b?l_FNlq*+?wfv^xC%xqNfuuB*;W{A2aub<8=p5W; zKmsM-#A3}2dya)vsy|-rR|M5T;V~zuMO4gYky9@APEW1k)2i@J=FMGuWeWz_|H7?MmHK zXLr_}56Sl__0Y=vU`$BA-!F(FQB`q75bhYyP6Zb z{G_JF2RLR2@{k8}_->Ay-?+zfc>MOJ`EIVO`RyubM$<@N4qAfG0&gw>F?&RuZB}2pY^(R zt5pd;KPEp;*Np*=sGsK#50B?^>)jVQ7_;rEnbliIp6@`dyVTQ->gd$ZcYU9(4?yPU zVdX7>+rurH&pUIc?)2x!wcb(pC*iej7t2;>N7wt~Rli-x-Q)al>bP5?UiJR7-IZP& zYb>0b_rskO8~%3eXIr=DJN@{_$eG{pFSB@8)@Ir#J%W@cx14U>?#|k+^zLVRJv}Wo zH@#-PZqcP~Kac16@om44w;b-_Oz##0KNh>U{k4wPf^KZ~cq8jia;(osU`~DQM`q{o z`zz}8G?2eNk>mIE&z;!ETR5NJXGm;L@82uW{`R^EEoLo0EkbE6FG z{#RC)OscpzP3Spg!67k?2{|C`VA}8_qjt^k{c5v)>-^au>wuJ3a@gcw9Pt&KVn#P| z1;($3s|{W*OYl;cTlFseC`qwOHHBA_)_E3!&EhLpb?H;M5bGplFcxSuvC4lL;7ziM z!B@p8g4kMl6@4+k=f_=RcUW|`)}?1a1M{tP;L9QTSW~NQK@&~2rZmzAZ_+D=_LZ2# zKBh3{wl@6EL$~@#0#;@RiQ*##f=BgJ>N_^2I=TjXl-1{5HeGYBk7NUfiVLiyI;PKY z5Qd7DFlyR1oKHrcl9btzU=rk%fT`zE@B2QW~4a`&e>G7vv-4f?|;UVfHq4qI;^37h_QiAo8}YP8Z}UdYBuHJcsJ@~9qT)W)?)VLDdT@LX!(Yhmr?<)U!s})=$Xd?W64z5nvIh2hU8c;aIXy_VnCE#kIRadGRwLDt;#}2 zQOy8dXVmvtl6(4r0+;21{6C@OE=+Fs(vjU(`+59#S2(R^oAx?tvy-azF3+|4?HF0s zN!N!6`mlMpX)WqMVZPi5Nh%iRUrc5NvfRS**253KpED1CpDH>~KiSH5D=|4M*7(Ly zd?*AKQpN4%xXH=voS)IQsD5t9q$*zSCT`^{ZsjVT$@~OiLhYj!KzMu)YpBgdCF=aU z%=Jq4N);eiCA?9pV=jVvQdF9+6PK;8wpaXP)OLil{gP&2BfF4Mc+NRGKEAjdK8!`h z2=^d+_2eUQYxK{@IdnVem8;|YLFn*7?`jam8|{LQt%W%eRFwupX6>ftNS+r88r3$7 zyy{4_Dd{JMM7I9@cQIqRV%s{$xN zE~-U>u(A@S4=!iaAi?*%c0>5!+rTf>WfR_=9xAfKr(h85${BBySgd%*v9lXrPc;1q z$7;s@WMa?w!91{2_V~fG{}m=2@Hj_}K4AiiePJsoj54@r**2KVy41I@Owrm#2j&)!Qjzhv zQBtcnO7e2ujoT~RM#P_R&`LyV8Ir{`EON(4w_ipr5nXYh5P}as*zZf)w9~%q3Jf(u zPjXa2WmUJ$x>C>OU2UH>gE!K8sB?lsw3sGFrBz7`fhz7tc+Hh04(pxQd^ub$C|+>gzQg#^oDsHDuJ6 z1>?5ub#YP(10cU&%zA*TbZ(-e6X6qmKnr-oBB}q_WBSrtwJS`>Pv|bl4|3|M%uYe& zWs}UOWD@I@l3Um8RZKiYGQ>v|We4^Js-+zx00d||9v}gS_p7JeWFZ$jjzMr993d6) zuv4xghf+lPLXD}eK9rEwzdb_1dOV7-YGKUEJ&y_IRwFFg?1kYvE7y`?2oLu?>^KIj z;jg2%Q~^XBp$_OX6nF&AO$o6*oN(p)8MF$!2#3~n`l+2iw&S1FP*rPb7YQ20rNEP5!V_%gOA{@ zS&Ho$6KLa;7e2fgGb`-1$3=`B*tN&TlaF~$$Lp+>T|F|-IE}A(v8wZ_MLs)^%vhL_ z=eHe!Gd0M#@tBi)o&Zv-EV`!*YD=-;wfH#!W4C=_K)!RU8|go4U{Z$*l48G;PAYe& zqI-JM00sFX)7XGD)}IkkQyZP7qh>%t8%%MMxfAPevT=?CW=%$Q%sq4D(wt;NNJ;YB zlA;7qq-9m0yw}$W=*6Cq?Bu+C??G?t(Oi%vPNM$(Sqv=~h#a|gSuWY4bcTG@~4Bkb=;hH@o z@6a9h9;H(B=`v}}JjQEEDgpvk(yo;XAxy|aA^t5!Ifr_{NSAZ?(q92)0ZBNW>1+>f z0ad8v=yoQhP_Z<#sM0c_YPPA~HJ=$gj8ikL4b~T_A{6@zjVY`ne=OnTow1E26Vea8 zBKI%g(`xxpKaZ&wKXau-*K3Qgsqj|Xfp`D|GBF~&U*T3j{#ogYf$Un;zPu9EOG(Fh zWFw|Nv_dW4F?!FAni+P)Qy`78?;{bziSraK6B`L<`d*X@>_orh__V0{>-@n=kQ_WiSpc zra+0EC!)G%{Sg}6f2+0ph@yQ=yng4wb~XFmkG@7jon z(bkjQ2%B}z<%3%}197#(6lE^#yUen-EQeIe4bo`Uasu&G^60Y6y8&{62kmS%(;E=z z*^3O6BMT0-wKuyc&b5+xpf<^w&msW|lBEmqaR3F$9s@}-sQGH2qHuQ$W!Zv|2vvDK zSF$I1;Ssr3lc=|haK4Kmu;T5D+s3qvSm1%?uOHas$b{b46AdI2fC!7> zI5#LrJ)3IR3yNU_ z4n2VmixyhKb*PF~T4XkoS5{oZF&^ zEg+>)-4+O$-8&}ZFqg-4ZbEBzpnbsP9}H3+w6*Qit|K;2e&3RXZD8I`o_ts5=e`B) zOE3_KhJah3M`QS+kBlA`$RP~6OZYCB2Pn~lE8{ECqqs!?{*VI>b604nL}v{J=-5r*gnes0Yj#8w0Mg16P+mLzAju-rVG$%ZLd;pUWV5 zK|V`I$i=&;8b0FyDn;+P-YPS8{Jc-M0g48->dTV{LkHzA3hb9t}){E zY{UMZP{}`C|F=VQh6(rznv@2k77($>9s37L+Lcn*}&E9#6v%CU*`Z zFSa~&)MYD4`^;DUQpWvFWS>Z>YQ`-YU6^xmGLVkQnX0?%wpF?_#;|s!mm-L*k^5qkbS`y$zTI6KS&?O(#T^Vuuodzs7@t;62Z!%DJM@hHz;*G7_44ygECeNHG{!j~K;uWWB ztAVCighVW3Rd1nJjE-%~+OB;|wPriQyB=F2AzY_BdZR@{+Z&fbFQzW$BLNrgz@_lU zLJ0ht@@v3D`A*<=xaLfVhtDGIir{3se58pMEv(%`UAsIi`F5mkA}#5;(0Aod)K;YB zLPBzt!i1Eq1!>@G8nqr*0jTUNph}4tbGVX_z0Az?7mDoQKC#1kBPO7taub~rb-n#uVRX&HK_|gY!Bi;kGb!Q@zJBXXfP6_HTeZ3v3B%`&Uhl(j^Voo(#ZPOL;c)dvo<1c~; z1R;PW5QNZEHg>VWxf9CG-&r6l%b2;5tN|G%3Kq5Jgh2W z?RiB=^+qcRxPrU|3}pw7w6*$m<$AG1WS7@h2IP(*2X#cY3Iv~qbw${ESBI_9x|}6Z zl&V1oT7=_ht6L@QNTqxNiG^@0hrdyOLaVh8DMK^wHd+-xojMOb)#w;I7gxqn~&j|B5 z@2ymNGwwwRnwKs`pW#&B^IDh&YjnV<5fAb=YNPovWlTfLYm3}CN|YvwrG))f30Zbw z$^rhRcNECT*NCqX{7Zk!i1h2#{n#^`yK{<6xMJFqbom=J1L|hf)?EGOI3cZ8ne7zY zgFbw30o}G~zS=}CuSXlkY_K8U66TB~;1y3Aioj0sEhMoA)SkIzi;Fo5OrnmqW2yXe zvsn)Ys-=tXsz+#7*BIzNShBSf}Y6w%NVRIf{U~P~oh--A! z{U<2|{SwJM6bS5c4^XZVNpEt_y-cNCxlwYuM4G}4C=VzTTKz47SHELdq_HcCO&6z^ZvP8TITD5!4j*XF4~5$j4?`HxCRk14%Qsoio^H32ZXQ_;c5If-&+j zVqtvNMoz&&JWL<&{5B-mE%j{4Et!qMyfV~AedQ}r+H2?pjNRHwtPa60dqk~K$X7@47Xwjy^d6G32%~yt3 z^<7(;I{{WoFTNu((h`W<0W$cv28SC+T0w^UM`-IM5VodiT1s4rG25z)PWvWTG?lQN z0o9kHbnZ{Cq}#M&+|SFvrH%hJ8iYzmA}(Mcsd{_{)#=*duEeL}s}&6U;y&=TtleG5 zs`9+0Aj^VU9y_~tCgP_mTG7O8gd-f3);LS7GliUe4K2R{?Ma>Torv^bQyIf$eE6Cs z3D&Upvj**+5CjnYt`gI{j9|;9-bETiym4u2Nv;3!6knhpn$tex4N{hAb3Zyp z03kNWCgjm}4iOiURJ*-`f@s`&D_MmBE>&*{%A7rQ%IIX7wSiZK5TJUmQ*Jq0MtvPl z>&XIX0+_7KUOsT3-XJg2Tj`29P#pf{dmt9w&dy+-i^U-q2eH}r2yAk#@1+&J(Ynk9 z=4MIqBdO>1_De^rW+sexlF|>3iqJcZdY!fW+ngN)E>E(?XckSznTP<@sQEg>l}aOb zZE8qEVxZ+K9)*PuuUs*07Tczhaz?1dud`#Up16MEbmlqp?#(_2=nUr~hg~;VcbAH6UCh6W3#GfIHN5 z+!gySpz$82&JJl+2z>&B=fgjw#)fQKBQSKa-m)KuvI%Uk!0hQvlVMXj-c-^UMArV0 z{v$xr`F|H6iCmplTaH|Q+R{3IAAeXJkJZ!7SYBTp^0rFv-tfbc>&p4`_W69-MC@+; z{Mg^Md;?PVuh!~Rm;Jc6W4kwEKkMxLy7B$qc4NEsxUPNOhPp$$KV2@>b3R|2aXuc0 zP2a9wT)f|(u6HN5r-oDcxb{PrZ{Hi8STj5Dx?dH#vAbJ8T-|JZJ^k*Q+cn<}qPjnB zw*R~oU3vM%vh#hmV1K@jcdIR5b>3}v52?-b@y2|5vBOvC@xHx3H>cWNVW{oP-EAK) zKM^c_-aV-oedzydRPqnMaJYRo^6&3D!JNmqxdpq2`xUxHb_ga6y|G1<#0yGO;bxnS}JF)n_ z-(>QA>^~t6;dy-f0BpU#TwNNz0nP$l%)FibYKO0uy9tKe#y>kcoGzLPwz;oA@80S6 z5zqZT?VoP$p1Rkj*xq=t5-(r2-RLXbaxPMrbGm10QHPe@4kj}{H*Q`&nLc(W5&bsO zr(*p&+$y^J2LDXnLtRh7HSk%B3gZubq~?&jy>x#*@Lg|)>aE@#j63U1`@IkSg744T z;CW;J$jMC2%<=W({CI!bw!7YV)#F>Ans~lVy*-P5J)Fw=eBXZBf7W`j`0?@fLa+L{ zdYoD7_0aG7)^7&5x?0a%?6%v;;f?n5_!h%QdwqnsHNf_U`1yQFw%_r*nf2~7{pY$L zCzW{@4;YAFbNi`zA0aT^0v&LuTxz7pxo-LY&Oicp=FB^5_T-x-eW4M-@TPL$G|)xb z7t&2o2hFD`_s+0*_}?a1nvW0QcfQ_y21?)4tHanKlM~pg5e?T$MekYxVv_$vn`@>_ z1Myvk%rS{AB}mpt{ZXIww5h-IX?+{pI`m-Tm#I7O5S2)#V^uw#HEj>?)K5=BPDz$Y z?A9MU4Qigs^`MkmQc%zpt&e<yC=w}A+4+5CE&DGrY$kH4L#*-_7gj{}`bUWtb!aqG`up64f_h#D z;QvSmju~iNy8r*GMJsr*jMMbBxYrYXut4KxGN-EP))Qy@dDjwUK$^Qv9GvmL&C+xk z089sd{=x*n7R)h}i&}5`>z_35Z-;SX=nMZ_jaCQdzo1a71J>`z;9~!QLfIsy z3~JtpH3II0vA^Ah_x2`S^B$n*5!R(+Lf7 zCmOGKdY$iEFQ1K1mDAyOl3UwP)%s?9S(^X^^*WmM>GV5gG9paJbPDKn()vc$Lx0e~ zUjJ6`rmfHSWNRbaE);R`{LQjEY5Vgf%l9@d%f_r3-pZ6S&HlDk=rLp}%S+o*Wtj6L zO+JdZ7N<5I2}KbFZ!3eJ=Sj?^{PmPE7`UA2;N*$^8mzk?4PwmYD(z&{!lRnoKVsQ6@sK2i={S zZIwhYnJE)mK7ws5nATTHPf~7&>w{_AKPF=bATls$ZtRE9iOU~x;E&`?O-D2NS4L(k zWe6`YT?!xC6j*3{0J0R1M=wai=e=m<0>9|*Zs55P;RpQTMlyX%hx#7HYc&nZ)2T#tJ713~Mb{zM_AEHRKX zDdk$A_TuP0|xDHKiq<(@>$mhr}t|dh8dy$XpJelPTD@P5KZ$(c_k!r z2*XZlz9Khq2kdB-@KOqyg3@7Y^4EqL{!owk_z-OIdidhPTm`-mA_uFesE@m*8odeT zhxX-6q*Cc{^9Xgu0qi7YQd-SRY*nMm$C1g3RZF!wb#jeY%32=Cth^ip85%8~yxa5j zRHx4O;b>n6Y*U*XC_+ly1k^%ZyTsgtXp>K|UcT;(&lW6<8Z_@Dsdy_>o@Tf>GTi5n zT}yv-H~=&_vf{VBKg`Lx?Klag_nO~>tc%p>8G@2@f!fIJglUc55nID4dY$J$y@!bO z0>XQ$tKnoh<|l{4`4JoI=f!nPqC*Yiow1kqncXD|!+5jZzR~#FJ2=_!Cs!5HuGs}y=Z-5|T{4A$_Q2g}0CC8HK$7r) z&0_N0hU#GxmDni79IX`}bjUERqYgV7+`tLMjeKShpJzkyuqvTc=t;LxY?4Fvgv^;n@ zG*4~aNfu3giUb$J7NXcfdY(-nV{BS>5JA=V4a+EMrVkBz=ak)Z^Lpc$$Od~NDRQT! z-KM{Zn!F>jf2{y0|NP#WPOiG<(MfHVEJy&d#X5%KP1glHnLA`_>&K2+V_3$Yp^>(! zI!!ZZNqMUs3N`H2lLt3#{aC^8geB_Cud*7k16#IhMvmc}+{pnQ}Q zb4-f4{111ea!i>%dUu_x{`0qyO-9KGS=eCHFW=@TPY$taD2wlz5?$HFzlpv)_8NPe zxk=`8Vc#A2Fif?qdQ;b`=^w=7qPdp&)Ud;QI%uq41VM-DL(=8svXNN zc2J|+c8FGl?6X#6kFMfbKpV$jQurh$q%yFfE-^@fi>CkxUkHH|s9W0s^x&t4@zy`H z43&or8s~k&4V4$0&$hY5MyV10Jef?D0~g{?vaeeom{dzup{bowLxQFq?1E|bV{h_f zy%#n3^J@J01tgz6ucfzsfD*#nISX1&W}0MBP3mqQ4&SSP+Pgr!*;`H$r3gu8%E)f7 zcqee;9B0#Szm~nWP%B66UbS`H{Xz!!^zqo`x_%xEEW4>`e_MTs1{~Qt0iUMVySDb51e^47n3$N5qyIW)m;u^@p@%$M4Il1?@;Dg^%zMsb z^HU!LUE)n9W^ryq`|yY=6!_HI^dHbU^*T8{@Eku>)~Pu;0q?EXyD@_D&!jsT*HlD% zt)nu?0=q;fr{v6X;86^%R%ruvjj+R$|J=4#>cPO?XZ}QFs%~Eo-ak6yo;v^d%?X)- zk^M8|p9B7W3|>F!p52JgoW~(OzTcR~VYTDm-n_CO6F;uf=B00mxVw%oX7tcIleWuKi) ztF=jna-o?QIGGpfuRra6@<%ClWRJqL3{kS0T17fp1}H_O?LACAIobN6LHcyg!F)J> zj;UCWUwxjh`{j<+mo0Zy*7E{qR z4XJw5w88!$yUFC39I{!><%jAdZy~%}^=j!uaZ?b zl?Z(+y_TRW;>` z?G$3rjWX*La;)%&EYsGAzKAZ7)#28K%Y^ZurF_W8D**Y*>M-aMRjbkj&Vzqoe;-~I zQ-CN&5X0LpKya-ikHE*c=}J19d0>~kmO-V=O(-sKF%^VUoV>i`U)v_g`Am318LZ1UCts3M4P(w)I=m)JQd57H2wp z)pCdc#IKYhizV)S8B;wtB_^t!vUSronaQJI($x0YT>BXZ^M6F{)HS&g-g_fdLjtpu zW@-0hhi%3$uslCA+1s%cEewC-^9CJ@$Rwd7VVJEjeW&L8plBh{-jg<%$|%}~FE2y9 z*N%P+K^Tix9$y>^VPDcDgksH60#MPzM^4FBX|d8=i}#(XesUzr1~Uy&OPE#gEs2Rv zHo&=dhepJydGt4x=_a?X%F!zyyjx;#=JKZY`Yx(aVc66cAg1jo@~D54m*L1r{w;X@ z(R}?Fg6s6+VBGzIa47Gm`bwn*A33?I_KiCGwwhsy0KyKM=KV-h zKe?+{(*0<~t)PHUi}y!c_r&bjyVGXZ+^bH3F8e0eNjG(3#9?jBM=!`W-&CO=Cv83V zt0L=br0}?O&U?!k-OKCTQ_*gn-ua7uUgzLT3gl2%dJOZc8^ge1K;vZDYU695_M|ss z*`xgo9)f6yiP}VO(5e+$KiB8V- zC#%~s6S1-;lW+JrX=Q2bvc0aVqfFnj$1UY!*KwNbifrj)6G|zm3|syACgcg4E;AI$ z6=)~Wp0Tmo^c?ZMkI2?Ai{rh3f+K83TR~w?FwGzS-X33kbc)rotF9Ua#8x$^x=6{b zxcaEi>VxR4xIAn>os>@-h`+dE#ZwH)%VVp#(NHqBUs*7K7;DUafq(?ioQsc`C^dm} zrsTJQI98x9(`7u6RbYt13Q~n%k}0cSkc)3m)?joLA8%@_%?6u%gqGgKSyCoFOqy9* zJ>N~ov7MA|ee7rO%l22me}Iq%YtJB*7FfZbo0{cepeUm(T{p%kG&mp9i{A8B72$UT zEO_ zS7!Q&JQp`{ujVh3fR$0n;Xe@v*^#msMLS*x-U!jKWXxK%8F$v{+L*3CT#=G~GhA7c ze#87qn*mc?B9XC(CKOml1^<;yB||Q$j4&?L0S?xx&rW-4g`*f(xdCFZj2K$DrW9#4 zXq!W}SAYy68e66wHVyk*TO%~brlL^oXUFdmWxO*Cyu+Rovw)y9C z*42VLSGV%YLf^zI$+xi5Vq$RVE248b^b+rY3Lzcc88e$wxHFF zSZynUZj?(JZv{JAo009B)UV>OIVdFRfwV4X6G6s(k*!8QRhgwHgyizjYV*3}Jz2X& z#ZS||?y?LT+1gYbVn9VstwkN~uGzXuJ0{8>nTb!LsOm;v zeQG+U!~y2ks(jdHBPRz_4d2XdGWm?28|!1N--N6}aaPC1Z(IN*)!FRE)UnHtM@9+r zLCLXN@03H`zBMT_A4!MQS~xn0VNhyVPJ!Wp)KF>&Rk+%mEw*2)u(k4Wwb?1BdXxXP znQ^DAbi;A8g5y7RhAvv$nwvS=Y!$N#sKwM`s&I9TX3$3%nC#&HwZ_@z=o~G)JT3&B zzdBj&X!140RLxiMKunvy(pxHMFwFycEhbyX`nLiGKzN~=>8ccR0v?irhG*}qxX*5AKm&(<3S`0ow zFOzHMT;%btQjrc66@T=_#SJ;c1_ZHKa$%s%aZKT&aFANBy!~clRQkG&AS3OXe1<18 z!2S|Q*G`Os6kp&!>6yS!nQH$R|D0L&fS5N~0 zSy*!U2hOdscbd6`dy9b>QG61Q1K;vcGA9zGPDA78NR2cDrY)tpq_+>ZB(bGO(o%@v zi_ht=C}6*b$^A@!a?))pB`!6t6YDgbH-w~QTue?%`&xMvKXLmb%!QrPm9LSR1wGN}W1okOm-DI3jteF_l+<*b~Tk_iFqH%F}O zZN{bOu_@zmxW&|@A`8BoZORc12OGkE^0BGKS}ZraloJ-Nn|;bp9|v+XiDe9SHpNYMab_h98qYK!@9r39i9p45J+U;6s&PYBRJx8h71bz zx9Rp76nl?ehYf-@u?@U-w37=l#i}G*sCLVO%2BE$>$Ld~{^k8^Q-ZMyiq})z-(4+K z&tJ=owaf{_DPA(fBg(Pc+rFbDG9Hj_`zU|Y_O@|1hM&?x@`O_Ld(*ngsCX0y@#f3Ff>^o2X zj#_Da?jKQ2VUKK-UO>6?h~_qPDU!-~+j!^d8BLL zb}nO8D0sHnayP@bS+Ol8yeN1hhG(VP;c4d{KmD!@&lz8^JU6xOaSsbuA9RL*8~VI9 z_K#HCn!Z8miivMoLLzVION~eG$fx~f7WI)|H@kEgafh!$(Z*}JPs*J+f9pLjH)rU6 zTX_#JXl_rrRZCvFl;#Q)&9mW3=F=(AqYMH}ls#-7iMYB%KCEVAnlTvHoqkAD-$R-H zX^7I-ov6F4;*;=kh3LEe_cV=nVmtSt+j<|rQg6|$Q|27;T56;8VYtn;URAqO1Bdtv> z774E7XYP7{5Ks1jCaFhff(oX4e^-TP-{@jb+*C!I9*(Q&BK&#w=H5%D+ zU|*TW1Hz`{Qnu~&F_MH2`MHv*dzYZM^)b@uIzh%1y8Z6~G9u zd==#Vf$T&c#7*R#$}U7}FuX*tX$r?W=W4<13vOf=Xs$>B49MDE`p8T9YkB=WwYB{v z{B6grSX#4q9-ATdY_%@$4;ixtInXhKt%+9%iqKX>y$$$hgqiQ{Q*W9$Zo<4}@nd-L z!0UGvW-k`RP3*N7cC(ZEK%cXXxLQpTCTG%M=j_@e<7!*yr|cAydGYr{jE}!_$D(=I z(5}MMkQJu5t>{_4nLpzq2Z0-)F~5DN=r^jeb<#SKo1LdRDGJ-OCPy9>OFB|g^MnlSYB zPuejubT9V2={9y^%GiC+wvf4okGh&TaxJPZ35L+^NcX`r)F5A7P*}RRx;&{i7Vslm ze?C{AGGJUvP_ST&{OP0|n3kFlol(;Tc5+$Z~PzcB%JjoVl-ejmUvsLOt zj3uzxVG|P`_D^03D#Pl|YsRqlvz_+Cw3G7RTVT})D+TyVLP+8gWvP)5qDV6LVPPl5 zyYTmCj=`62xP>b<(K!vVj=9C%pqR8zD})JQ3-(p(fKzD zn(91;KpN}BAkFq8=Ek^p)(K@3hQ0Vatz9i@>Y}!#8e7+&j?EJc-U#RWn&f>5vVt+v zhThLIbw};GtBQ%R2*oXX`j+}fq2YNFcg;zo+YjrH6><}IQNFx)*<6Pnnbju?fT7V8 z#~P?#W<}N9@W=<3p6{LBa^a<^PY=AXQr0`R$7Zf_E$h#m!(*GWSFW}vuKDcF+~q=> z`BFALx_F}p&KMTGsLeaG6SvmDm^8RM`WLydP_UCOxjm+F(2uiHB@F-cIRMA*Y(3)4 zC;#}Dry{hAlweT`h(M7Z??DgIZC$^OeYIr;AmCfK>|-@Pd}E7%U;=^Cp3B z|4#tsE*a6wZh%bS=|ilozWFobq}NoWzva7I%DlT#z8MIqB)*%f1ZCQ-)ZGkg!E1Wn z%{TE1_4XgvqI4Q&ZSV&mN!r9m)Y~MeWMfb9>+WYs0BZ=$r5xt5rh)Ji;VtiH$%?FC z83#=(8iEmfO(zkwTYTvVs(0)&f%20%%qM$ay(U0eMyc zih1`*lcWMOssFCzRVf9%5@1L&ts3+mQ>Wt36jF35;Y?w3R_z_9+=#t5>=a1TDTO_Uyls0(Pl_H}GKc?F7m`chcYqp|9$Bp$)cnYbA6fAuD}H3fkF5BS6+g22 zl1Ek&M4BRL`+j5v)a77Ieqy!M@6Wgsyv+z~4Fe6!?{Q)!Nf>D&;wM(wPplLLvRjq! za$Y4VSW*x_uj1!bL9h6E6+f?9CFbWI;c?Y#4`D35E21r)!9X$N8O*zeWG_ftMyI1IEo!+oALh-fMWP&X@Wl&)Ud~H9T1ghzm6%yh=g}YjY#2_vG`iH zyo}``S_Rx}n3A)W%WgwjOA7EI{syc45IHtb8z9{^5wuK<5l%% z6;<>~%vJR>eekD}uR7aG7OZ{yU>DUVok4@LP!-j}oEQAU`nOMfu{p;ttbfX}@(b(y z!g{(3>*DH#^_6rmTfI&k`+|tFVgk4s=9mS*u9o{IWTXL+qBMvxpwbkVqwS|O#no4} z4r8ph6vd?&<1IyT6l1!jC@#l%g=vb*uj)LGaVgUjk8vnd6jzqrj%_`~qegGx@`c0~ z5?@Fu7ZSo^bm}|4koZF43n|?~V!5pOLgEXFFQgsjF4*}(QeHJ3UwIj_=lR}m6|)B? z-wy=P>n85C7!q?Zv;Pnjb1L`d2$s3{^1N>e09AzE&m`vnxN-4)0>HO;U6_j^7e%g% zxi0?9x>#e+-*Eukpg0G>O^b5?rkj-DdDTFCKHxElu&)i4D7f%$3JRtlN?Dki?khS-JAy%7ZHpt~|K%;L0Ow(r~Uk zxboo2gDVfNJh<`*RUSyzzShwtR~}q>aOJUKajraq!f)^@$IkKhD<6IGD{*^b2yJU6 zDHy+>8_ZV{UrC>4CB;1k!&eetNqi-xe)@!^s^u$*uOz;bUb~WF9ueXziLWHSlG5e~ z7Zr)`IPjJ9x|Q@6Z&(Gr3Q1Tm&1y(7p>g@{WAepwdE&_Yw#Xd!<`O@P<7aXFERLVW z@w2$~v$)Jq_d2mrmceRU=no;!d%oe&xgGUh3voZ~DD@rlHFG;^&^;QSBw$F+5aXY1@>II!FLz_dF# zm-3rOdT7+_zHw(;wp+;Y{}D8-2gTe{g^&-kCC*-yb}R83{5g9J+iS`F;tp1vF^|ei zZeVnn=zPmiadG|%_qMAp8||*W?Mkn7ig0~4^^_w!s=R9sDvd04FH2PIrER{@YTEjX z**2%X1T_q^-WX8nl&HYIP;vd1qvDKJDz%jV3xp%nG2KVL*~{T(b(GeQn>U*Gl=+|`1 za{nTR-YOaueR85#+=j)x#%A(hM0-*<9{kx6^FUrGdaAeEI{Q`nmb>#icx$k!&oeT! zQ?Z!7>;9So9eaFe;9W6ZOh5gMNL;Biw^yk>l>D#JB&SaxtX2raBxZ` zolTsm(e{Re`7f@~M5X4+Z!kC3N5)-4XVZE#Fq+rn{$pu6A>+=y%Kj!-($%y`{~q0| z7r(}Y+_3MGb6>7&)5^;g(dDzJGGz4H@DQJnJFC*1LcRUFrMcxF$M^jvrGKt#kEOy9 zsj&J)`mLh%TeSwc6;ALJvih|G{XgSe!gU%?hzDP^XE9xA<6a$4^`8HfewLI&JTvPdbbr%7d*O2v^PPDX|MjgR;W- zL)$;aq<1?WmC?b$)q&vhi;nrFOvmF?gi2YuAP(an>ONknLc*Uua6lJA4wxg-4q-7{6uZ<86vG_Lug{F zLPVkct8zf57o&q>;qtJ4T=;#04oLxmgR<~YyeSnf;r$U&P4%FuHBJt$3PRf{5}6ba zrmAx~zA3ysmW~NfE)I(YsdRH+Xq~lA$fR?7SS}o096S{s(7`Dwlphb9!q3ZbrEqRn z&WKVl4yW4F&w9O}K8YoQ`qoiVZIYW-VRU|TPQascR4xo}#&)40I~Qb9oE|j=`L6C1 z?t7(6B8=ptsc?LMGcKIztt+BD9TdwNc{mUY_(J_j(D7NZsp^koRB)81Ya))Xi_=2u z_l;Vp^+iHno_-w{h4J&@LE-M|=7tER=i{=5D=)`|W7WAOcx)awwNs-}DoEAiJ0exO z$5ZvDetT8uRqJIUmc}Q=!gJ&0rcnK<-jlH?os@-kvvFS_!qYFJRkV|)_F~;W6^@4D z1HnI!Po}EcIjj^w?dp+0`SP@=p4l(;f=%ovGPUkb%Y}09wpGx_$2u9`{yuFARp-zy zP#!%KRjZv&HGA;l6b^3G3X!|*v!Xg3HO7VM?R}NNUxTx9L3lY73)ainAk#zftXVjp zyr9C9c~v8Y0X~}w_hLgWT;ADrqUwjGqE>@<2L+*c&>-?rsZ>^b=;*jGo>oi(CZyC< z|Fw5OYw1;|t9uQ27%Qg3Sg&A!t6jU#YyoYBj5(zAi->YneZaKjDg$N&@{AzA8e}x5 z!6|6Kydo7S8rC8SXSLzHEUStfNvEv-&J+wSnR6<-W&H81e#@>?6;fp$22=s8_ttdti~@pSq_P;p9T~b6(u>tHU29l3J3*;SYSl7}bFFhL?Z5xJNNIb7uL~*=mf2x;N5ldV{b~9R z=9Ezq8O&%BB@9+w0wq{zK7s-y$RmJJOMKgc*3kRaKb9vqe*gkfcBx}kY%X~}3cgb` z-z^CItq>2aLOn1lTK`cMk;<+jMSEmm?vc?&EzM-{!mPBixn#?m8PJ};<<_`w4s_So6=$sl{iBvUrFf<`{JeL}4W8A$K>(hh6Y_{e@t^L8u&wNIW1Ak zXwdBHz_@MRfVa2c#y{_jE|*m zBzW?y^#otuZFI-RgkyH<6b8C_U;hK3xPdqC)j((kd>d&taV{LE6hj4Rc_sa3QeMJ| zb%UjjBnYC{e$uv*+wA9-#ZP6{A~DIVWfa`3jWcU?E!?aHzFCVF%~}}DLs_?4R1SU9 zwrzLpjCL(}GrRV^08-9?j}W6gPK}R=Djh8LApsh_rW2rXO^tI{Tx?*^U=bw3BH*yN z87$7ceR&ZUSu9T&EP4Sw92nPtQBrmVjUcaOK}RX_R6ZL%y3KNk^nd;U5D60@5^{)q z9Yo@#W4{1(%6(I3VwltW-F@8(eYd?GHOt^}A9 zltnq8K}iEq!g(o^EQ;x+(d|l7(2(DQWNBQFWaIMq0ptha5X`X?(BX;`EwYlJg*gJYt+pRf}qRUF~-zK_I;zB+BFJ?cq-+kDtqr zkrMqFiTg2eevBru(2h7iMhUsZc&Cp|r;$5q*!^n80LD)eIEzrCvbZSKKm#aDCR0guio?Q8-NP!DfsE32FG;L-eyGUBI-6Lp#=? zV5SH_3Yy6Kz5#`lNP)5~Upg_GIKR<_u#y);*F+(r`dN;EOw9raRSC{#Mv`TmU(Fl0 z*z8*MD;Wo(eIF%}Lf!A{{I|x)=s)ko%K#vqnG9az_NOqDL5P`rW%rGZ&EvPbG+Q++ zeUk0|5G#>$|F>+mjXQRw#c)s(>`23U@@S?J*z{ z5mVf2Jz4DgQgiDK%s;Jm^Lm!&X|;~k+Wo#S{d{5=&w8a5bU)8->#g;3rODL=^2zSY z?8?4`ij0voH{d`XX+R9i@$xSAB?5rul1bY=ZeOOt{T<`^LIMOauLO{OJTf}D_G@rm z{-|5JP)u6BGYS2Ul)RF7EiKrkW#n4&O(Q8AIm$*pOAz%PYI&TTLk9=sFfS!2>tBLV~fn}%UQF1>5 z?Sk_-@%9w}sZ6ci7q8ms*SO)1gzn}Zcjcf)M6)SR*^vRB}OeR7n zNGZ~OpiN_6R82+;oBtw4AwV4rD&lZ@p1&Tan1H}K0x-?xRAs~YVux?cZ}5kvDM}u% z-$e$R^0YbSa&QglY1Y@sRu zA!;?H27`!56|f8jNgk4i0Z}eerv76MKuJxz;3FQf080x#!Q=ARd8E&ksryjWG$gBO zS9M7BezS?M9wdfAf%VlD+jW^mpCLKj(haHHZ*+0#?$}kJpSlaKLfG<5a2327KFAf; z{mV}VBqZmPZ|pBzmPt+qkR?gkto;PH6@+8};BIRa-JZCuix~#jlEhRtxM9hkhzLG% zzjgb5tCGT~-pr8iwduIQKLJa(L+_CDnl*p(ognKL9_*klYFF%&Z#6TP?OWH z?UIoT1Qoa^TRcL%sTuqT@+-$C+O z{tQg1B9}8H9ASwIGMF2ocjuQjQT+B3kN)BMW5Qo=6J?(=1tC?T=oyQhI1D@eWaO5m zw{?!095eR?Goh4McbHt3B3NSVBv|cCV%$`!^s;W#UmiR6%kE6X&aec3VRk3v7|Jp9 zqejk4c{#&=u$Whp4b>n98;E(FOdbSdt9dy?*pZYXT6Pwkjq@lu4nDYlhx2kW0~dn_ zQrzJZ=NwFq_RktO@6KV;?wvbzQTunxGZsO$JW)v@AyRZS@zS(J&@9hJiU^MA+iuIe z-?0{c!>IYsr*Rh&n~HfY0Mnzs?leAWc0{FulDHCR2f8!PjiEbi%OQ zoky0Q@eQBY^}0`cE-5$?mZs_QP!cox37o?kGBQ`E?seC_MA0l1xIAE5t%76vSjrQ#KaesF|O4_$h%F&LtL z_t0;D)w?mW907sBd32IjOhq!vkD7-w#n8;dBqAwrh>a8zy&*~}(`7R>!Ac(CP^YmF zQN7=6`izs*brqw0KrwIvqjf^ISP2Os882M@kTfkHec@=ma=+1}Pze#jG{b|5sZ1dX zw2vrhkz3Xx%$9ppl7OPR=SO}*4x~``IXdWYy?Vb{oNAa#;xN^e!_+bj6CzPkl!f>b zO^d#QwO+TsKPILbqXd%1D8Y1NlrmoI&?H$|xKop~WY??ro6Q#Ig_r=%IZIJWi;l5d zuiI}k5x*pyl4%)3y^w?zoStV9p`232T+CP$v67l;5viD^r1&uriRqYzvc)7~4JcSj z4;t&$`^~~^IteKdBb1VB>y`VBCPFPr3AHF2)G8VVl9HZj5lD-&DRNnrMO8{GYU`Ez zjV1y&N(tO38*pOyGv--U0A9?c70vj8WW>f(xRu>;Jh-cY)<$j}5lGTpqk;IXAhDDqx ztT-|w|9)iCKkf~6%bXg001x#}b%Twf54B~vtIUg7l~si1QVFOliNuZHTG~zOMTF`9 zFBk#|o#d$(gWtG-BdQ&h1v3naVqR7EO~6G(L{i#5ek%F4?D1C+@AUCe$#)9V-#(O5 zQ=-(AL6A+lI4+!xT**eRW+T_KkuT;aOhFHloTlWvXGsn|NZOVME7Ow{1QkoAq@q$& zQE92DRyqE8Vxe}6R@DjpuUT>Y_c-iq=Wc7I{{-+wS)o^2YzK&)mRW}GF#AhU{AaVh zr-4A8xq{yN*#2WddA2j@Q2k)oS8k)!#2o7*Q%F9nYm9F%ft@Ur8 zj!74Ej+(efZV!b1h5d`Pb?vl?F6s?csEtc4G61I+MO6A_js;`-G$w=o@M?-+qgfGN z>Q@SBX+zSKPbcEBP;AI2q+@mNiWqnA9br=jD+!k4_$%xP1&n zp)CEb5mO&Lm617YUkNAriACDyo!?Wra12|*!^KOF^h)Tth=!xKDAdK15$QgkREmmy zCzXWp;C4!S=e24X+gN%M#7-5F#!b;^qH*)g5h{%njaZYLT2nbWun&X>ML!_jpY=Lz zuYit)tKUaQq=)Y6Q+(F2uY|rZDUwn3v{6)!Ep#Icu1}9i>#5Tys)%&Bs5iX4I5U0)Js+%cyzys#ZXI~rb+ zK61<{YF|D%!ov}~ByDv-zhm}y&7^Vs_;1#}tg*kVtUv81_V)?< z`+$A#sI>q1`ht#6K5Adm`RcI0OLTmdBBcB{&?tY#gz|&`z3JcRUmxHI{cY9@Ck;kYAk^?;^5jn#jD!?G zR|$CQU?hlugaAl+KDc+$NB|)XiDG)LZ6_Q_k$|S4)Lh%Qa3qLD05imhY$+NEDhR2m z18epz8VQOj)D}|izfm}nDk>^iP;?(G90`dSASFZO+jioSWR`AnA>i(V#Unu|N!mi_ z+grpVDRg;iTH0X2y^BX;;Z*8EiqW@-N22BzwM^8sEZetuBuSO2vIOZ^wyk(11=B^7 zc46{(Bp#3SCgBV> z?mNr3sEFEvHcjZ{r;_il5^wimpE^!Op8ZrPO~(DSl=~T#^d4^<$5Ev|YKl&coBkx6WqelaphT#8Jpm!73-d@#Lfj&JVm8?}sj9dceg znlz_(!cbJr^Hz!I6>!>=PQ^P%!>6J~Zt551sc3iG2imQ8J|X4us9pxgqbFPYRT`Gb z!|m@*5sc6Ay;dJR49M@x;-Cr5*0ZWPR-r^H_XDvk-3nSuLu$t)wTnkQ6_0+8ZnWcK zStGXxhX+&iq#sm-(v^Bi%sM`Au3U{j;)M63t`bzSwFL%dB5C<@(`7 z6@EiBCGEnmpHmr;;#jEZKM#mqxwtLko63Dlu(ZZc(p3xhP2B2QPeQ*Ao=LwUK2EU) z;g#^xI=6{~yU#`C;rQ%WkRGdm44)07tc-6TkziQ*AsGqh^(iinp6-Ri!s!{Y$1Sso z$U|u?w9xRHG>k&4h;_AF63$Hh7wMh0hl;vin|0Iy{g+v;|yTMeEO3!scR8A4;X@|oyz7(HZl)tB#v@TzUO;oEtJ3`^` zNF@WKJ}Tnr)ukx3u8uU)9^8zYatU1`p(Gs@Nd4eoTvRGe^*|UN77j`GZZs~-W1^OX z@>Su8nAO^NijEcaNr@~7UIc29J9Q{6~=x)k#yn?^h_liqa*iPk&CPiv4&+`DIM6NbgXcmSwegMfr6;y(Y~YXF5eU zqbpTVk4HDeX+DcZ+|o75FEY9%*6>nnB6O-zeqD^pWORT*QTaI;P=5XD|04Ed50vGG zF<|`aKaxgI2UA77s8W9QJ5Qu1pTjb~fK|$`c1I_zq70j;VpbhNdudk4;Letcczpd+ z6q=Q0m9(uVsVSe}pOjyZ%^De@GgMT5Tf%`bxT`ft_f$ee2fbl; zI;j+&Z`5CEyFf0^&&2WsX+~YOQ;i*kkREg?Ut){dN=B<%^OgTui#;$^Pk-#pmaKUF)g1 zK+4YOWhz}YRir(V;xA&n+(>1hI>wDwY+O3zw>(j&;!rHun)E{ZV>T}ii}Kj0*M;Gc zeMRcU=i*c@nJ-sDtL!|ILBD_6L}up@33_X6lK!Q7URH4bs3qJ0aX@V6=T%uLkIFZ~ zDMX00uMASe{b^GbR7oz9rZc*o;$um)g=tB5)zinXRvKYcnD4#~w4>wlvtU)lDP zP&w?;-F=t!-5;)$r#g~RECOswIbjYnyLqaP%Ivj*5qzn#-^$s3mPaNk=787o49_WUJ2S+vq)NfeLTg+wR$6{ z-RUuD)r6NKt~{#uf_havA*~zprHS>r`Xneq`INMNp1w@+Q&+7B%0aV4TIan9Wz0ma z3)q~VlcsPzDdT%ZZ3);aUXUi9Oq%%aSak$hDqoW3=wUL&H$T;}AfGp{NVAWpMNA%4 zQ9#P{CuvrUX%k=7R3xBb@tQP$A5EwD!d6v58kcWKztItk*cN{t2Uw_`HALB%F& z)AE9Tq&5g8zi29ha|&B-eVtN;!jx!%13_z?>!jJH^~=i9gD8ybTS$6Uxn7j7RYbqH zCx@gfwd-a1_^DJ9l%MS*(*0SkPZ6FzT?weA9}~NC&?utuwS6zZ%j*-;Q3nm$eiuCn zQ}gJIbRP7^6je@2b>XE1OQh|bnPqfWe`*QCQSY3zPeikcN{Vd@&Z%)pmu178D#8PW zweH~VinPFWv#f|ONEAB9r`M!)tu&iT?`KIBT0Klivt_r6O5Of_Aee{4TVe{oT4m*- z*ewY)+q@&@{ZVVGTp99}P$ds#(in`aqH=`KZv?%4@ryLhDikKsuisAs9nu4-SI?~} z9*(;e;a79=L`D#_=`z2RTSEDw{hN%g%r;%7opW2bY3R>n*d}ed%#MDKh3nFFg$xR6 zr-)ChT~W9k9aYKTSGUuYRU{+f`~=pBGkWMu<;nfU0fo_Coj4~&yDZxyox)<#plZh$ z+fCWH=v@hiy}KsqiPdgVzPIoVi-xmE@A{%!mM;!3o&>c6+oao(x=s0@LSY+U_d3MZ zTD>BIQm-yZmeD2l<87~uoI7j_;OeeNI*!nrB6E1*2$Lp*_0xX8h@Q@MQ5c_N3hR}p zeiL1{dPo@5ha+Oall~MP7bvXv&&)Bg$b`Z=)+vnJ!-p5rvT9CK8NL6B-}J6pf;Kt3 zAYI{k(v(k6DvqE$DVL;sUz!$?T6e|*DvYj(Jpj`(nkW>88^h5pv7h^58J}Y9cEDy#IR^U(vphQPLr2GfeBu+?JUA}N9|!BHhxWq--iO#Y_CwozG5Y?W{wsu*(Z8i; z)GyN*=i2&<**2$sXk5ea-uso9N%iutZG)#~v6?L>n^ONvCb7DsmgakxhVk6M&-ZkAMH4qvApN;D3Jnd-3|H{6vm(XXUHQ z9JxQdEFI;(eJ>Oqq@zNiST1@WP@cN;$3MU2zBLAe?q7w%cs$OJrM%s*7s|JV+vB6} zjX~Q&V!@%(SsCQ3gDM5{AMD4!e>4oe`uC5vG0=UP?NOS>z<8nRgRK_B6R2F_?;p-! zVi`GaL!J>7DpZvJ{oRL;MT`+E1Ex&QZXKMHQizkd|G z#-8o!Bd?`Un6ZPX@;er@))YSVRETW349v9Z$Hs;{Z-+uynEbcPL%)^@U; zN%U*8wj*GSRkSv9t9`3)m2Zu$io{?qM8cQ$+pVs|?V?0`s|dCg0SnOeC6(G>c+cK4 zuTzcP_|7!^Uq-t-nEdk%^+}o`Vpp<$JolM7@_`khH;(V$`Q+^FYsC(n&lDM}McV|#PteQKqSbLvc936MCe`@Im%Y~BbE<#b}eV=6Qgf->bb7YB1bz;&hF&s z7}z&^&@lSJhkUM7%T4TIuA+DTG04?uhjL>|!0Kxo7Tanw9qJaeS#7nx-Tv>~pkcCI z%SrCpprn&L7mYm>4bc~kLEkj0x%SW+n9n-f@AuCTOezpVhfV4%HX;`UM=+rn+s zNrdsDR|eK3H@5pNI^z|6=on0l^xX5Xo*SEk1{DY+q?Z5AId-m;>l&wluuo;pc9CpRM1B<5xdA&wVCNS9`@ihRg<}~1 zWM*Kty+$0$-e-dXt7#AW-kEOK8O#pUtuE-=?0j2cXH8aD^!}2i;7Z<)N^me1oEH8d zhTfY0?jMCnv$Oa4QCbdu%XuewCHM6ZNeZehtkg?KXm{#VTnbjZ?R@u#^!LC0e;&<% EPE)HV{Qv*} literal 46081 zcma&N18^qaw=NuOV%xTDPBQVtwrwX9+nRV{>y2&Owr%6)hyOk2-22t7ub%4t?%sR# z>aMrCp1pcKYXg1jt#HdYYu8LzZJ43=>hulb*FA64!UraFD|J|?tidzos@b*ANNUsJ zpwh@?jVuYEvKW}C`UJukl=7qWOXQ3%)Gi78o@;N#yKcscJ^8uWsLtE;EkU_1_45i$ z_YcMArS9k9^XKEq`{<^?`Wt7cDPSI8T>Hd>TX5$6ZqF#yL#RzE!0Y~cu|7zV{u_4Y za`Y;MqO9NRC0|d=$g-|Oz5W(b zWT)Y?Vm=<3ROTvc)^ZpaQ0lr6+Mw8{OG^Q6NgJGN!nWkz?s2~1^U7HXH()!(~s zzL|`At%cxNW2!_gxe*NP62d_w)D}S|kZRQssItFuX#nB|QSIr|)yVs*-o<@acb6EznFjfs09YLC`iMTnKM?3-IdSl1BADYbULr*N(WR zKX%q6AL^#;GNK>5L3^!@x;93*X(hJ2@Dq%q@3vh_K4g;NNsSwMUt^{Xi+}c{KYOL# zz74capA%zY`0)!W?}tPk0OVRvxg!Rq9pdr>`nk0$v2Y=7xi0BdY_5Z$ z1C5Xd!42%C+rRDhQ%Yft@TmNb++I!8mlNj<>j>I4o7hmH%1*kx-QyA1a~sz+!3urJ zx#>4?UiuytAdOlR@=7W-8Zxfp5Z9&VAb%PRj1$&RnjxK9u4An*r@)**UoSW6m-pz^ z)$|xbQMt<(N4nIi`_bk5-Xs6a5Liw#aBIaoV>b-b4Zphp{<^Iv^`j)1BMA1=kJoYJ z<^$PhvOtw=000{sE8mMg_ofNyuz4(+Rp0)tU5_r-kC6tr>)^epVpMPN>U!3NYb} zelh~J!xWgPM0)J8R)ou6J~u%dasT$2z8e51UJ0kU@5lZZ26gKUtrBd+`32J>MACPM z+2iM%KpVBqVn91UI|xh%)Q2EMu(o(|;BsUL%y#2!l9{`{eIdfJH)5eo&f-mGQp;SUJ&OW@YP+v*|_kh+B5Fz4PcFNF4}j|!0!F+5y5QN zm;~Mo^b3Zv2xf-C?EFG{RN!^MUpNM}_JX7>_~$8K%w`u3{RkI9V!jKe`xxPLvvbEv ztg(Q=Ix@q8?m+j=+5-i|f{IDmgkkt`{Dl}uTUw_az2Se(3hxfXIVw^%lU;M%zJ@RY zIR+DK=PodrF)$BMu^_-up^A5bg01n6ki`+@|GK2`E(YEf*DJ>p6WA8$3)07vXto** zU3gWy_3&Q~LOGB-`3P*Z>_2!t<1sG`S>bPa=5!A@s#$_H`yyz?CJc%P3?>^dXK z|J;CCJ&0<>t;d+S_L|!lo6g@znbjnT-gZ#Gg}!!Nfa%0kYaWTf{+wjnP0*4cY;3F7 zf|X+<6lH5a;4$GCBTgS z=6#@v>$3$nq~`nJY2R|EmuqY2?eC^9u3}mkP0!lX4N*i<7x*qyCxbIYCFTE-HuOW8 zHJ&UfeOUbCALe@fhq+?n83W4Ch_W@`RD+V1cL8?zUeS)In`4~clee9)joqs^xkM;N zS<`}6wN={IZ5TaulLqibf5P~Klq}D3N{t}Q64D7+v1WU(XfLX>Mkhy*;?XV~JB*VG zGiKjj80@3lI=G{Tgdx(n>bGHvQ6)q~E0b%IN0=jzG51K`(cwxsJ~T)@mwRPXY(5iV zsJ|H}|0+hH*0oXziJD7oqz(D_m$*uXg368{k_G-Dt{#P!m4&S*)S(Vof9IB`mS_jo z5zkat^J^9CCRj{+s`(WCtAbLyERxe|cm8r?{B&9cf|hY=`*Gzs{p&Vtuxx+}tRlNB zc(J`0iRUP!tn_fd$Hd7qtafvyTUNy`YeJJ%1LP*lPMaSGEky|PbAvYC#EJRHeat*% zXm6)OGZM=%CaF~uGc)8ed+%o}q0Z=Tyj*XgxMaz1?gluO-V7s08w?U1)v<=eLJ=H7 zTrWaTil<(kF$JRzGUAn=OK8@)0Fu)VM734texk<;2wI@%| zFqL{?W`|N-qhgBaWaDfy^^M9%e6bgg_HcKm5}ALxizF9u;8?Y4oi#8sHGZV_7(`1bf?}6UpU)DUR_i@Sh+n0EW1R441ag^ zB+wW{JRGSeg5IvD-3fRX#1{^p^aA2611Pw^I20~t{2JpNa}~_X49MCe-kP+GX^j`* z3I1tgG7=CA7tGw{`bC=Q{8IxYdQ_nQ@Sy&=lr;gZu^&6%F#D?ita#TK#|k#w|6+$H zU;k_PA9$GUKChPt{>8D~ku0h9mAifg|NmI_O+}#v$oenf)2!5WAYn9D2)hBVXfdwf zUUs-zHHKg2Z(%eEJ_lk#^r3_n?E*m%LADG)(OW^b0k<&~bW*l0BRSSanKJ|gHLNgz zWd!;q;0+ON8EM{Z)MI?ZJJMmD(h>9oKJQQb(5g>a7~mb{nVL8IFBW#=L1!GJVuppH zM3f%{hF#4KxkiQn26qE>GnD!~x*F)lY(V&ngMn;4)ctsL25C%yZ0QkgNqs!|r4#h- z#CT2QkbAu&2eyycLk|K@S&gAFJLz`Btb0c)?U!!#GU-i(u;~ueZAA8qWauTlJgyZj zgEj)YQ@Vd-4HHNA?&*F~kIMFP5&XfjBNcWX`@=NG<2)t6RG%v(81Rb!lB-W4nQvR5)U{d`nLP|TV zjhvabPIkMA`qYDa!z~w{7~;CDRNbc4gGxrFayPxwm1N8|xl%s7$SC5_&_+|6b3}{c zP^@M>oA)C!=2RzTNu1Y5?k^u&WN;tQ;!f1HS21`Ntu5lU<`j2lsV&;j1<4C*YE@%;Nz?RY3B=u{m0G`zw03b$2CdxrQ07`u=Qj@6A7A z+2gB!O~b$ey~{{hrop@QB9dkG#p1BGYRpy<6m8d{H${Z$67D|Fwpy<;;WnX*84PMK z@Pk$%U32E8pGxNpEfB?C?;l^jS^7%T0AU@?0xpl#j>&|}x7g9| zAIzhIbC5|4>}9>;7Gs*pd}|a68vIrb@e%r!WAiQgUE&5z&=e?z{N&5M>gQ1g8YktRD5=jovtD!h{cSLv%V(VX~jn(~59%zQJ!Cg_GbfBvBB%7a- zgr6xK*tnqIkfCcy9okDhV`z`txK_G5PacO8F|rHm555x~r3WnyRd zcE?707^PiL(3>_?g6u!H2LtB(nCFU81kIzyb0anI8@PBIvsrDIRrx2M%s{ar1&vzs z!f*_F$760|kQKq}yPs>Gti0jzEJ20ojKhpmlZD7uOGA|QrJ13fof;u_H$uJMb}{lm z0k$i}@XXpRj%CRYx@t>~dWOF0b{V>yQ)E`TN(D3WeZ}VQExOnFV9y=i{IdhoORbj3 zA@c|60PE|yS$9^K&9sq0v(F3VJfF%M&GqlLuH~J1<)7VyU*Uox^+^eG5c&HWrIhoF zWKH;SQ8sTr(lkIaG9%>A5D7U(I90NfnJrCvvCISz3|D?2ETSlmxuri8&l~_1dz;D0 zj7crI!LzTMD{baIeEwO}jZb3N4zJv2((C>d$%rPt`=-&n3BKixtWN}$VA99SOzR4I zgJEit;KQrbegT2K3Ev@L)K#7X8XQN98%GPip0U=mHm(O^G04j9xuen7{Tq%JLQ8iL z(gLR*EcXZ1?xPP2(@ogvwy`N2W>2S)A;0!W1*DT9|aLLrcy4 zK#w8*Ec@##+~XC9Rf6hq=gL(r%xCQAj_S#KFvId~YOfC*?egE`s#iU-i&#=5k+_>; ze8)2s)$>6$G7qP!hqh<^OY?SvK|xpQ41wAR&soOZ!cRmPJ@oZZC_1Kj6DYacV#}ne z_H^O{#|RJh2ZaO7ll_$vYaNG$22CD{;bDdj*TR5b`!%&m{@M+Q*&05&(US90GAiSm z%VDT9@5sR~YNgE*w3GFdgLv4QLqkl+q(69u4N$UUvlzEf!6L+D40cc~i_mtEy9&vE zQlUSkOYb+K-(+r(MewSzA)v?7O2pGfA9D3^ZM0lpA#NcBP<9cjYe=DEXjYyQT(0d5 zhoMVJR|@#C{U~FL*f-pJV9?N;zCp|dHwdkOK5_&E6q7C0WnZ;;C0OGj-*~jhDXbI`vcPcTDq;& z9gsy$80Zpd1vG(*5ViR#2d^1Qxb_-M$NAGrG)px3lHgKrWBXV4Y-k41#>=7Qq@C90 zpFJ<>{XkCa^lAj(39-vUOHO6Gha#9zFEAY<*%*4)*I{NS!>E2 z6d=D!aZKQ)OOL5{`jhlPdB69{rSj8C3J=^l=7$ z(7`ZoI_wdCD#z%lG$xT_8`LLSW7UGxhT=(VTD3R_4iW4LI_zbI7T4`Fe5gagg!p_k zuGLu&-u=`^tO>t>UDq^&(#WnZ=Hq*M_$|YrAd1gm6HjlBHVERgb!zI+wLUH=wU_b;Pc56T&&l zt>4S7iON;9O+EgV>Zx&yI;{$G%l!Q7V+}2L<&yV#oftc4;dMCwfvgBKByWpnq%`Zj zZ(c6`U8{0e*^MA-R&zr;)TXWHhDNbN!#g|XChbaB?QLXKriT-Dq!b;Y=_8gxvqIGz zaU5xpx({W;jrbYG(0su7f2`zJ4{7#lH4_7`jQEh|jH!ude5s^F*^jE_UBhOX4yhw; zg5;0-rBg7ysc2lG>Ubj)^r5?Y$(`Y-1$_gH|r%xy?h*I01`qr_6?}#CQz9GtK!Sc}B*dMZQFw%eR(fJWsJ)BRW8=Lv-7*gXaS ztf;sAvjKqHDu1BOc)8abxnRH7gr*tSgq90&z3ax5q$V{m0eV*K30VQ8?rhZW6i0Va zAuVo?Z>+V>jRsB&BDzV5bVsjE1HxUlJn!JyF1aqfT%Leg5^scsdZh#V3V4)_F36)_ zfSB5MYlJxD>7P%xj2H}QlCj(gqV%U?#q41;;!>VwmP`u?VE5q${oy6ov$G8C=93QM z)Fv4*8mXZO7ubjgzLQp!f(jj?dK;d#Bu|}ko-MH}=M2XJyBoR$cQIyX`AXU_T~Z?S zX2mMqS|d!f2qT_gW;t=#)^U5NZl8HuE$9}-egtIgBI;bqHD=ni8@ffJ zB)&lhwe6-FJ251&?E?UqC;kANPC-W=_nS6ahhvtWW@L0(9ijDv`!SEE40WvuGiMk- z;9mNdpw>$GNiDfdqVsy8#=xmLD~8kMf<1P-5wmT(qOwvcbV51SUM4~A3lH!-n-SWM zM|8*=b`xjEd>>Uf2+SH|CQ;TYcfA;=vd#$gh_X>9w73jZ0PBnr@w3@w1XjxTW_3YgrUgP}s)EfmL%N-3tzq81;5y&JIT*E_pTsE*YjW z?s(@JW14ZEk}Ry^kJfbkALD++uOknSKesdVYKoKZ^uPo~}EM=miVa(Al|EMr)SnYX~p3{p@GZuZaV2%w%()SzZs*1}5ViRxxAb11YW+26+L;}^=3;}g!ks)*dXB!V>w(f7 ze43DZ?yqIoGeoN^qXLJ40X_>8Cb1$YCRVu(R=njdRQbgSv+YJWC-tRXk~X_4c0@zw zIQd^UER>#i(YWzCmW;~`oHx-})}8Docl(pG7>lfYS|VM=WVuJBo3|K?Dsxe0Bk`!J zDxx+7*kCYiU!4JreGj{0^(Xj+3@imkiIgMi_tojcF9avsL83;QV+!K}|F7{tm3^Tw zAhI2;Bq-yyay+R(>)(CWD7Yt!F>t>p;Dy$t!Vq(ozM7w9naB!?jX#N{PLibp34Ni+ zgkd#!$a3R*D-uJ)+mevCU6qKz+-b;!l{&sGWl~(E0vSxw9eU1!5@^%1=rhc6ptqUr z0bzA>N+*LJr+WbjyujvmfnJx(DjJT>s)g&Ew8xyqRYNlB0yc1}L4k;b}xnc!Aoz;jNMJXa9TW2u5yZk=4B!bN$ zmX-I|$sUs-)W#SfC|vKSk4-d9xA(&@I}^vya}9TSw2;1`^CG*IoeY zYDhdqA&Ty0lxt~^2fI;3d!d_r^IoQI6NPgr5NUyNMhDqW-{`m2ilc%=I=54r<>ld7 z!c05U4O{d^kD`*4MI9f_VI>oeU2V*{J4 zZZ^p-K45Y!dXX(7Gg;J3!Z6+LaC$qw5Z|+*UEO@e*hZK5mmB#(Mwpp#o zy^-v1!mK|Rv)RJ0=z3|c)mz2d*!Veu<+ z{Kxp;t2|TD>q5Ux6H8xU5)I8mWq|xMt5u9{-v(F&Tjk!KA7(EqosfM*H4oC0=zUaE zdOqtFM)qeV9?ejS74_${{p@KBE1O_QBqORbJUemx<}Mm)h$Afu1^e4MEl3)KAX`}P zy8#kh3U+2!>?CDi@S2onXUd2;J=x&5xFY;Ya%j{0piFeK6s*V3P;YUCLAz;F3R~Jq zQhZK3$pwQ<9`HYt?5|^N&~Dxz-(XKH!IE1Tt=9*zubbs86^t#{&s`<`Y+JEwr1XB( zEStj*ymGzPp<_l*;Q3Z}$&<2a=rvWg;(cc!So)!>!#USy03{{AV45Y#Yk9=7VSr(- zb>Ieq{PN5w<3EjYoysRE(}51~jV05A)A`Ej3vGzDF7#nfn1qFl$l<(eIW3|ubZ9_c zZ#km^oIYO`e~5GahP~2@57b@U_PX=N@xFy~0{@_n!_b6ZZXt65b$!=R#F!q)S4o<4 zeLn~4XIm;rb;~-miXM_Ro5xAt40!s6p`Oi>oI!UG@fjCfjF3LQcN2HLCwK=snhn=)fGkTc-B+bF zmF$gWhxO@d4rX@&WS|?H1BkssfI8s!sgthb@%-{kbvR5)Ss@Mwe6`eCIn!d6h;in7Gef(FUH2YK0U>5SS6nVU-}TDwZ-xXlB96qdfr>|NNL`eErIN_u~X zt%LP=!I(k2rGcCkHqOSj8@6N56vv|yb;WL`rvb}v#2H!E%txgK$1|Rq600L{Ct^U) z7~J7s^N@}fKbITe8-uy>O3Lele7?@CVroXq%>ZSJ*J>f&Xs!Dl;g`C~6k!wghTt4l z9>#Vgj|1%+E&k7u&M-!qe zabeRqjUPoU4T)~g-Uf;rxmZzeX~bf6CADyU@Dt1wsYV;Ovud1Gvv<40yr03}hL_}g zVdA73XVA)FvAszoD|~8C8Y7+dLHSnF>6WF%GY8w$$hmrZYpl6-U7RPh4q9OS!>-R+ zx*Qi~tTI}a3z9SLkGf@yQphXE>xrEhVL~XhjM;8o-JJ4l*F{W3>m^lo5&9Mvc^m>g zWPQu!VD_O~`uXu=`ThBgh#f2Ze0B*)oo9~LrYLv|m{=p=asbrK8&f|)rf(EjK2j^H zcZH%z2+Lz6BfKS?iJi;Thsk0Y*eh`2r>x;w8tpFzDAmxfEXypSt{?@kpYWCPO3y*} z*)DrJG6_Q3H}ePJwc`m#ADouG%wXb54B?v@R{M6f?399VSJw_NAni5AmBalZ>E5TL zqTC3amX@q=dK*m4YXhfiP|oM9l5+Nl6E~rznRSsDEs3?E(HGG=mWtS47)6=p;Bak> z=j8x_cM&sFlRwA|P-5f$pP7*^CN3V^Ms+NJCWn-~OlN^c`@-54+OE~!o$!OW6jGRj zS^37PCB`krK1tFT(3Nl`~>>!Jl3>L@LhTCT}5xKa{py!_|4#3^O0}++MK$i z0K({xVeP=n9wRjSl+7_oKDm!;_AR&$`p#YKjyY#rUsrjFk_ zy1qM;wokXC2ygD)*_$_qs>`l@*_)=5k8jT}NrNBKA!m~z>941!*>x#@r#e+cvYX8DxVnBjH5A)F#??<9X~(b zehGXIb#^{=WqtTW=lBqtJ@6rRrBCl%T zsH51E6fO-hI~@YLb!*!tr&=Ii=e~Nn4gwn0WVRVTMN7E|J1FOmIj7{%@1|i5EIz!Z zVYsJ?(a7p4?uo^`g8@}qn!AihRPlK<4Nb0L5YQWx`li%WyE^cwQU3d-<4n0Y11f<1 zbPih$0un{0*py{kxyX>}j9McIjp%Z*O4OR|Q6rb%()N7HzXzyhDBo%M8HgUtoQ>eq5SSf@z{c4UZ^WCSpCAgr@Nj2ZEX(XgrbA@(qWLsHPKK1m;aoCmu#2+!^wPg=0RGjNXbyxR z4d)rD*waeFV^#fWAqK?RGhQ+dx!t^J~_xh;T zP6VY)wdLOH3rhoRaU{(X&m|{Q^LqDmv`sM+%4BQTYD%U;=pF*zW1>dcM9*QCkwr!m7BV+ zH-!)=71M8yw7gn07^-gO7z$X8JW^Z|seP5G#CH7sN0q9tM9@-xHdwI3x@2>I@S`!ce zT~v|GojBuZ3HPSa)!qY6cR7t53Q4MRbr#YXb<-e}iSiveW7(0w*U;40q`z;Zo{Eqy z7yu@(;mcsvpYHS^Y)-ggdbEcRADb&<`-d2ZfZj^OEhrK|sz+c95mfyJpWi)1Ifq_5@F|>{ zZC#K$oe&h`w|}wf(*CDloV}a_R>_AGf62hBPK#LI3*W}e$p$iDR%u;M?+Dir!WnT+ zNFGuM56ro`g0pvM25J|+h$@Nmo>J~-g8_491fqVzu6#PKb@2C7bRjiYT$A)5fFVUR~YRwB_I4rq% za5QIDVP3SfJg(l$Yj?&#br8G0QJ;iPfpMcuxLY^Deabg-HkJfu?~oP2(Xb zB!8YGfpu-txa4ij@Pr~#l-mhA)QJY z=A}{!jah*j&9p%}kvYgP{f6+(O=+Csdsztf@6)c{qVWkO;+wm$buRLOlc=c)M`BIFD05g=#Mpa0v7 zm1TxAo@u8y^qu;YsZch!H~QW30_Ul5y6ivm3^s=4}^p8~&QBR=72@DV^2K%=%Q>z}i$QDq3QPjT2(% zmT|U*2sL3^VTBw@Aj|E>RXpk-cl`dR*=aXg_DsJTud=wg+odUY5w@Y1&z+`Y@Z65> zz~7jT)>2>n+o*=xLIcK5Wq12lb-6XelvP9?GZZVL8R1mqu~5!0!P$OwRgx$2RD^D? zC9zZ<%m5T3C`ZHt!m+4hp;Q7)Q~mS<`Bl~m(LaNSB%#8HL4+`06R7+!3Qu0CD7<}Ur+ z#(%WjG`_f8KVRAAJ=$xp9;%THcdskZDDU*eNm4L2Ha&V~9A2Dw&ifK^;EBfP){?H#8F?wAtTv?rFa&N$USc9SO(G zV#(%z6Q5`(!wzPbbBDO#RLwui+@ks|wR~mzPTv;IkVwlA=Y0-`FVUwd>o(-b>lq?< zuo~Z^dljzmjFTpPc1|dUedrgjsoP=(j_EELMLBfzpV6pvD>^16+_j$=Xnppb{}!M| z{<{FB@D994aAOpij+OFfmY>3}n$5TuVX;ls0Qbxs8S!RE20|5jx9yeFNk zbz<$Re(loQedeu>Tft3lTgW%F?^ z3g?1y>)9?Lso+w#=Q8{()l4#XQd)$8936u=7rnd?#h9$2B>CC{dmMb-7Ir9pl6n93 zj}kOFO|4`bl>m%V2?KXSGDyAL>pDh*Dz0YwZu7dfr>)jDa?ysK;%wR4Y1B~(y*H0; z3}5EHK>|?m$N;t#MA=J=q3ZqO&*j2l-|ruaCyhpFTE%icuU3v zOQ#(OYwj4Bx<~YkrjZgYaMG5mU3Cp`()u`<4Zr?vn#}e;to2SYSG#td5f5`2H^Icu z*?;NQSm~H9x6yL=W5FO*N)ZuR*}EjNd}fY@so&9EO0uUkV>a(tjIpLrVWpt9$3^%i zhoUB!rbVRD!qJ3|@*$`be-$S$@fxlyvqw_0Pfi#HWYT=IXZ@_LfX5JB#y9OKX;W5t zuxLFomW^T?X5MHAJ$>nO2Iz%9;}`nwd(PcpKj=+QtZ(Yj*yzVV8ls~WsdgAPN4+kz zQp;iwPY!*aC!yBoR(bjx2SO6Rova$2)e`NonF}ReAzHhGTU0Tk% zB#ONM4S#9CT|VigeewgznfFKb;uXD4Z}|0Q?*V@e?x@;6Jh($bSk?4c^bH>Q=IjIUL-l_u~%m7>upa#Lc@ z$&`AL@`?L7r5A6__}5E4D^5CZxD$sPVz95NjRqqVHChJrNvO!kS0p8a0w(7;Rd|Yx z)Y%)ViI;Enmz%8dZO+;cMff@C#LHmN9pQWLrQwz!c9n5JF>dj!#A-pKx{(gZr<>@C z%~2#p$=w^G-bQ3-xl}s!A#NsO1tAw_mLL)W0IAb7J6te6zT%At$fX)NOQEYty+{MA zEX~R<`uo9hGB&VYOD)(QE5BkbDfX;VG!_ojl1X5MZ659Ll!g={+~B#ga_u9B&CRv& zffO$c@^QsGhJPgogu@$KjSP$&{BdxiDZ7$IQZC3E7#eal#GK|>(<@Z%fGrfiYv5Hg z*{A6Vi|B4>bhVRd-*94<2OnY`&9Q`20vRtyFbW(6tvCY28BF}%&w)9!QG*oA{|UfV z(s2&1h%GWiJD*+XvJfK~W0t?mQ-=u0GFJorjW^CLf8~fjLx}^iYRy&x=O`~NHOoCT zW|S5XX&uZZ{~Pa?QFvM2s@3Co+!6qb`g4R9RGA{xI0thW4+~car#g0^zDHw4@Q1pQ z=qBk`J+EW{d#>1Kj*JsV!FkRgE$DuGIXM>VDG4_gtA*$}>312Jldfq0C|E~%ZDzw{ z5deFRGGu?fV=$LfQooW;Ggc{zw&j$1Doy3&X?e_CfnFOl^auuXD+L8h?=WDqUK zs8E4wnEZ)Gt`gDM*VEtmD?pT*-I%b+n}KGg6V8P-l)?7mV$ZiUSd#tqTUvn|{YUjH z$|ucl>&wae-9sBtsA;f*tpUpt`+I;kV8SGLA~<@YFgc_Voh@CXj{A>#&GhsQ&N!}y zvzf}3)5{rVe&+9`~^hbmy?O) z_=yH=&>xbfn&U`HAQrFWjfO64Y)-88PHguXIVIv&QVpz$snZ-*;SE|U*wZvGRhF-o zt%(}c#tHN@+IgoP?+Y$?`l%v$^(6(~U9N#%gy3Nj9tivX#FsT(9SRyOnfDKqMs z+n+~Tkoxww>&fd=`4N~PJmyJ4c{QbEbvPzdMx_>1xHL2}nlWgzxM^0(9&LN0B<5-% zl7V?^BAz@aw!*#{_Km?A$o{BLUWx9Hc9ui z^LRSG&eNx8u0|tuTHi=QIV(~tA(2_u!Hgt`=4IMkP?5C7<^m#?Dv5LRLb-*IEFrG( z#EeXl<`qOwbbBk;eNFCQLOF6(1@Dmj%P#!X2KUrXdb2jCzV*Z20PO(}fyP1-B3Lv6 zor-*Y2cW(Q3f{*eu1=HBBjs|77}+BFf+h8yT|jeoZC(HgbZ3b)#t?g9!;fZd~QzrPcT6#$)Z;nBBnBY ztG`ezF{)Zy+ekJ+D81&rlU0()l-Tc^Q9fkSSYX3@I?-2ObXR}w8ts|jF~>_u%1jBu zOL2zz;G3zvgR%g2mf3mXt^PgLz-lm~v`ZXQHeBoyPmW+t&f7$L;tKxy^+!RnGxOK6 z9!DM^Sm%40bw)<}Akn=e!yhbO3;JL|-dY}=sZLKYax-A8UhW)g+q`&w?WwtXw?OS@ z>Kx5qtuOQ$wUn{`EHyZZ;*YyvlL5dml)~69cQke)m_*nk%3-`s)Kct&4&$F-AQJn4 z>R->tcNl!sfB0Wvy!~=T9+~;?n{1leIGyqT*<~|&D&;OUO5Kt0KeREeVMp~1dh!Hi zwY7W={%vNYWw&;-jO z`{O6&Vs1m*E$dLG%^tDS6#tDO9AJAqqIF#e9})cSJ!O6rsQ~5qBkcU=;EgiOJd^j3 z+|N<>R3yS=5#G5+Hm{Z#f)Cu&7$**dEK)7Iup&65QxfsgR>IYLJ2|8-!e*u^}hL;^m99+ zi})vb+kVr0BlDF!;>dUAO~%Uh^sCdVI^V?aKwOpY7v&qkz4z;qRrHWgjlp<(ypd2}Kfn6sV6>}ZDsrGe=r!0Ds}mmuNU2||OQEbg zX+mm%cDRBZgac!kvEY^^t{r6hQvXsP)B`KKh~9Fgl(mtuJym^M7AsD*z6&1xi5mTN zSFk&MZxHVyS1!l!^i&Grq|uLD-_~TWqn1pu$^@D5^I=LC+=LAJ=j+rl$}0nK;bcc{ z!aG47i)72$jx!{f1l&lV{cp%cu6f0fB!q8@p_d6A4UPm+G83K;TvCvuZhSAyPn|-w zZ`r?K*YIaTP3Y9&OAGSNt{+PH!QJbtQT7V_$XhRkTuJ*%D|*F_TSdmD$WyS}P37B2 z;!u!ClFvF$YW`fSEW0oGIEXE3MpOFu-j`^3YdqjzzizWdvJ{_AShXG|LzI~#a@ei- zoRIU+QnvmY*I-+nxvV3{pUtt^s`hb`Ke4o8^jjBBS}`IzBOd${JsVREQKq-J-P0g( z)-j>y_q*iOt~ptR@AA#7Dm2PEp*Rx3obtqI=j4E01H3rsoxLXJUXPuAbw1Pu zh`o~89B;1Jnx}~(3D`<29EmV+1^?uD4`HbZm1L_Gy?&dpup#URL*!BYQJzq;APkAc zVsEGlUm6)cwLponSjAY15HHr=mxgXzrR&|+{IQs=&+V1ub@=O7!!mtLT;2|E{egQ> zLz4|;CtE=om*6n)3_I_8gq;Nr=yaFs;AtW67_if9md(gyiV(;7ap6uV(A{j7549E` zC;Q?4r;TP?sLu>aIa`2F|Ck=JYUG^P9% ziEhW}wo0vU>Od|(`Kz#%;HInc?YPmdJ{9s7sfw^NeoG^s*hlyJj{Y0>cek}Qvtfdp zr&{QrcVDclQ^Kl+_Nz8xQLSy@eo?e-BRq8+jSh(<9kBUtX=`#M>NH?*oAN2TR_%CM zPV1d?d2*jyC+KmSvt3M`!TnxU%|~oRm3P1ZsU!WpXGc|MnKZIDC-PGJ?`ZhlieDrPY0nn^RdDfi2A`BEB?S=RIB__Q^6&Q(mkW}0&t0G@O?CQ5&=4a#m18o^Wpw?s*!$3T+w{Lu5g zY}p`g<3rg_paWjwMSU=g6P|EUl%}1 zK$v(Q>D*c$rJhq{Gg_-xAOI)OCC<>Y^R$?9Be^+mg<D)SY-Q!zQ+QN!2~y8Q350kRm<*E-?3eltAQMBqk7YDCt-UZXvamcPUC~ z#8kolNnRxb{Soh6wk1ZE5!RGg^@1MPA3zCj~=nL>9P&-v$}aLq24Ymu1b_#?LEF5 zCc{|tQ>KGQ)6vL*HxIbam%0_Llcwx!C|WQNikKbj3~sMjm5*_(1xXom0C)s)lCehfwd_#r6I| zi{RakSFZl7|0Zy}z52Ko4=vAM+dXWK}JH zvQ}?2qLb5tPR*>~ewhHLft#V{sQdk=V`h%7!~RnU05slX6#c)aQ(gGY&|@S3SI{#{ z*%0F(tly?+1;1?m?M%(kdO;FL_?hT)4*u`q|Hao?K*iBSZ5nrX4NlPDkl;>mcXxLS zPH=Y#?(Xgq+zIaP?mEEih77U``)Kycu3eIB?>gr_D?gp z!T)p9|JcWKhT#kM*O6?0QsJ8_z2rYt zzIQMyJ7OE$T%Bj9-lR^{y^xFBFpz8>9@DKm$*Ot8ny)^eV`0Il`r-t%oSB2UEMvku zy=Q>+9lT@sUNETB7xiDG5yC43PH$t@iqyu>Xj2{~3)Hstzw98z<2?u_h2yI`XFM9> zm82wisIKP8Pa1SnDb>s1t64WQmW8P`wY!BDz3Gn4FGvF`iMJ@Nm)J&Don`AEi32NH z>Sb?-ex;jC#byzj*r6v*&~l8ebDrW1S8D4lQ94hDsXDxiyP2+n5@cSe9^Rbnq!>k6 zk`wtzQfTUfmHNehdGhanZN&7Z4b<_z zMDI8;47;}!AP`%(1~H1TbVNOpV)fgKUePjqqxQm!IOs&_l6E8f?ZPPp)m38A1LG51 z?UY2Br5lByYbufsI^MF_%(6h`Sn8xd_q{4`^r6PSAX7lcK%3;tJuaN9k49dc4SD2& zQbY@dn`wBAKf}U=5|n&kJm2|0X#kWaQ|v)GfD z8AsX#vWyf0Dn90ENbbSKy0yy)A=gi)QBNc~tWt$s;jB6YQ;+$4xbe}LGopf90w|ro zrc(UR`A~n+`n9SOZ@M+b!W}A*k!cL-O;52bn*h`gkZXjt_H7lPLInKU92o2o14>P2 zvL8h=yweO?T|&lNymKB4T{vEb@UkY=WaKD37ZkA*8#WL+LQ#AUGV&~XhIX#0x9})D zgEth;vO7aw2|f7f&O^+y6Uzzz$Q)y5XHaLSX77G2j6W*gEiS|cTLjTz>8fmt}2 zCSjEC4IF5$wcg2ggG%=fG%!Kst%m}{Sm*S9`msTB`zC>dgl$j?CNhXmNLPA=dHU$W zkUwgU;eZY@&~4T$W_nsc;E*%wC@v$;4)ER1gr2)%T<4z=uTRELN>0K4@@I+?wX!L}5riP`>G%}o1AxdwKhVW5?z zkP(VmA6R0qU*^Sv?gJxb&2T8#l6T><($b_BzfMeuB6wo5#rE0FbfgWjg%i}Y68|2M zxr>t+DLvF|UUeRgv~+Zo&cOwq>|NjiQpE>7v7=%|mnO%AerZjSAI)(3RQ zna8@L`0kiR1Y1?T)qBq%8P`0?9ElMub&sDHdS`HHfkZV-h*UOBE}%{WpgPo(HIvAu zLd>ZY6{Om=qhUSUaSwzu52=8 zJm>AgKR;VmnVZ6vmNYF|gzQb-{{F4R9*FXx9LDu4yr*p=o&Hh2DNiujutMugh(ABX)duD z$X*tTn~&HG5-W*MeIuTZqurM}VzZzI6?sru?h_R*<8e5lC@r_T99KNH@boV(`-!Xf zjdOk~V6#E^;K*0M-V(~H*zB%Fv-G--M*Nv6y{|W0+s~u3Ae*W4>GA7we;?FC)Kw zFr(ZJI+aUc_Od{ih-u@VVe4eHN6OIL@9kUd-r(P$cBpacsqx zaM4o}vQs8+)5!6bjH4@py`|2xvHV`>xDR?S)30tX7$Hi3JARs=HIQ40Czzlen;&ju z^+FMDED$AUUn zSuGE0KroHll!O8n!uato0)4D2Q#++pZEMjwZp~H)Eb}qzi4(?VC=v;4y_9AQ5LKn%5tl9oY8{f5`ukMVUVkkm8=V@?b1?+;eCpk;9sr9}vhoML ze~n?#T1v0#-(sO7XQ=^qhUriROuH;z32rOuiT2*+J|KBO>WiKyF>{W@ooafe`3~=- zvxfM|F{d=|i`v^W2)Uq_!OMK%i9Hp7T_F{p5J(P81|fHv74=cQsP>$zdOe9-xI=pg z*Tr)5gU8ux22QPSt+ITN_GHqY4VQU$d*HW@@i%VkYs@WCCR-}IM**WI*oRD)eI|pR z-zo^t2F^Da(tG6kYIqSRsHHo-i3gdM-qw=O_9|OrpA1_kFt!%EjjWv2gGnW&JtK#a zw5yyur&(Vabq?NrNY;aEa7Qa&2b|*Xsx~QiSdnI9$7}HhZ-&1T9G$06cC3lofz0ZGjT*ZQ9 zJOmfmdVXWni|0s$Me&yr_7|uSZ#IR;`XL{UUK;(-V}sjg^eowvFx{oYAE(-IAXUm3)yr~aR@@ME|5C<)1t z$WJDynkXwZ2zT|vpk{d{syNh$W9UC&AGWIF2)SsNOBxKAyE5w^x*|DX|%8EQ(4$^1L43}13_1sUQZ#H(wtSSCLPo4_|8 zjIyg#@;?$Tc*1A27BHpH)Z{6>(nXPqoyZ3No7^PwBSh^cD~ZYRffb!e@+09vCz}fJ z6A{@V-m=SL4?Z<~Wa#Y8vJoyp^%rL4?162whES9IFA=7)zu6WHvF4f9gAz^(4+&_O zL>U#SYsD$Vr&LAD8dP;Z?9C`!)r%;#-Rb8s6j+#A(sGSZKGj5ox2VeC&(||S+7Y~h zs6(RvMIC}5MKr{R-CJNCaW*1WUFMTol~pT6O*3ix&urAa^suqR$sRKGk6zmgq675; zQWW4=9>1PUP7+V$C+cL6Kka>CrBzP-y3qSnULjPbDy3E^L8N_L&5ggj8T}@?BlxHue%m=oT*jFsbauDr zl~n_pjq}yM|0U^)fQ4ny(=vV{;OpyW^}GQ!h2s9BQGrdE&VZ$Z9`)nb*U6@7U!5)Q z?dkj1*Mo<-voqW!F3?|)SQ^>S_vNPm)AOD7#~U3W=J^hA4$@)k_uMY$oW#AqI5_+H zykq+LdQ9fvzP*4H)q_0^Z!32aA8+Y?emCI)AceVr>DJYY^GFWRXF7-0@003Q$=l0x zfbWCT+umfCE$|gEw{P8uCw@uZPUhb8cHaX0+4+O3(*t<;KKbO=-Tk(f;ki_>6U#NH z&eeZXv9onqLK2#jU9G=aY2U54^{plowTJ)xI>{$(b0Wolbn9_g;0;yYe6Gi@Lz993 z?XCkb-ZFE1Lihd@neniszv$P|Gm*e0udW}sFnN3SbgrY03d#uC^Yh{dT$Jb7r~Z?d^KHJf3(S^nBi~T|Uh2*JQchH-KwS#qbZI zOFjIidpCR6SAa8IpxZiX<%j#ZM?&sd+1WGDm#e1-aQ|?)V15++?)7{>813xyo_#5YH%Z!Z*nA2(Da0%JK_H%F;&fQKXj;6RSP$=25Ok@bi7UBX@eeazjk_7Dk3 zw?FRnSHLe|J`Iw(gEaf`0{Z4BHFx*Ek6Cwn+Rb#P;~4xrxQ@+!>x;*`DdM=^gDj*i z?P!P{A>z2P%2)b@`eaL68l}dU&}%(AVqFR)F<7`psXJmVZQkZ>i#h>PJacSnL>|x7 z6opo-c!qBHhH!X>U;e=DJsJm<&AD;*Iebw0QJ*?QC)#y#8kM}H5^oK))s$)#G zh}(|Fa4Sw=Rjc%lt>f0OUag$6$J3rnvES^p>=IN5Y2RN)Gx1OjznP9AH4d+ef zNgh2XPkYtSN*SO{pn>Q?XxRDC;r%U=($g>8Y8WG;jv(Ku2Gdr|PJQ_G0DNqYMe6`knGaDh)e4S;l#Kw4Ve+g2vv}@ zMSV5HDiq!F0&|&H$6V$tFED&^a4usDb|dH3P2OIl+eODpQKfCRVKnNiG6(aaTsQm1 zLoFOtw*He{3*6@KseWB5Eb?V6M4~NfLOnpzZzbcE2y#wS9!Rc?*$_#!DUuw{3;!1a zwz>el5}|_9-__+>CG6(a|d1*4p27@!B~Y?WWQdx=K7 zcLAt>$ZD#dy@}@vL zPUl(+h)@r)MK`7hX<5ZBa(>tT;**Wu9OKFXC&hP(U#y-T^KGW0LLHC@XnOOI$#~x- z4yTTz*Outq>uOaNTE3(N%phL1kQ;dUB~+2ErrBAWxE0?KJZ!KoEPg$|oT$1Ku*|rO z)h8KPqvN@)+tM{2=QHi0m&10Gev}n@b*!;jxhqvQd5Bo2tZ$d4a*Do|lugF{Y{EWL z#^iNiF(7VV?&HR*-r*gr9xqNaSbm3n0HEIZoGM={so>{v9WC6vB3jH9zEWJ?Gs+kr zanr({U(L0!b)5a3E;dK*xS=>L$D`l%7eDvTFu~^@(^)?!K$-0yzw%dWC`elZKf<*8 z938)mU)MRjzTD~oD(_5pIWt8>5wAQWCLd$4Skcu7wed3cLs{W?zzQLSaXkev5V{J?!wFN+_wj7v~q${KJ~MwbkhBm))FriYYJ&YlUUyR`5P8D z0Es3Ph1Y)kgU$pl>M+6b-bGhIS3M||SNKnQ1HDrTVcS-O&WKIhR+C9{sM}-b!4*lH4NFJC6(h2k^!hKVZ3z{9iRlMQ zKK1#{N2KZ&EQH9?&Z@exN;)RTn3#l9NvZPE&VsrjN2)3GWOrV5F<(mmYnqL#EJ!eL zkU3t02851ZPJkIl$Vm6Q-T69mHsgs%#ol#Yk4P5$qDa;E42ZtQWyP~0XosRdg>BMwh~p5A z#PHR>O@Xf|4j4efvbLE|Tbb}h@daQT>c$=jEuPBt$b=a*WV}a20yK7aV*~)(?r*=b zeH_HL_S*BWE&w$>>vfMYn1DGyhwJ+iKj15-&dk!Dk1JmFF=6jN+ZlJ{PyeKU-HzaI z&9C7!a}XoZnQ76STg>$H`~$CkPisHw>@!|tL*1n}6UrFrs$?1r+8Sm!{m4Nq=f3PD zf`7c>;+4arwT1pw$EuCL44(zikss^Xe1z7c#;!B-6z7aneH{4r0NqtuB!CEho0p{R z)u}(pp`LAdv83aURvqdNB3mE)&N`=t5n?&tQfx1Tfq`3pB=}4yVC|0SH=BB9rOpZL ziAC@_;nWanSU>YX*G^wG-k;Zvyo*5soPj_va!-IXK1P_uft$`n#{eLWV63b9!F`AZi>Eh9<>QOnScqxJlcpcmas zwtbW+eL|kR#+Y+mE$p*dW!cJ2#?2dvUEsRsNBzG#!H}0*h``9ol8P3<%&+MNo?8i1EHr;`12s z@c*QcMe@q1v~+)h>>^AKAdt`baNtvBnnW6^_*9A3F`$Tl`x;?s9)UQpRRgfv=~EO# z()J-Z_7nc)>1)jBS_b{&q8O)yCk%ult3AU|k*`+z1%SO;;^i~?R$bKin044W0}ru6 zi55J2J5i@?$wwdo0D;H|6sHL}T%t>lqVNB@XEia*4%d#4I*=P`REV;#ZHg(U_lZWt zbMgXdgB(2C%JCBC@4NMuilL9q_2F6+O5S32TtYD4^P?MG3E>=l@>fy6hTt%Yk0)GV zTa3@9;w35&>D%I3iMu#=D6j=|>XxRJyEMvfa?VH5+I%A*Gc$PbTdeRqf36OUZMh9v z0Ddg=92gC|kuc|X(SETIE9dV*<}V7H<88n4NC^?w7aOC&&qTAumUF!9!RDaKDt!lu zB6K`?tlTr&eWlk<3i|LfeTs1&@s{r!)zux7k^jD^niw;l_kWbEx<^$~kr79o=SeIM zrI^xsf5h_ClVdEVmgS?kS1Bzl@!a73lh-@D$W^1Q?ci7%o!Py_=Y|KRh0-B?pSBJSR`AQ+unWr_u2 z&w14RqH!YXDS21aV4)rMAJ#czyKYa3o4W_=vneadBk)H=YX##d@#m)eqR^G*oVR*Rbn(+04jx!V0U z7CAqiE^Tog^K0u7n9ErZpW3@Xc4{2X9}@?DDBo0%V&6tQY7;Dd;%d@AI@~FK)Rt>$ zt8nHs-P>Pa&gPobE3cDQoS|O_rNq(FdA0#dhqB6L_sl60KPXuFSdIk-pV!=4UMie( z?34j)OFbx{1ht^h{P&pn`?$3lXDWMRfb4!~{zrBATdvcKowS#iv~0gHfg?Ifk>!Ym zVIKoRE!!?O{#g_|a@%dLrAv8FT5;YkapH%v4cmkTs#v z3E&0#&trz9mgXEwB4{<5vPyW4U3-q6(ly80Gq~8WxmbM}>Q#4+XL^&`zBMloK%x*`~H87GnCL38ik~xq+ zAPDdu$t2vD2%jjk>oDpN9oDlkEW3Lout60d4xbACh5MD5EwWk;NoZZOQVl>mPz8@C zTc_oin#&nK7Q-Ck1Ybd)Qa7RJ$CFgv zKGodnj^DN-PQ6sB5=F0JWlYS#&{YO#8BaHpr~24gX;+s_TcEc1HWQOPVZ|#)r4;#C zofu+$P#Zo83U^6Q*bCPUS@MUyAhYyvIGXCu77U^`huJ>4>JkmSsZsK+9$Z%+b+}!r zhlxyF?dg-rg$*+Goq3p$9X{Pqh}@x#^D)wr?YEGh%oH=Y@$U0(yERS|6Jd{Cf>On6 z#1)BPPskNd+ERwPrWUFtM{oH%o5fDJM>ZnSk{Y7;fsIqD`(@KLCZ8VGFxW5|b3 zPm~4`aLbU!{UiU{>AZjB*u2DmnAJ+R?Srd+#y%Ulf#T7#Q_=aX-F`y3ttEpaoj&J$ zNT-%rSTmw}=W58@OJq>`T;C?+G=T%XhQpf4Pi8W{ETg#hFNTd1;^R*~OP6beMd{i_ z@4YmYf*i|LBf*h14*S-)7JtGzZ{^)6oucDlk_ky{Mf85=cga>`#^qwbfwaTq@zz0* zWpqqUx}lw?+?MNSztN^hC~B)>(O(e*+CR=N12Kn-M=r+0#oS&(fATH%VvLh@7kzh& z`iU)3M+aTs=(yDHmkI3ct0zi1sAUk?Gi^K5#w+MHtkX*QH#P#nkd4Q%`o+xkY6FJS zGO~JfRDVapNVNKzl6I6YQ6VFlUXtEvC3S!eex8;m@i>mYQxd9es844{ZZ={)dBi80 zGzY;wNo~J*{*idUDiHqauBo1}eQQbT(a-S|Vtt-+D?$TrY5-ghaxET@n!;-3PAt=x zD%FGpCK+CZS|M#p!?X{_scO@^)pQuwU+f^eP)+I#rpjSzt@n*yr`q(&8v7goWQndHL2vCG5hbTt@ zQw3L*M^dF7;?38YKEzYUgC28jnzBq8dpwlhCrEWhP^gw;{1vC}rqzPn)`z8&2 zvVh`pIplOuEE1hVHI{VxR8mp3)e@Wz;vdV#qtmhhx8K<`RJ1|)-|i$OTo%_l`zzH! z=yLJPl7z|rFrDtc`4KNwJz&e7o~v{u>a)n{i-=NCXEp>rjooeNFsRhtP1t5a7pRa<;)apzY^64_{u9K}|?af#NNQk(iaMx^r@n8gR) z9Da>fzMu2k)G&`&wI%1@ksq;4Pxsox71!tDp;n&M!XrB7Nnb=E{ptKq!341d9|i=I zTT{#*>|`*yTVlemY+YY0Tp?w8Z9l=MGHY0| zNqzAPU1r4F+szFWk#bldysIboaRD3zJfv6U&j%J)$*XFUjmO2`hVTpo9T4RQ=lrd_R!9n1HY_C zUJ&x2_rHVNxRmaO^+ti?-x3$d{sEvc{=WezQ`9}6UYg!IY)xj1eCm?CfVYR$R^?}U zK(cX9j~^G&``7n}!;is?r5=C)@a5g}+3Mrqa>x;f3;06-rDq)#=w=UW#rOU3ek35@ z#pU;-Utm|@qx&XP9{4^S>j!k^@OHi8 zd3)SlTn>FaKi&y^7~Om(_Vsz+x<4$#h5;g}_k1+seY_nU4MzK2y+2*;JUmaM_5(Mg z3o=T!fG_uexm5Rtvz_4+lDQ0k`#A93JnVZ^$do`&v|VNDo&fOiLP78ecZ%Ci-~Y60i?>V&ebU+X4E%IX>SP-Mt*TybOShFUY1OrcL7&B*V6)*0dU^ZjdU zlUogm_SI(j;X z>UY0Qo9h9e`j?pWfga{w0`D)2&L1)6{i%CPzq5$*1a!rNdQkhBdSWgsbAazX_U~6c zbw@L)H+k8>k1RfAtJuqKAlLiWw7h!H+l|ZfFp1y#=F3x$k5gCp(;Q%Dw+MJJmHlzI z)zW`Lf9v@5?S9u@4LDjdEdqRf@O*y7^mTp$2{n(VKbS~DuX2X^WA8dno(%S9uQxy; zjj^xxo?NDWTdW^mPqRldKZaj!7+-frNz?B}o5I*C6MmX%Oy)#C8SC`2?*gb3F*hdiQK#O6;x*QbXDRBifEIN~ z0Mjs2q{yM4?m07nzajoSHkaNPJjROJPqk|EKD2!%=Yx64=TVur#CDfO`#sxu?XZy8Zd+ewtxUrQ_-rUV)K#55^N6rmsor4ay1ayc@cSMR z#QPWu|6`k^3jxA+|0LAIZCOUuW4coygs(#2-MBlID3>{Y0=mEV)SPW+enKmHNAyww zgwwoAB?=9ghP2MlrKXq&od>qFJ9c;9&qUa&Y*e$9g(It&cb|9C1eMspm&wuH0xl1q zgWB|H*G?OaGIixB1Bq}QtFvAdr<1r5SiJWCd^Wd|QiYRIgqmnVlWtORgd)hf+Mm5b z!jYJtN|KxQG}>MyajMpX*vd6>+4!D~Bkg)2v~db`K!aJ~)}lfuR@XKFt4z6x^cg;m z`mU(WoE0jCwxrL$n60AMdmvF(nRVfFBtQPs_gZ<3_)H5a;RNd$2z;e`8u;|t6L8%D zJ&juCeakDgKmJ(k%+S#o@(sjwE!JmY@2D`JquVvvZH*fhN4EqP;CEFIRn7~za1KSY zB{c@R`x3%fj`L32Klw0lGFcME2o++ws&*;Ii+E^i*P-~=zaefJ9{kxdWi|Rp+xA;4 z19F684gJDSx9!j4-P4NIYYbkdej8F88VW~X&!2kyTAqh(f3T~n*y8KI4n4^v;;3Z-eXbch;S+S;QT9HIDu5Ihu{ z+Lwh&>wuMS_`&X$*lmY_5p01RTx= z7bSI9WhzX1KWT)mxL%X1-ZdLaIG

a=cfHuPut%5ET*p%(#2Lhis||<#S<{^Udtt z&~Jnr=Ly2mgY3 z9QEHyV=}us$x5opJUVpJ=cGEo#-gW!I8&I3!+PW5 zr2mp2jGw8`z_l-UALhw-CseBf!je*H@leBc%oGbzSx3IL1oYSn0Py%{G9#TviY9)b zrKI1e$$p+Wk7MW@EY-XP8WPk|aV+~^O9onkWU{D`!T z+X?b$(tMk%pIJ?FtwrEX6KUcQ^9TwV9*0n+*j6qDR#>OMt{KI^tpwI4APNofu;a-E zFK$PCOVd=ts#!JY1usf6GzEXrBeWb{!@E{pElR`{xdVtBR0#Hm{zns0%z+~%L7I}c6&N0*#>|btx&4}`o__79mh+?r{Jr|VB zwn~;iEuo7+Em85P*dGQb1AsSI1NRp#NyUtFb2Jc`A2|KKWD0I6ur)x3SBloNC#!ES zzSM=4SJ;Y8Mq1_HNCK5W{Ut@vJQ#Ppi)Dfszd8e;Kiu_mjkyBY)Tva9`;yhDRE!iz z+pmZ6y$0f|>95@9Px9mRw@M!D(fB+XDx9?VaG%by%?%<&iVH`hu`5|x2X2yfxKNg> z+bjC8)*$JrX@11L`ENIQR*FR~<8;Uv+|rWdgi59^U<-Fq#K3<~TqR$iQ(8(dFi6;m z;YNBuigZX;Db@%^ALL^=kbdy=M1-2D4?4G)s7=|u_8g>y6=wDO?Gs64O>6Ys9MRDu zE>O})?Q*XJ8m($iNOVwQ(%7fe3he6CPLNr*!X~4hqN{9Fqc6)RKhYQi62>XvF@#z>gI;DUhbYD#RHX;HBKm9Hc!5=)xUBZxy?==mJ%|pbTr|; zZ9r7M4wh2E4a$-_3E71hv%X*)!7I2P989x$0xIZsD9RtTEOhhrTo8eh3dL^VcXXH- z#?jUqFYr)e1nq*Krz=cs)zh2L49+hS_s_1G8We>sa05I)ZsUjwbLKlwn7EQ@zO6i= zJ??^0+##H9uGUX0=yGgNmkdvp&k@vD>w*Xh9f9}0+$r&3O*!^vEJ|U`6*`L$^Wt%K z7wwpo>E%9uWN5&wEy=Gzk7ll#zgC_^OM6}kB=HqL5OkAi%VQT`FI~E4L&^EC67N^S zuqwFuwu7G5M`Fd&^7Ka`{V-^#l5kkuzq^841%KM0E2WB%$`$;3Fb=Tr?uxGpKdmUf zw>(ug6#H)g=ph#D{3>uP>t7Z7QP~Rx!L6W6CwZMgZwHR{l}T__iPv3hiP-P{t3~ne z3Tzd$tO_ly7ZidQ9YQ3si)a`(u%M46BS#=7;rQtF+Wx1y4O5wdO~SEu&m)L}l~?P+ z2r3CPJ`jO~0W&pZ-;1qhjHKoz@Gg9J^F4_iueGBv{mo zAQpVv(^OqmDGZ+2=oKy&Yeq5C;u!f?$rLU+ERAz-I;pNgt+sB75R)X+-|Qh45L5sx z3MY@VA40iy=TxD@rgTAI<{ptd{pMaF{#*9XS|{0LNy?qXb?PtKWTN&H7eMF+5|J>~ zn`)^=A?1ArdGhoAemaGpv__!O!6Ud?nQuARxoE8gKU%t^UXZY%2FK~}hml6@y(M-F zin2o^QV#Oa$f)#U7oxk2>)28xQ$V4E?oj`GcFPHY)X>}jtShr~(6Zd#J7t?bKsVXu zlmoJMe}-_&;XPm*)mn4Y0o!=U zOJ*OdX5{jx_CwX5EBs`&>yoD>rBu|wg`7J$jZosX8#roR6%kz|ORd;ew3oRzp&VAe zlSqMMK&aNc6h+ajY=K?>aQ-IkV$i7p`rFhlDO4jfv2D&}8EEF@B8(Ro_ zOx{wI0Zh6)xyn-cWRVBPFC}QiY6cNg5PZBYlqo0>yw+~e_()G~(8Nk+lczT(c!hgY zYZ%|p8Rp|NrR2bbX!p=#%apC&d*U7L8cTvy-lpR9Zk&DXY zpvhiz&PAUiNc|O3apzwUlIlGc*l1}RWB>3A1K=(itS|O@@agAc6$FIe;T{^CAJ{zf zrAkTie1l%mPtBK3hfy`LPbK~>&@A$uy>LiH?l>TRUlCu^SlvX?r+GDVUH7&B!^!h5 zsj6F@%e%Wu4}?wa_}tI&!vDSxv)aqC7Bc?pR2|BYoe zYrf>GWYYSh@5E=Ktyz@=s4|IAK^2?aLz@29&ra>*S2piGHrlg>0?dZ} zb+f~XwCSq$Q++Q;R(y`F&1@1=OqcucZOE?%>GfNS(eh!==<Vy9jv?edZ;X94!u0pBal_(C zaRGC-vK!cvU0kz#<`o)WItWT)qBeG>1Ki9Y_WAu%*{AGXGTXg>77za({ zn_O|9*5#F^x^z5sTE!}4shyR3sNRe~-Q!kKO#H{=uCLJewQ6TswMW%`zN|Ja!=?M^ zJsVb1-HkIygALqB+-#(66#1P=F_Z(0DQ4~0-Zgbym z)@A46TI=VEHH{3gs6!2RAXlPNgmoQNZ8DW-YU^-}N@qk_WqD`pTA8nH7cHOMH!9qd{-7 ze8EuPJPCI03iEcEg!(nx@173sIlhGCaK(^K^?>j01Mji&<39F9&ncr%lob~VvLa_P zNSV!=!wjv>Ls!XSlj)lsdJe4G7CkGzA%Ee=Qc-vY&2VSFjBNhDy_~3MRrBuWPWB*k z+fh0hy4#Mc!{caOk`GM9+BJ%v<42suY5>|SGq z44(saHhv7*AomBBW;}(9L@o&h!LPf(MDYZ*6b0P=PC0m_RiC}iTr za|4`MFndkSG3l+w$2Cv)o80-_2ItytTjyPpB3POnC3+mg%8b9)c}8^3!z<3-p`Lv+ zfcD2ME6sYJ(UQMO`b+ce^3`(pN^J3uL4DGXf_asLFZs!jYz=$;M&MKm7NtYz?UqUe zIEa5&yB4zhAz*HpN_DRsGT=Vp7dGOadI$rfQj8l|{6dxzII%a!Ga?l~CP zI>kQtP_)bKtL$j+&Sv{U${{h&5%w@968F=O!|J+0X446nm+})x+B8<#nQwv*G@X%7 z)HX@JC;f&9L_b()8AYAlS#~TF_ z-bIR+YyLX?606W4Jc+u_Iler_NF1zrUkB z<4thEYwhcCkDbZu$+i+nlOx=gKsUH(W4s#kFqH^Zpy)3 zuN-psV6DYz#<`S&H)8`UeOHQV8n*s;sfB$#tqJ$}SsGRVYv1Ji2$(=yQd1nlgt8d$ z?JNHzaf4Em)@7qPKk9^>X=75%A9{XCSeU)ppTP+Pj4>8pCDmxUk}`*HBs;-o8qtb~ zaz-5gkBn}er1-V4Gb-?{NU_VRPWereo$vFCB`=Nez& zQ!TS;>D!3++jG@Ds>hKNnPFFtFqQti-XFhdNBa3qw^=wB4W)>4-}O>3cRma%?__ZF zNfTqA*5=ba_8}RAyN6HN6Ps{ApK~lOrZpVx`!A(*6dL)BBQDCgAcIM$1ky%Cwxy${ zI|*`Xj1!FShwt}siC76juiqKJ_!I(5Qgh-ZPiE=W$=3=-9%<_NzX|ULA<$Yla_<2&k;;_qF2u3r#9Z_!iL#%^PQm2`lcEz{ki!zv@NHNS_5qj%+ z^%U|7wQ?$W8W{oxd-a+B{z7uJ-{HI|p?obi?tM8KmB>A~9{%X(K#%@0eXoA z{!|z_!L*AwmSaE~`xhVRDIX6YbSBOcc?^rC}X>#G@%hOIWSTo+m&iQaFqiza~ zM1c5%LYmW``wS9d=;!9){9zXSYrz2r{%i79Z~#YDj8Rp zK5-Y^!7X(R43V`AnxXO3STorMptRc2u**8 z_&{>V{n$?Q678aNZUfg6EjiAUT#d4*4%YgCSeNOeyN4o^up8)hE zo!|SdZvQh2y&x6dP5fYU6oRL~3HE|jKorVq6QnB+n^_wSfl=wioeTz@0oB)d zonBwP4Ru@C@*gxp0wWRQST6PfwYFutt;DKZn^CbKQxErXR_Uv{6hn9=lS)%{voy&? zR(caUH*3vg&6B1m1dj~a-$GUit};@+08bpTId#G$4(G`AwPIEK{TP`ww^Td1kfz@) zB_+Wc50+WH-?wVH&6t)r^{SdV9tkb3(*fR4&uALe2OI6+Z)E3kVdu%=82W=0zE-$` zIz3Ifmpx7*6|43R3&o6^ec_jyeirHPGdU4#-bPsf$h(CD6!G=^@HSF}PP~y(Vnx!> zt1u_`FDEg_ox=l1mx7b@id@bqc98^uE}vX{t#+<=m`iZ?(Fo$)BK>ua>|niIWWyIGA=7?J)Q z(?|_-Q%52SE_pk?bN@W>A^J%sP)o17*LE(qL4Rk;^3Te&-s`S=diIE@rUT>4Ov?!4>< zlVxgV2lL)A>iKS6c8zgUX35Wz$X7N--$#dnadqor=1hXaU8nN?HqG|QxLf{SS|jO* zK-+**spX(ENO#7HiQIJYKDB5-bB?4NX-Ks=`MiJeu$YqyGPe^qBjNYujrmH_eEpsq zBS{*SQm-5M8ng{=EuRJrL^I;@7fvc=Vn<){UQUtk9bh(q&_*K)U~Z?}{+7}6uJGFb z05Lp_x_>wQK#O0dKo*xekFSPqd!l_~>jiqDf6)5bZhwkgQ(j-z?5>53Tc*PPqbukR zz5(rm`{%tv#2tMD?Gw$g=OXM-Q!i z3Iytm3jKIssu|Akeb|ty;opDbsNMvzx(=RvTkJYF(k{glWP)A5u?i7PZisRUNsmv( zv=^H^xiuw@U6l{X3R>Q)eMKs@Hq4z`5<5gb@fd7|5#GY)tholb^~(GNCnZJzR42?&#%5bN+CbZ82e{*~lG4W~)TL1Re{(8yz7 zT?B`@$yRNS(Ufja%QgS~aYx4rSBf?z3Nz-kKBX@MxrK0N)OF9}Y?I&o1@E#U8_(?? z7oUp;95-rC>po;yxU8D56x7LBCzddjSM;65z*K}qMVCOKU%$bM`GJSX)VhUfPfJ$9 z9v6t!QIBfxT03?pH{q3)JQ*I@_+;aRtM~!t{R)z$JV$NPX|lCA7MZsqgJ!VveDCVy z2-Xgd-K)Vq43YV2)u)IVlkPAdRFJZus67$Tgf3CVZO}SX58%cBAY5-GuQ;5qIQ(&1 z&cX=wA{kf`-<9JpaR0XT$GG0MOHMqY%5=LXlA92E8!=n}ueU|u3(|~WPDm4E=M$I% zXdcc8!NzfLj+s1m?OXo?SD0QOV|zb`Xe^OKCVS&b z{I39-EoIV!ldaLn-PA{=6yAz{l^1#Zo{he z{{W;Y>v(QPn+BDv?Z17s{T>Bi9f6r_!#vh?2E})<-=h+hVE#;t-!#z?jM`Ob=OgJC(rw{tv$bOxYout-_M-vaN;fQ#I9sikxBA06`ZErhu_Sgeu&xk1IXT$Q;?2R;yvv75yq5w&--qjQ*m6B_P7W{ z9v1=Tz?ld9&foOZNXa8S`63RF#Nm-RJQ9aT;_yfu9*M)F4|RB?K%^5z-}l2Kpv?ym zinF6RzCY1U@CIXrbqsW@zQ@^-qF|)UNSqyIKRY4>av$<5GdedNGVW0$U!V5 zNZrN3QOZLO+vv*Z?PnZN{t^ic3gPkEw<8VU!LLl);Ql5!*y$!X;CbF7w!y`b#^>#( zIMT?8BaIK8xFFTeyT3t)(zv!hF_1N+<+U^$<+JJ&ec)-WJMq<+#m3QoQkGZY+r|&ay$+JHJc)ZkBV~CFylwn+@V4<||9YbU0{LwtILTA|rV8S67pAR1 z(cd_TNLw>5GPA--6=y8;jnFqj-w1t^d%M0NIK`%~&^JQg2z?{;P44abCe^JUp>Kq~ z5&A~xo7@IAa3Iu@k6^`$7b{+@_)oIp6)i=0h=3SE;RuBz6i#jf3I}WY+k6_KF@(kt z8siIT49#0H0-^|oBNUEMIGGg=P@{s8S1BA_PZ5YN6pm0hUrpg`!Mdl*!j)>V9YC?# z#A^FIt4)~qt(bSH$b7{LjUzOU&^X(-@2N6Eiq$4o+m~By!oF|EzGIN$cDB$sLgRcn zjkD%;_;aKGr!slV@+}E}^)GQ5e$?qV*r;lZZ69&w2f# zm*VBcOYsZ#E9Ko~!Vhp(n6Y)90pXy;Gq~Jzd4`GSvGW02qPbpC8l-?LtBR}1Xk}G# z?MR7U;3q$Du=?>mKJ%_ z1byX&={=YCZtL26RPx=_0(#rTy;ZBEI8NDLrIaETcIP@Rb@AwVU+*!}c$KIj^o~GY zsKigD5;twHQ{^BL!YPDP*lS_0Kd8OVsuKCjdO{@%m6)x#P>Dh%epZ#3F7>z2i9#m| zohWqT7tx78g<`8)=)|nWg-#SY@x$oEj0fxKGK_v;oygaIT=5jwf>sqzu`*W`f164L zDK3D$uSQ+`OyRBzyjwI$Y;SX-IbmhPq3 z6>CeZEwQ#XzP8dlr6kstSX*LkO?F_?uPrDOMeJpWwY8zOwQ1F#?qM%3!Sj61vY1b2 zI87G*h~hyK{1xSegbD5aKFBEDD^%iaP@D~lv%$?MHE}lhxy}YttGc&|4T)A(8@C4# ziX8L;YYD8CW^HB6uLl+FJu_tstR=A4zF@75o2^vOdy1tcme!|VTEdMHZj5kaR0SpP z`U-m>?18Wc!X5~F@agSAs>is6JrMRl*aKk?ggy9h_8{w4EY?2v^H0Ja2z#*RR_wPz z=uKyZ{BewbU} z-o*0;L+yQ^K98i_SyX$Pa(rbe$xE`NxHX>W9 zyleFDPiTl6&vGH2rLOG!gt%9W|;KAY(@MzvlOupy% zKt2zwzE_}b_#-#{`lmXRBwj;qXig|6%(-1wMQ_kHI)*(Ob2goh_j6FbGZk^Kv^(ll z+D_G+=iOr`RMp64Rq++ub8NS;K*yBByzVGWIfuVQ88WDc}wS(>iRS7 zUan`_rs1+e3X>75(;f8B*fg?s^0LmF>i3y2@^DPn`k+7im2qplnD-5XtEJ<<*}NI{ zewD@}I_x}X{BL@#T#t+F@0SPd^2d0bF?!-rmz(LdGoU!}q^t?>F}_FF~owQ3D|C!OLkrKD^y((9^Y%$mzpqQRquT*(md4j(!ugT9=Z8AANA$Ld9(t{cOGW^er#7H-D*_mGv`-r|874@=jca|G9D{M%=wq@Z%#3NDHLjBwbDc-$?H$!e$W^* zejXiG-{_iJynpx^?n4GX^<+Ti1@lG7U` z0oHBWydG;~%*S6Mr{{>anhmLm?FyBp_K(UT9bdj277JHL^^?NyQ*=ZN5FD1JpT*l! z;R-$+Q_a#2n|kB)@VX$i-6B#x!Y1?2LmSWrs04~5ow>y(ZQXh z_MM`W^I}soehpEW}{S4swekUsdP`q+HL*ry6{}Bm#JJDo)!yFjn~^k^}F^!hq7{7 zmfFq6LxD<_l_{(f>g*0j!1r2uNzzbI6%&WhT(^I9)B)On<1 z`~Iw4C_mq|3dZonpu@Z0XHBW<9ytXzMo(1JYiDEK>A$*#!&|LF)o%N|sEuD5!@~IP zp-SP8{&~3|y&lO0`*mp2@sWJqEL@CUQQ^_LuF*mtpO2*nxuF%V?wvZ-jH6OfufhAn zf>bJ>7%Zg(N)T#3h5{rA8Gv!?cGL3J zFnZNL_C#F%00flm!p5suU*NtKd?Y_}X9Fi8*=+~}?$YA!>(psrOH*0fZGuhF><@eoV)D_eAk63FLx@e@I{P?Tet$6x zZ&VG>2Ml%!m}5BXS^eqP1(UikTHgv$qx#J?JJp=gb_Sh^DhXJEb2qV8&?zbDP{^~+ zB5rUxH}}RccVxJ3$WP4!b#0M@n|D=>cGtFQ)|-6P!)YDIf%i7q2=H#p*=B9}&tMQj zTiHu$Bzh(JKTc}!OnDSmi4>U0FH21B55U`?%|ppHFYfo1xQQSYV5|hnT!ndA1M#>? zPB5c+BFC6^_kqVX=UG;NgT448-IW@Pn=D(ARdVW8 z5}3BipbzMtPiCBPQBz5auC48BUr`P=-tPS}4MB16x&u;9V6V_}Yqa z>b9%Vig0qyJ*NtJNR3d1i>go|+fs%guV>MRO6>7>HjU^tE0m)DdFM(IzN=D%LMd{k zhy$hAvkWe1IiuH|JDx-Pw2QePy6`~DvQeYT#5?3ntqjw9$`eKsbfMpVqB8vKp7N&> zK{cPj8UtCuc_otw%ISsBV_Cd4qr%3Jpg^EiVhL;$IIlx5diNxy06 zagrd0J_E@c*AeGKLI`&poSWVpD{3BO5JQZ!*<*gyGp#}PSxJz?2?vQ{$Qu6KVz|%l ziSn-Qi6Yz+?w*iPsDos)R}F^`ey(s;er5R`g$}*1(5)d%X@xkG&}j=ocW(8= z9`X7i(w}y7@PK48z$(=9S%Mc}dKl<)lH?M53?$lL_}O7#l>Oa*qo9Z5@7A^m@@k8) z|HA-+%is4lziFO(GHt$gCzpqrSZu;fDjQQ-avthC(OTPz`9?q|iaHCYhIv_*LDVTj zs+jK-8!VaF({-S5PbD39Nx|M*--R*Sf4o!+Zo{he{{W<{q5F(B4XW5DC-_4MGlHrd z7G|IW6eWtOggM3WT_*OhhE<@VwCp8G^VW~~gfyp;W<5rTvt3$7Os_$n@5diFIj?K_ zipw`Kcb2tmjY5KT41-4b@`*ltDMZ&kX;hDs$Y4cHk!*Yrqzpg`n%KLr0fmxCfwC=H zHZi(9yA*{m$;*)|p%7F3^iq7LW&wnn0%tR$s4C8*s9oMdWcB-T_x#R$F?&zj#_#~7 z1EpYH-x&mo-3^mF6L}eN`*l@w4!!YR z;^StTq=zxPSbmB4)HI)rO6$;{Dml;ZVzIhUd*<`N>{Ldc2@Zc)=)zJ9^FPnnjQQ@d z86JoE#uoTpl;Y2Ekz-p}3=$P1Wd?RY9_c`iN(J&RSPTKcQ;wu%PhhdEI(cnf8{Dfu zc`-Y=cAUbM^JIOUtA4OOODHF;(y1UGAd)AE*P?=LNKURNUwKiok)v$n({w=xP|stK zNR9(?m{*c_TuDGKr(s45$cYy+n?SAya5HhAcy_2y5<3YN<}+be*B|H@DkT*aN=3CW zS20uzQxrqBFf%b!3lkDUr6<6CFhBfT*47oH>t@%_C(obMLr#G)d^4IHM-v2Jsj`Cd z$qOk|by7H&U*nc#qiR;HHXloVHnK({h@kw}6_&LCsYY~#ALj*I9eQ3?OI5`c1^JXXkzvxlJ~LCPxa;K}Oe*ocJl zeu6whBJUp|5-iY#1W0Sel#2vNd!rJSL_9ZBML6BPruC9Gs+#P*SvB3r#(AbdRvZ+r zRmornw18)|@YXgvOl# zziT=ndAxilBIsJ&Ie*Z#WG6I0xd$j+MC+O!Hc$w{>dbp3D$avQi0O-BoR6J85jRMk ziJM0TtT|yAKr)*!2=IzyETF`+9%SFoauarmBT@}MfDrdzrZmHUl`m+qDw0rpcj3L}G4`!s9%pcu+30Q#&~GRbSpNoCXepvg0pDN1I&E+MSTvmQZJ1JSP2M{+%( ztm#T*QRW^B7p@vg0;(hWou|fb5o@T)7XNCD{49sU^(#rt>_66AAqYaP) zkbJm4Rh7(4s~OT)@O$qWz(nMg?YRvQzx~9sc)0wI>zCdD*_ZS(V3u>$Peg6oM%xe? zQE0?{(uh#WYg^1Q%JfrWgd$jECNWGYRhCh&X`vbS5Hpfhm=OvMDKz9qjy_lNYKEO! zIZu*7W)Q~=UriuvhbAsqMW^>552*sI}nLcaKWifhVm@TA0Q2^2SvC1?T1oPz~i4Uh+ zuHJ7pQ9PlVvIE(K3nEz|Boq^&>+yGimh1N0B#GQ>DY?%kU#hOCBy?>fX=yB1?{9r& z%}d87eKuxH8|d*7U(0p-Z6?aBl2T@sjA1ZH!33usv*p_TRQn zxhgp2pk=-vM6i;6p)XhOH=8Z@FF*uhgwl>$OzK}HDF#eNa=KAN+2RU`j)6i_5886= zeycE>R?t*gQ__msa^-%biAjnPN;zoRvI=8@u%f3Qw58hpR6W7)yW> zEdfet2~ajm0H8{AJ*6F6uG@!z86zQesH*eXz*h{}a^-%biFQCuX$RD7c7P}d!cg+p zl|!~%x8G(qD*zeT&_mY`5{4`;Qa{lOs41<0n#~G870WtEJ7h5?K+R|ZG^~Zwzb6dY zLgo9BV*KQpIl$JK`MB`N=v3F(xt zs1`^3^F-EcNJ&*k?7vpU_1`~m-f~W~RM($CYxPXqG+Yj>2=QOgojl?K^4y)*%6^cmgP z$N0SATuVJ^RHQG}vqq7e*yvX3-<+M$)?=qp#y!J%kX#H;X@C6MD67Mp(xc>*F3)J^ z>8{Zv#}|(k3H*}IX{%6cjLG2EsY|U(t3=zC?^ap8cye0Og=<~V&f%lgRQtak9qE^9 zT++_CXtDAt`+M+$FRAr-YmLd}8FD3he{)6MVaFP)@X~Q5{djOid&srMsD1V5NV#M_B-Z(*DUt8bIAV2NBr;lJ^%Y;$A9zoRh|D`<@~fC`QNAf??e83M`Qfsn@iR| z^|*b-=Bva1F0uYqija-Rq0Yu&=6&d?Fq8}UYCmD*dA^D*dDx#kh{+ri4w1_vYinr8baZX$b@i?{fR@tNLQ3_ z9EvQj$awnk!cd5cG%a-c_2I%$2!uKg=WqQ`VJL_ICN*H!VJHv*1p$yYIAGu6Pyj+Y zlI7H+wxK{2q5z$s)T6dr zvh~Jy+SLjVP2B3*k5aD=o@lQj{~BW*!fWZZb>UDKcb|&n=gIkrr2MJ^I(Rb8G8x|e zLXv44NAyLysE={+d&s>NY~AuJz6iE zK99-skISL-I=FY~^Qqh`tJ2XWl74pY2ekRv>W$HT|5B4k>t;yZUfC^T`;nYT^_QDh z+CDD2O@vR#wRDPa#`NWS)Grb`?A=Pw;1W=$X7$Tzv(kH%=yBEVZtWb=vOVzBJVmx&i8?T~rMeF15qcL%Q zU9<5r$Jg}vNE??`t$5AG>tcLEo44+GjBa19HAy>pxutINNiO1+p|kO#FL%@)T**y@ z&U7|jmoH`da)?2Zd>{4Mc>U=8pw6#nP*xjepO07X7i~NnU`*u8DjTm}=aD|E7qE;k zVU>+nyJOH+QH4!Zv8t}5zcwqhfA1(oJiPfXOU+8NO566M(o|3JcQ#(Xnl<`@&QX#4 zwxvU#5sY3@B02i@zWVWPs_ZO3|M;kZQ`@ZQ&R}a6KeD!|dF=_w48A`*KvP=6` zQ!A23r*J62OFCrZHP*(9F)2&dM#Q8nQGVu_l;mgZKV3}9@~eLP0eAL3CM9_N>%4S! z9u7*u>*s+IK`CLHcL_>Sd3u!aGb*b>JKi3W5}(y2W6S{1G))a{;LAJ}(AqI6@gWcI z6_bKhT?b0)!^6D`Nr4FKO6VB!V}+zBJY7ZTzLDG(@nD6QA7lA{oW<<#fidl!=eu_|jjPmTgv z4?b{m6ihIX)s&`eQ^`?enW?l78UM9TheXl?80USs$y^z<(iIxUal(Yiqe}7VR{No~3-t2hTrQ80Zq_wh zZ5`3Fc3B+Dg~F?&kGm%=`tujQYRdQLMOlZZjqmjCu=1-c-<-6E`t|qknBF%p8%2mN zouPiuPx`CPUb_jL@?}YvE=OJZ=zMp}^3zkVu0LzIK+EpS>sYyNYDoV@i$AFOdaIOy z=9;&9v2o?n-|9#k%LBRK=*lbO$7)_471g0xuS4;t)bN(Ae_1bYBrONqpiFWVqttRH1N$iFE(S#;*;Ub{XH@s1>Pyck`R}J8V3OK2(QNsXH=h>$mI_)n_)oQq#I`(PmBR zmT?^#70K>OUD}d=JhSmBJ`bgr%QKgDN|(JdIWw>7;ZvrtPxj zjxoL^w^9-Rl-c;6^~)IjCJ$2ax(#T%^xSV^Xpu+hcmN@_^_%_}%RN$&j*rd|wO>br zA|A=4E**`3s?;9*9F*};CoSpdw2i6#j0a6TI3uof*nx!FHFGe=E+s?h@bXNf_U-XY z89zS~St@AvI<>FcFHP(;2$BjnmPGA~%a<{3KNC%o>f=M6-DFtAt+93}NyXw3ZH?}S zWo&8MiKK(_F>MXtu!-xZ+O?#gH;c5@GlpYq-e|Xy)*YYFR!w>>;>s`WLDH^^r?hoz zy*9B?*B&JzmCtDF``POlKX$c>L=KxJ+PZiiv4I(BbqQPJ3)+-!MrHg!w3dYJ;w5e3 z(Wr^nk*qQ{!W_}bKJz&H4RB+@0UihIA?y_b8jh&V!@) zCS_R1#w%Dbr(KWxX7gs)`&Akb>;0pOc6oKr6qt2`#={o-Tfe?M(P`Pbc^t#~%j2zn z(PO_G&(F_IdFVXJdjFz!%YR2>C^hJ#&c2<{!7VB_QJa;QjAN}q8TnpU^6Kl9 zDoi`cHaL{@#)UzfZC1ZZj(^J1i*pC*b5*Su)f)}5-`k@j+Ev>1vU>7ZDoNyf`rE#aZomWzPap{)yt#HM@j3zHtlwlZc{z1Fl~)*o;%dhThB!VmFK#o*k+eHzwVyP z$i2so1g`I&X~&hG$H*F7y3(l0wfR}ES458&hAa&)Fw^Fh$6gcNw4RaFuMb|R4NrSx zbW&j2ymxL5sZB>rn`47%Z)foHmA34f+ayDHRg&7Lmt)#2T)WJA-96n(&7LIF=7ZWV zlZxAWkQ&D$NUcG;-z4|vOk11XwnD9w@}Njc&8xapIW$z-Fb@aZI`BBK&JCt@rO&jr z{JZB$ziy5Rt$#OOiuhS(+WMgdb^6jdW7@iYdxfO?t7nN0v@z4pKYLG_MDJ^dbnvsz zwDa|udm>${dPtZ zfrre_@`AV8kycF^L;m)qaEjJ7DHp*D^W|Z!4w+q$Rb0 zpysszbr`gO(X0ioS=t7F@X@SVOs~W>l-LzC=?UPE)J3$z0A7<#Psiz-ZcH1hw-G&U z14Oi;X~=5?hXzag>z{A`<9{C)56g%D``iC0-W-=7=}GRqe0`Op4@Xy}-~Y<})#&%Te-{eF;V?f`@=mW_DBl(CPL2;6{kDzdg3F|{($81> zRi-b$@sIy}YnVp$pKoomZ}>9%%xD^Y^OfmqzFJIQxC;M#bNeIP%uV*QIYFVqMERfp z=>9iTFS-Al8#b)IdB7K$`FD=V`N6PfbpMt6fB*5V;FbL6TVc}JlT#h#yid$f)mR$` zH3pmQ(cl01!|YX!j`5%WntXQIaQzKHx9|Tw^v3pYEXz{@Udi7~sM?18FW#qa&akaI zGcy&&H-DvV+DzN%v6Xnxce;Pe-PeCj|9*1%Ok(Cf@fS+xK6uZUPQDyG_Ya;tKQs7^@hRuXVq7> zQoStu)jM0O-Y{78oh?<*hlf>P-DvfH{gc}gyL>!Y^`~1B6~B6WOM=dan^kXZm;3s5 z$G)*$N$h_RN%Ys{-FCIgmUUu#yBd8vf*#nT*Ho65n@s-rZ=Io^#sNo5f1B-YfAp^% zn&dQn#?kWo?WxDH-UkL3F&#TN5YzX8r_Tq&e}4k>8`DGXy}8VNbD2Y5t=P_>ny)zR zLVeP4A-o@WYVS?vN>_8IHvjhJB-r(O|EHF|_d<^MbAp8p{m|2QXLPDgJLm6S=Dh7o zF`*xLLQA=Z@nYr-_MfLGtDkdfIolYSJ*!jCbqyY}+i`PFC&xO)e{=c`vlsl3&y{Mq zku%6ujLv`db2Y|cZpa9DeQnd`o3xh8oS-(Zt=4ne|DEeMEWY18$~~Ekbd(pu^AUt6 zW_!7m>-Q|Pnrjd2zV*aZ1D|W}e9B@XGjRE|Pa|4#K}ZW1n|`ipRk_TVuSTVBk8(q& z*J3hJF$S*513YKEbeKT)dj=B}CK>$8lWFt|PljvSR>#cwCxcA> zSS@bD8Q7jEGmX}F9HvNnOd1_e8-|7GTY;Zi@gppLYW9Er5C0fC1oY1Z`&N6>h}{KU zhleN-eqw2N-TpLi!tTnGUsNTysraoD9Kr@C)8D9Rv}WJ^!@1dMZ4UmGn;a6Cyf-cQ kyKj@DIVGsJuuw1SkJG6$X(`z4wtL`r;Gh5a{|!4YbEqA%Qvd(} diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 3f019483a10..a6529b5d4a1 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -27,10 +27,11 @@ #include +#include "AutomationPattern.h" +#include "Song.h" +#include "SongEditor.h" #include "Track.h" -class AutomationPattern; - class AutomationPatternView : public TrackContentObjectView { diff --git a/include/BBTrack.h b/include/BBTrack.h index 70195f28de6..36a10884547 100644 --- a/include/BBTrack.h +++ b/include/BBTrack.h @@ -50,33 +50,11 @@ class BBTCO : public TrackContentObject return( "bbtco" ); } - unsigned int color() const - { - return( m_color.rgb() ); - } - - QColor colorObj() const - { - return m_color; - } - - void setColor( const QColor & c ) - { - m_color = QColor( c ); - } - - void setUseStyleColor( bool b ) - { - m_useStyleColor = b; - } - int bbTrackIndex(); TrackContentObjectView * createView( TrackView * _tv ) override; private: - QColor m_color; - bool m_useStyleColor; friend class BBTCOView; @@ -92,11 +70,6 @@ class BBTCOView : public TrackContentObjectView BBTCOView( TrackContentObject * _tco, TrackView * _tv ); virtual ~BBTCOView() = default; - QColor color() const - { - return( m_bbTCO->m_color ); - } - void setColor( QColor _new_color ); public slots: void update() override; @@ -105,8 +78,6 @@ protected slots: void openInBBEditor(); void resetName(); void changeName(); - void changeColor(); - void resetColor(); protected: @@ -162,27 +133,6 @@ class LMMS_EXPORT BBTrack : public Track m_disabledTracks.removeAll( _track ); } - static void setLastTCOColor( const QColor & c ) - { - if( ! s_lastTCOColor ) - { - s_lastTCOColor = new QColor( c ); - } - else - { - *s_lastTCOColor = QColor( c ); - } - } - - static void clearLastTCOColor() - { - if( s_lastTCOColor ) - { - delete s_lastTCOColor; - } - s_lastTCOColor = NULL; - } - protected: inline QString nodeName() const override { @@ -196,8 +146,6 @@ class LMMS_EXPORT BBTrack : public Track typedef QMap infoMap; static infoMap s_infoMap; - static QColor * s_lastTCOColor; - friend class BBTrackView; } ; diff --git a/include/Track.h b/include/Track.h index a4930832003..dd6066986a2 100644 --- a/include/Track.h +++ b/include/Track.h @@ -133,6 +133,25 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject { return m_autoResize; } + + QColor color() const + { + return m_color; + } + + void setColor( const QColor & c ) + { + m_color = c; + } + + bool hasColor(); + + void useCustomClipColor( bool b ); + + bool usesCustomClipColor() + { + return m_useCustomClipColor; + } virtual void movePosition( const MidiTime & pos ); virtual void changeLength( const MidiTime & length ); @@ -154,6 +173,8 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject MidiTime startTimeOffset() const; void setStartTimeOffset( const MidiTime &startTimeOffset ); + + void updateColor(); // Will copy the state of a TCO to another TCO static void copyStateTo( TrackContentObject *src, TrackContentObject *dst ); @@ -166,6 +187,7 @@ public slots: void lengthChanged(); void positionChanged(); void destroyedTCO(); + void trackColorChanged(); private: @@ -189,6 +211,9 @@ public slots: bool m_selectViewOnCreate; + QColor m_color; + bool m_useCustomClipColor; + friend class TrackContentObjectView; } ; @@ -264,11 +289,16 @@ class TrackContentObjectView : public selectableObject, public ModelView // some metadata to be written to the clipboard. static void remove( QVector tcovs ); static void toggleMute( QVector tcovs ); + + QColor getColorForDisplay( QColor ); public slots: virtual bool close(); void remove(); void update() override; + + void changeClipColor(); + void useTrackColor(); protected: enum ContextMenuAction @@ -486,6 +516,10 @@ private slots: void cloneTrack(); void removeTrack(); void updateMenu(); + void changeTrackColor(); + void randomTrackColor(); + void resetTrackColor(); + void useTrackColor(); void toggleRecording(bool on); void recordingOn(); void recordingOff(); @@ -503,6 +537,9 @@ private slots: signals: void trackRemovalScheduled( TrackView * t ); + void colorChanged( QColor & c ); + void colorParented(); + void colorReset(); } ; @@ -635,7 +672,16 @@ class LMMS_EXPORT Track : public Model, public JournallingObject { return m_processingLock.tryLock(); } - + + QColor color() + { + return m_color; + } + bool useColor() + { + return m_hasColor; + } + BoolModel* getMutedModel(); public slots: @@ -647,6 +693,8 @@ public slots: void toggleSolo(); + void trackColorChanged( QColor & c ); + void trackColorReset(); private: TrackContainer* m_trackContainer; @@ -665,6 +713,9 @@ public slots: tcoVector m_trackContentObjects; QMutex m_processingLock; + + QColor m_color; + bool m_hasColor; friend class TrackView; diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 2fd1cea125f..8886d7ea5e6 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -538,6 +538,11 @@ void AutomationPattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "prog", QString::number( progressionType() ) ); _this.setAttribute( "tens", QString::number( getTension() ) ); _this.setAttribute( "mute", QString::number( isMuted() ) ); + + if( usesCustomClipColor() ) + { + _this.setAttribute( "color", color().name() ); + } for( timeMap::const_iterator it = m_timeMap.begin(); it != m_timeMap.end(); ++it ) @@ -593,6 +598,12 @@ void AutomationPattern::loadSettings( const QDomElement & _this ) m_idsToResolve << element.attribute( "id" ).toInt(); } } + + if( _this.hasAttribute( "color" ) ) + { + useCustomClipColor( true ); + setColor( _this.attribute( "color" ) ); + } int len = _this.attribute( "len" ).toInt(); if( len <= 0 ) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 343768b56ff..70da1e1d73c 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -38,8 +38,10 @@ #include "Track.h" #include +#include #include +#include #include #include #include @@ -56,6 +58,7 @@ #include "BBTrackContainer.h" #include "ConfigManager.h" #include "Clipboard.h" +#include "ColorChooser.h" #include "embed.h" #include "Engine.h" #include "GuiApplication.h" @@ -104,7 +107,9 @@ TrackContentObject::TrackContentObject( Track * track ) : m_startPosition(), m_length(), m_mutedModel( false, this, tr( "Mute" ) ), - m_selectViewOnCreate( false ) + m_selectViewOnCreate( false ), + m_color( 128, 128, 128 ), + m_useCustomClipColor( false ) { if( getTrack() ) { @@ -238,7 +243,27 @@ void TrackContentObject::setStartTimeOffset( const MidiTime &startTimeOffset ) m_startTimeOffset = startTimeOffset; } +// Update TCO color if it follows the track color +void TrackContentObject::updateColor() +{ + if( ! m_useCustomClipColor ) + { + emit trackColorChanged(); + } +} + +void TrackContentObject::useCustomClipColor( bool b ) +{ + m_useCustomClipColor = b; + updateColor(); +} + + +bool TrackContentObject::hasColor() +{ + return usesCustomClipColor() || getTrack()->useColor(); +} @@ -303,6 +328,8 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, this, SLOT( updatePosition() ) ); connect( m_tco, SIGNAL( destroyedTCO() ), this, SLOT( close() ) ); setModel( m_tco ); + connect( m_tco, SIGNAL( trackColorChanged() ), this, SLOT( update() ) ); + connect( m_trackView->getTrackOperationsWidget(), SIGNAL( colorParented() ), this, SLOT( useTrackColor() ) ); m_trackView->getTrackContentWidget()->addTCOView( this ); updateLength(); @@ -512,6 +539,32 @@ void TrackContentObjectView::updatePosition() + +void TrackContentObjectView::changeClipColor() +{ + // Get a color from the user + QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )->getColor( m_tco->color() ); + if( ! new_color.isValid() ) + { return; } + + // Use that color + m_tco->setColor( new_color ); + m_tco->useCustomClipColor( true ); + update(); +} + + + +void TrackContentObjectView::useTrackColor() +{ + m_tco->useCustomClipColor( false ); + update(); +} + + + + + /*! \brief Change the trackContentObjectView's display when something * being dragged enters it. * @@ -1151,6 +1204,13 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), [this](){ contextMenuAction( Mute ); } ); + contextMenu.addSeparator(); + + contextMenu.addAction( embed::getIconPixmap( "colorize" ), + tr( "Set clip color" ), this, SLOT( changeClipColor() ) ); + contextMenu.addAction( embed::getIconPixmap( "colorize" ), + tr( "Use track color" ), this, SLOT( useTrackColor() ) ); + constructContextMenu( &contextMenu ); contextMenu.exec( QCursor::pos() ); @@ -1354,6 +1414,51 @@ MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) } +// Return the color that the TCO's background should be +QColor TrackContentObjectView::getColorForDisplay( QColor defaultColor ) +{ + // Get the pure TCO color + auto tcoColor = m_tco->hasColor() + ? m_tco->usesCustomClipColor() + ? m_tco->color() + : m_tco->getTrack()->color() + : defaultColor; + + // Set variables + QColor c, mutedCustomColor; + bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); + mutedCustomColor = tcoColor; + mutedCustomColor.setHsv( mutedCustomColor.hsvHue(), mutedCustomColor.hsvSaturation() / 4, mutedCustomColor.value() ); + + // Change the pure color by state: selected, muted, colored, normal + if( isSelected() ) + { + c = m_tco->hasColor() + ? ( muted + ? mutedCustomColor.darker( 350 ) + : tcoColor.darker( 150 ) ) + : selectedColor(); + } + else + { + if( muted ) + { + c = m_tco->hasColor() + ? mutedCustomColor.darker( 250 ) + : mutedBackgroundColor(); + } + else + { + c = tcoColor; + } + } + + // Return color to caller + return c; +} + + + // =========================================================================== @@ -2073,6 +2178,10 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : m_trackView->trackContainerView(), SLOT( deleteTrackView( TrackView * ) ), Qt::QueuedConnection ); + + connect( m_trackView->getTrack()->getMutedModel(), SIGNAL( dataChanged() ), + this, SLOT( update() ) ); + } @@ -2135,7 +2244,15 @@ void TrackOperationsWidget::mousePressEvent( QMouseEvent * me ) void TrackOperationsWidget::paintEvent( QPaintEvent * pe ) { QPainter p( this ); + p.fillRect( rect(), palette().brush(QPalette::Background) ); + + if( m_trackView->getTrack()->useColor() && ! m_trackView->getTrack()->getMutedModel()->value() ) + { + QRect coloredRect( 0, 0, 10, m_trackView->getTrack()->getHeight() ); + + p.fillRect( coloredRect, m_trackView->getTrack()->color() ); + } if( m_trackView->isMovingTrack() == false ) { @@ -2190,7 +2307,41 @@ void TrackOperationsWidget::removeTrack() emit trackRemovalScheduled( m_trackView ); } +void TrackOperationsWidget::changeTrackColor() +{ + QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )-> \ + getColor( m_trackView->getTrack()->color() ); + + if( ! new_color.isValid() ) + { return; } + + emit colorChanged( new_color ); + + Engine::getSong()->setModified(); + update(); +} + +void TrackOperationsWidget::resetTrackColor() +{ + emit colorReset(); + Engine::getSong()->setModified(); + update(); +} + +void TrackOperationsWidget::randomTrackColor() +{ + QColor buffer = ColorChooser::getPalette( ColorChooser::Palette::Track )[ rand() % 48 ]; + + emit colorChanged( buffer ); + Engine::getSong()->setModified(); + update(); +} +void TrackOperationsWidget::useTrackColor() +{ + emit colorParented(); + Engine::getSong()->setModified(); +} /*! \brief Update the trackOperationsWidget context menu @@ -2231,6 +2382,17 @@ void TrackOperationsWidget::updateMenu() toMenu->addAction( tr( "Turn all recording on" ), this, SLOT( recordingOn() ) ); toMenu->addAction( tr( "Turn all recording off" ), this, SLOT( recordingOff() ) ); } + + toMenu->addSeparator(); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Change color" ), this, SLOT( changeTrackColor() ) ); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Reset color to default" ), this, SLOT( resetTrackColor() ) ); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Set random color" ), this, SLOT( randomTrackColor() ) ); + toMenu->addSeparator(); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Clear clip colors" ), this, SLOT( useTrackColor() ) ); } @@ -2286,7 +2448,9 @@ Track::Track( TrackTypes type, TrackContainer * tc ) : m_soloModel( false, this, tr( "Solo" ) ), /*!< For controlling track soloing */ m_simpleSerializingMode( false ), - m_trackContentObjects() /*!< The track content objects (segments) */ + m_trackContentObjects(), /*!< The track content objects (segments) */ + m_color( 0, 0, 0 ), + m_hasColor( false ) { m_trackContainer->addTrack( this ); m_height = -1; @@ -2431,7 +2595,12 @@ void Track::saveSettings( QDomDocument & doc, QDomElement & element ) { element.setAttribute( "trackheight", m_height ); } - + + if( m_hasColor ) + { + element.setAttribute( "color", m_color.name() ); + } + QDomElement tsDe = doc.createElement( nodeName() ); // let actual track (InstrumentTrack, bbTrack, sampleTrack etc.) save // its settings @@ -2484,6 +2653,12 @@ void Track::loadSettings( const QDomElement & element ) // Older project files that didn't have this attribute will set the value to false (issue 5562) m_mutedBeforeSolo = QVariant( element.attribute( "mutedBeforeSolo", "0" ) ).toBool(); + if( element.hasAttribute( "color" ) ) + { + m_color.setNamedColor( element.attribute( "color" ) ); + m_hasColor = true; + } + if( m_simpleSerializingMode ) { QDomNode node = element.firstChild(); @@ -2861,7 +3036,24 @@ void Track::toggleSolo() } } +void Track::trackColorChanged( QColor & c ) +{ + for (int i = 0; i < numOfTCOs(); i++) + { + m_trackContentObjects[i]->updateColor(); + } + m_hasColor = true; + m_color = c; +} +void Track::trackColorReset() +{ + for (int i = 0; i < numOfTCOs(); i++) + { + m_trackContentObjects[i]->updateColor(); + } + m_hasColor = false; +} BoolModel *Track::getMutedModel() @@ -2932,6 +3124,13 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : connect( &m_track->m_soloModel, SIGNAL( dataChanged() ), m_track, SLOT( toggleSolo() ), Qt::DirectConnection ); + + connect( &m_trackOperationsWidget, SIGNAL( colorChanged( QColor & ) ), + m_track, SLOT( trackColorChanged( QColor & ) ) ); + + connect( &m_trackOperationsWidget, SIGNAL( colorReset() ), + m_track, SLOT( trackColorReset() ) ); + // create views for already existing TCOs for( Track::tcoVector::iterator it = m_track->m_trackContentObjects.begin(); diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 6daba567b0f..1c9ac03a418 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -216,8 +216,6 @@ void AutomationPatternView::constructContextMenu( QMenu * _cm ) this, SLOT( disconnectObject( QAction * ) ) ); _cm->addMenu( m ); } - - _cm->addSeparator(); } @@ -256,13 +254,9 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) QPainter p( &m_paintPixmap ); QLinearGradient lingrad( 0, 0, 0, height() ); - QColor c; + QColor c = getColorForDisplay( painter.background().color() ); bool muted = m_pat->getTrack()->isMuted() || m_pat->isMuted(); bool current = gui->automationEditor()->currentPattern() == m_pat; - - // state: selected, muted, normal - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() - : painter.background().color() ); lingrad.setColorAt( 1, c.darker( 300 ) ); lingrad.setColorAt( 0, c ); diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 24d0fc9bdd9..2bd0fdaf546 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -23,7 +23,6 @@ */ #include "BBTrack.h" -#include #include #include @@ -32,8 +31,8 @@ #include "embed.h" #include "Engine.h" #include "gui_templates.h" -#include "MainWindow.h" #include "GuiApplication.h" +#include "MainWindow.h" #include "Mixer.h" #include "RenameDialog.h" #include "Song.h" @@ -47,9 +46,7 @@ BBTrack::infoMap BBTrack::s_infoMap; BBTCO::BBTCO( Track * _track ) : - TrackContentObject( _track ), - m_color( 128, 128, 128 ), - m_useStyleColor( true ) + TrackContentObject( _track ) { bar_t t = Engine::getBBTrackContainer()->lengthOfBB( bbTrackIndex() ); if( t > 0 ) @@ -74,15 +71,9 @@ void BBTCO::saveSettings( QDomDocument & doc, QDomElement & element ) } element.setAttribute( "len", length() ); element.setAttribute( "muted", isMuted() ); - element.setAttribute( "color", color() ); - - if( m_useStyleColor ) + if( usesCustomClipColor() ) { - element.setAttribute( "usestyle", 1 ); - } - else - { - element.setAttribute( "usestyle", 0 ); + element.setAttribute( "color", color().name() ); } } @@ -101,33 +92,21 @@ void BBTCO::loadSettings( const QDomElement & element ) { toggleMute(); } - - if( element.hasAttribute( "color" ) ) - { - setColor( QColor( element.attribute( "color" ).toUInt() ) ); - } - if( element.hasAttribute( "usestyle" ) ) + // for colors saved in 1.3-onwards + if( element.hasAttribute( "color" ) && !element.hasAttribute( "usestyle" ) ) { - if( element.attribute( "usestyle" ).toUInt() == 1 ) - { - m_useStyleColor = true; - } - else - { - m_useStyleColor = false; - } + useCustomClipColor( true ); + setColor( element.attribute( "color" ) ); } + + // for colors saved before 1.3 else { - if( m_color.rgb() == qRgb( 128, 182, 175 ) || m_color.rgb() == qRgb( 64, 128, 255 ) ) // old or older default color - { - m_useStyleColor = true; - } - else - { - m_useStyleColor = false; - } + if( element.hasAttribute( "color" ) ) + { setColor( QColor( element.attribute( "color" ).toUInt() ) ); } + + // usestyle attribute is no longer used } } @@ -153,7 +132,8 @@ BBTCOView::BBTCOView( TrackContentObject * _tco, TrackView * _tv ) : m_bbTCO( dynamic_cast( _tco ) ), m_paintPixmap() { - connect( _tco->getTrack(), SIGNAL( dataChanged() ), this, SLOT( update() ) ); + connect( _tco->getTrack(), SIGNAL( dataChanged() ), + this, SLOT( update() ) ); setStyle( QApplication::style() ); } @@ -173,10 +153,6 @@ void BBTCOView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "edit_rename" ), tr( "Change name" ), this, SLOT( changeName() ) ); - _cm->addAction( embed::getIconPixmap( "colorize" ), - tr( "Change color" ), this, SLOT( changeColor() ) ); - _cm->addAction( embed::getIconPixmap( "colorize" ), - tr( "Reset color to default" ), this, SLOT( resetColor() ) ); } @@ -210,13 +186,7 @@ void BBTCOView::paintEvent( QPaintEvent * ) QPainter p( &m_paintPixmap ); QLinearGradient lingrad( 0, 0, 0, height() ); - QColor c; - bool muted = m_bbTCO->getTrack()->isMuted() || m_bbTCO->isMuted(); - - // state: selected, muted, default, user selected - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() - : ( m_bbTCO->m_useStyleColor ? painter.background().color() - : m_bbTCO->colorObj() ) ); + QColor c = getColorForDisplay( painter.background().color() ); lingrad.setColorAt( 0, c.lighter( 130 ) ); lingrad.setColorAt( 1, c.lighter( 70 ) ); @@ -305,63 +275,6 @@ void BBTCOView::changeName() - -void BBTCOView::changeColor() -{ - QColor new_color = QColorDialog::getColor( m_bbTCO->m_color ); - if( ! new_color.isValid() ) - { - return; - } - if( isSelected() ) - { - QVector selected = - gui->songEditor()->m_editor->selectedObjects(); - for( QVector::iterator it = - selected.begin(); - it != selected.end(); ++it ) - { - BBTCOView * bb_tcov = dynamic_cast( *it ); - if( bb_tcov ) - { - bb_tcov->setColor( new_color ); - } - } - } - else - { - setColor( new_color ); - } -} - - -/** \brief Makes the BB pattern use the colour defined in the stylesheet */ -void BBTCOView::resetColor() -{ - if( ! m_bbTCO->m_useStyleColor ) - { - m_bbTCO->m_useStyleColor = true; - Engine::getSong()->setModified(); - update(); - } - BBTrack::clearLastTCOColor(); -} - - - -void BBTCOView::setColor( QColor new_color ) -{ - if( new_color.rgb() != m_bbTCO->color() ) - { - m_bbTCO->setColor( new_color ); - m_bbTCO->m_useStyleColor = false; - Engine::getSong()->setModified(); - update(); - } - BBTrack::setLastTCOColor( new_color ); -} - - void BBTCOView::update() { ToolTip::add(this, m_bbTCO->name()); @@ -371,8 +284,6 @@ void BBTCOView::update() -QColor * BBTrack::s_lastTCOColor = NULL; - BBTrack::BBTrack( TrackContainer* tc ) : Track( Track::BBTrack, tc ) { @@ -474,11 +385,6 @@ TrackView * BBTrack::createView( TrackContainerView* tcv ) TrackContentObject * BBTrack::createTCO( const MidiTime & _pos ) { BBTCO * bbtco = new BBTCO( this ); - if( s_lastTCOColor ) - { - bbtco->setColor( *s_lastTCOColor ); - bbtco->setUseStyleColor( false ); - } return bbtco; } diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index e5de8b83b68..69d575813b6 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -30,11 +30,13 @@ #include #include #include +#include #include "AudioSampleRecorder.h" #include "BBTrackContainer.h" #include "DeprecationHelper.h" #include "embed.h" +#include "gui_templates.h" #include "GuiApplication.h" #include "InstrumentTrack.h" #include "PianoRoll.h" @@ -356,6 +358,11 @@ void Pattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) { _this.setAttribute( "type", m_patternType ); _this.setAttribute( "name", name() ); + + if( usesCustomClipColor() ) + { + _this.setAttribute( "color", color().name() ); + } // as the target of copied/dragged pattern is always an existing // pattern, we must not store actual position, instead we store -1 // which tells loadSettings() not to mess around with position @@ -387,6 +394,13 @@ void Pattern::loadSettings( const QDomElement & _this ) m_patternType = static_cast( _this.attribute( "type" ).toInt() ); setName( _this.attribute( "name" ) ); + + if( _this.hasAttribute( "color" ) ) + { + useCustomClipColor( true ); + setColor( _this.attribute( "color" ) ); + } + if( _this.attribute( "pos" ).toInt() >= 0 ) { movePosition( _this.attribute( "pos" ).toInt() ); @@ -853,14 +867,20 @@ void PatternView::paintEvent( QPaintEvent * ) QPainter p( &m_paintPixmap ); + QColor c; bool const muted = m_pat->getTrack()->isMuted() || m_pat->isMuted(); bool current = gui->pianoRoll()->currentPattern() == m_pat; bool beatPattern = m_pat->m_patternType == Pattern::BeatPattern; - - // state: selected, normal, beat pattern, muted - QColor c = isSelected() ? selectedColor() : ( ( !muted && !beatPattern ) - ? painter.background().color() : ( beatPattern - ? BBPatternBackground() : mutedBackgroundColor() ) ); + + if( beatPattern ) + { + // Do not paint BBTCOs how we paint pattern TCOs + c = BBPatternBackground(); + } + else + { + c = getColorForDisplay( painter.background().color() ); + } // invert the gradient for the background in the B&B editor QLinearGradient lingrad( 0, 0, 0, height() ); @@ -973,7 +993,8 @@ void PatternView::paintEvent( QPaintEvent * ) // set colour based on mute status QColor noteFillColor = muted ? getMutedNoteFillColor() : getNoteFillColor(); - QColor noteBorderColor = muted ? getMutedNoteBorderColor() : getNoteBorderColor(); + QColor noteBorderColor = muted ? getMutedNoteBorderColor() + : ( m_pat->hasColor() ? c.lighter( 200 ) : getNoteBorderColor() ); bool const drawAsLines = height() < 64; if (drawAsLines) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 80d41adea7f..6515be3e42a 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -275,7 +275,11 @@ void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "data", m_sampleBuffer->toBase64( s ) ); } - _this.setAttribute ("sample_rate", m_sampleBuffer->sampleRate()); + _this.setAttribute( "sample_rate", m_sampleBuffer->sampleRate()); + if( usesCustomClipColor() ) + { + _this.setAttribute( "color", color().name() ); + } // TODO: start- and end-frame } @@ -297,8 +301,14 @@ void SampleTCO::loadSettings( const QDomElement & _this ) setMuted( _this.attribute( "muted" ).toInt() ); setStartTimeOffset( _this.attribute( "off" ).toInt() ); - if (_this.hasAttribute("sample_rate")) { - m_sampleBuffer->setSampleRate(_this.attribute("sample_rate").toInt()); + if ( _this.hasAttribute( "sample_rate" ) ) { + m_sampleBuffer->setSampleRate( _this.attribute( "sample_rate" ).toInt() ); + } + + if( _this.hasAttribute( "color" ) ) + { + useCustomClipColor( true ); + setColor( _this.attribute( "color" ) ); } } @@ -397,6 +407,14 @@ void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) /*contextMenu.addAction( embed::getIconPixmap( "record" ), tr( "Set/clear record" ), m_tco, SLOT( toggleRecord() ) );*/ + + contextMenu.addSeparator(); + + contextMenu.addAction( embed::getIconPixmap( "colorize" ), + tr( "Set clip color" ), this, SLOT( changeClipColor() ) ); + contextMenu.addAction( embed::getIconPixmap( "colorize" ), + tr( "Use track color" ), this, SLOT( useTrackColor() ) ); + constructContextMenu( &contextMenu ); contextMenu.exec( QCursor::pos() ); @@ -525,13 +543,9 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) QPainter p( &m_paintPixmap ); QLinearGradient lingrad( 0, 0, 0, height() ); - QColor c; + QColor c = getColorForDisplay( painter.background().color() ); bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); - // state: selected, muted, normal - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() - : painter.background().color() ); - lingrad.setColorAt( 1, c.darker( 300 ) ); lingrad.setColorAt( 0, c ); @@ -571,12 +585,12 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) p.setRenderHint( QPainter::Antialiasing, false ); // inner border - p.setPen( c.lighter( 160 ) ); + p.setPen( c.lighter( 135 ) ); p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, rect().bottom() - TCO_BORDER_WIDTH ); // outer border - p.setPen( c.darker( 300 ) ); + p.setPen( c.darker( 200 ) ); p.drawRect( 0, 0, rect().right(), rect().bottom() ); // draw the 'muted' pixmap only if the pattern was manualy muted From 5d0340f6ae9a618cc44f153f293b781a2e48597a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 24 Oct 2020 17:24:08 +0200 Subject: [PATCH 117/180] Fix undefined Lv2Ports::Audio::m_optional after 3fa4b98 --- include/Lv2Ports.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 8241b3e8af4..e61f9bbd1cf 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -190,7 +190,6 @@ struct Audio : public VisitablePort //! the buffer where Lv2 reads/writes the data from/to std::vector m_buffer; bool m_sidechain; - bool m_optional; // the only case when data of m_buffer may be referenced: friend struct ::ConnectPortVisitor; From 7808e0b92fdb7f84180553fa534d66b2f2f041c6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 24 Oct 2020 17:24:40 +0200 Subject: [PATCH 118/180] Lv2 Fix invalid memory access if plugin did not load --- src/core/lv2/Lv2Proc.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 0922ac242ed..925dca5bd0c 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -396,9 +396,12 @@ void Lv2Proc::initPlugin() void Lv2Proc::shutdownPlugin() { - lilv_instance_deactivate(m_instance); - lilv_instance_free(m_instance); - m_instance = nullptr; + if (m_valid) + { + lilv_instance_deactivate(m_instance); + lilv_instance_free(m_instance); + m_instance = nullptr; + } } From 5864aea3d325238e2fcc3c076a3f6d81454a4015 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sun, 25 Oct 2020 09:59:39 -0300 Subject: [PATCH 119/180] Fixes bug with cloning Automation Tracks Fixes bug from issue #5595. When cloning an automation track, the IDs from the recently created AutomationPatterns weren't being resolved, causing them to show as disconnected automations. This PR fixes the issue by adding a call to AutomationPattern::resolveAllIDs() on the Track::clone() method. It also fixes the code style on that method. --- src/core/Track.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 70da1e1d73c..f2630b31829 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -2554,12 +2554,15 @@ Track * Track::create( const QDomElement & element, TrackContainer * tc ) /*! \brief Clone a track from this track * */ -Track * Track::clone() +Track* Track::clone() { QDomDocument doc; - QDomElement parent = doc.createElement( "clone" ); - saveState( doc, parent ); - return create( parent.firstChild().toElement(), m_trackContainer ); + QDomElement parent = doc.createElement("clone"); + saveState(doc, parent); + Track* t = create(parent.firstChild().toElement(), m_trackContainer); + + AutomationPattern::resolveAllIDs(); + return t; } From 090b5a775267e88b9862ea7e8051c0ccde531c52 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 28 Oct 2020 17:20:12 -0400 Subject: [PATCH 120/180] Fix DMG titles > 27 chars (#5741) Workaround upstream bug https://github.com/LinusU/node-appdmg/issues/48 --- cmake/apple/CMakeLists.txt | 5 +++++ cmake/apple/package_apple.json.in | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/apple/CMakeLists.txt b/cmake/apple/CMakeLists.txt index 835d886b9b6..0b66689e75d 100644 --- a/cmake/apple/CMakeLists.txt +++ b/cmake/apple/CMakeLists.txt @@ -9,6 +9,11 @@ SET(MACOSX_BUNDLE_MIMETYPE "application/x-lmms-project") SET(MACOSX_BUNDLE_MIMETYPE_ICON "project.icns") SET(MACOSX_BUNDLE_MIMETYPE_ID "io.lmms") SET(MACOSX_BUNDLE_PROJECT_URL "${PROJECT_URL}") +SET(MACOSX_BUNDLE_DMG_TITLE "${MACOSX_BUNDLE_BUNDLE_NAME} ${MACOSX_BUNDLE_LONG_VERSION_STRING}") + +# FIXME: appdmg won't allow volume names > 27 char +# See also https://github.com/LinusU/node-appdmg/issues/48 +STRING(SUBSTRING "${MACOSX_BUNDLE_DMG_TITLE}" 0 27 MACOSX_BUNDLE_DMG_TITLE) CONFIGURE_FILE("lmms.plist.in" "${CMAKE_BINARY_DIR}/Info.plist") CONFIGURE_FILE("install_apple.sh.in" "${CMAKE_BINARY_DIR}/install_apple.sh" @ONLY) diff --git a/cmake/apple/package_apple.json.in b/cmake/apple/package_apple.json.in index 1d1147cbf21..f6660b345bc 100644 --- a/cmake/apple/package_apple.json.in +++ b/cmake/apple/package_apple.json.in @@ -1,5 +1,5 @@ { - "title": "@MACOSX_BUNDLE_BUNDLE_NAME@ @MACOSX_BUNDLE_LONG_VERSION_STRING@", + "title": "@MACOSX_BUNDLE_DMG_TITLE@", "background": "@CMAKE_SOURCE_DIR@/cmake/apple/dmg_branding.png", "icon-size": 128, "contents": [ From c23c1b79d5fb068e60d129e4bf5e8d77bca268ff Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Thu, 29 Oct 2020 01:06:34 -0400 Subject: [PATCH 121/180] Add Carla support for Windows (#5713) --- plugins/carlabase/CMakeLists.txt | 19 ++++++++++++++----- plugins/carlabase/carla.cpp | 28 +++++++++++++++++----------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/plugins/carlabase/CMakeLists.txt b/plugins/carlabase/CMakeLists.txt index 28a1bc88c51..d382fceb8c5 100644 --- a/plugins/carlabase/CMakeLists.txt +++ b/plugins/carlabase/CMakeLists.txt @@ -14,13 +14,22 @@ if(LMMS_HAVE_WEAKCARLA) ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/utils ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend ) - ADD_LIBRARY(carla_native-plugin SHARED DummyCarla.cpp) - TARGET_INCLUDE_DIRECTORIES(carla_native-plugin PUBLIC ${CARLA_INCLUDE_DIRS}) - INSTALL(TARGETS carla_native-plugin + + IF(LMMS_BUILD_WIN32) + # use carla.dll + SET(CMAKE_SHARED_LIBRARY_PREFIX "") + SET(CARLA_NATIVE_LIB carla) + ELSE() + # use libcarla_native-plugin + SET(CARLA_NATIVE_LIB carla_native-plugin) + ENDIF() + ADD_LIBRARY(${CARLA_NATIVE_LIB} SHARED DummyCarla.cpp) + TARGET_INCLUDE_DIRECTORIES(${CARLA_NATIVE_LIB} PUBLIC ${CARLA_INCLUDE_DIRS}) + INSTALL(TARGETS ${CARLA_NATIVE_LIB} LIBRARY DESTINATION "${PLUGIN_DIR}/optional" RUNTIME DESTINATION "${PLUGIN_DIR}/optional" ) - SET(CARLA_LIBRARIES carla_native-plugin) + SET(CARLA_LIBRARIES ${CARLA_NATIVE_LIB}) # Set parent scope variables so carlarack and carlapatchbay can see them SET(CARLA_LIBRARIES ${CARLA_LIBRARIES} PARENT_SCOPE) endif() @@ -45,6 +54,6 @@ if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) INSTALL_RPATH_USE_LINK_PATH TRUE INSTALL_RPATH "${CARLA_RPATH}") IF(LMMS_HAVE_WEAKCARLA) - ADD_DEPENDENCIES(carlabase carla_native-plugin) + ADD_DEPENDENCIES(carlabase ${CARLA_NATIVE_LIB}) ENDIF() endif() diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index f437431271c..a01ee625afe 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -142,20 +142,14 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D fHost.uiParentId = 0; // carla/resources contains PyQt scripts required for launch - QString dllName(carla_get_library_filename()); - QString resourcesPath; + QDir path(carla_get_library_folder()); #if defined(CARLA_OS_LINUX) - // parse prefix from dll filename - QDir path = QFileInfo(dllName).dir(); path.cdUp(); path.cdUp(); - resourcesPath = path.absolutePath() + "/share/carla/resources"; -#elif defined(CARLA_OS_MAC) + QString resourcesPath = path.absolutePath() + "/share/carla/resources"; +#else // parse prefix from dll filename - QDir path = QFileInfo(dllName).dir(); - resourcesPath = path.absolutePath() + "/resources"; -#elif defined(CARLA_OS_WIN32) || defined(CARLA_OS_WIN64) - // not yet supported + QString resourcesPath = path.absolutePath() + "/resources"; #endif fHost.resourceDir = strdup(resourcesPath.toUtf8().constData()); fHost.get_buffer_size = host_get_buffer_size; @@ -444,8 +438,20 @@ CarlaInstrumentView::~CarlaInstrumentView() void CarlaInstrumentView::toggleUI(bool visible) { - if (fHandle != NULL && fDescriptor->ui_show != NULL) + if (fHandle != NULL && fDescriptor->ui_show != NULL) { +// TODO: remove when fixed upstream +// change working path to location of carla.dll to avoid conflict with lmms +#if defined(CARLA_OS_WIN32) || defined(CARLA_OS_WIN64) + if (visible) { + QString backupDir = QDir::currentPath(); + QDir::setCurrent(carla_get_library_folder()); + fDescriptor->ui_show(fHandle, true); + QDir::setCurrent(backupDir); + return; + } +#endif fDescriptor->ui_show(fHandle, visible); + } } void CarlaInstrumentView::uiClosed() From ec2e854a47c477d38effa93d1f211d500af23377 Mon Sep 17 00:00:00 2001 From: Frank Lyder <213777+TheKnarf@users.noreply.github.com> Date: Fri, 30 Oct 2020 02:57:15 +0100 Subject: [PATCH 122/180] Fix AppleMIDI crash when deviceName or endPointName is null (#5729) --- src/core/midi/MidiApple.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp index f4bc0d4dd97..0575b76ae51 100644 --- a/src/core/midi/MidiApple.cpp +++ b/src/core/midi/MidiApple.cpp @@ -401,9 +401,9 @@ void MidiApple::midiInClose( MIDIEndpointRef reference ) char *getName( MIDIObjectRef &object ) { // Returns the name of a given MIDIObjectRef as char * - CFStringRef name = nil; + CFStringRef name = nullptr; if (noErr != MIDIObjectGetStringProperty(object, kMIDIPropertyName, &name)) - return nil; + return nullptr; int len = CFStringGetLength(name)+1; char *value = (char *) malloc(len); @@ -615,8 +615,12 @@ char * MidiApple::getFullName(MIDIEndpointRef &endpoint_ref) char * deviceName = getName(device); char * endPointName = getName(endpoint_ref); qDebug("device name='%s' endpoint name='%s'",deviceName,endPointName); - char * fullName = (char *)malloc(strlen(deviceName) + strlen(":") + strlen(endPointName)+1); + size_t deviceNameLen = deviceName == nullptr ? 0 : strlen(deviceName); + size_t endPointNameLen = endPointName == nullptr ? 0 : strlen(endPointName); + char * fullName = (char *)malloc(deviceNameLen + endPointNameLen + 2); sprintf(fullName, "%s:%s", deviceName,endPointName); + if (deviceName != nullptr) { free(deviceName); } + if (endPointName != nullptr) { free(endPointName); } return fullName; } From 435dbc5e00fa06cd789693a56e6b340f4c7722ab Mon Sep 17 00:00:00 2001 From: firewall1110 <57725851+firewall1110@users.noreply.github.com> Date: Fri, 30 Oct 2020 06:16:27 +0200 Subject: [PATCH 123/180] Fix crash/freezing after rendering when using soundio/JACK (#5681) --- include/AudioSoundIo.h | 1 + src/core/audio/AudioJack.cpp | 1 + src/core/audio/AudioSoundIo.cpp | 39 ++++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h index 79b586f0645..6a740a02400 100644 --- a/include/AudioSoundIo.h +++ b/include/AudioSoundIo.h @@ -110,6 +110,7 @@ class AudioSoundIo : public AudioDevice fpp_t m_outBufFrameIndex; bool m_stopped; + bool m_outstreamStarted; int m_disconnectErr; void onBackendDisconnect(int err); diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 44ecafa23a0..7e7b703a1a3 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -204,6 +204,7 @@ void AudioJack::startProcessing() { if( m_active || m_client == NULL ) { + m_stopped = false; return; } diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 2c3d493a6de..b2be4884f93 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -50,6 +50,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, Mixer * _mixer ) : m_outBufFrameIndex = 0; m_outBufFramesTotal = 0; m_stopped = true; + m_outstreamStarted = false; m_soundio = soundio_create(); if (!m_soundio) @@ -196,6 +197,12 @@ void AudioSoundIo::onBackendDisconnect(int err) AudioSoundIo::~AudioSoundIo() { stopProcessing(); + + if (m_outstream) + { + soundio_outstream_destroy(m_outstream); + } + if (m_soundio) { soundio_destroy(m_soundio); @@ -205,28 +212,50 @@ AudioSoundIo::~AudioSoundIo() void AudioSoundIo::startProcessing() { + int err; + m_outBufFrameIndex = 0; m_outBufFramesTotal = 0; m_outBufSize = mixer()->framesPerPeriod(); m_outBuf = new surroundSampleFrame[m_outBufSize]; + if (! m_outstreamStarted) + { + if ((err = soundio_outstream_start(m_outstream))) + { + fprintf(stderr, + "AudioSoundIo::startProcessing() :: soundio unable to start stream: %s\n", + soundio_strerror(err)); + } else { + m_outstreamStarted = true; + } + } + m_stopped = false; - int err; - if ((err = soundio_outstream_start(m_outstream))) + + if ((err = soundio_outstream_pause(m_outstream, false))) { m_stopped = true; - fprintf(stderr, "soundio unable to start stream: %s\n", soundio_strerror(err)); + fprintf(stderr, + "AudioSoundIo::startProcessing() :: resuming result error: %s\n", + soundio_strerror(err)); } } void AudioSoundIo::stopProcessing() { + int err; + m_stopped = true; if (m_outstream) { - soundio_outstream_destroy(m_outstream); - m_outstream = NULL; + if (err = soundio_outstream_pause(m_outstream, true)) + { + fprintf(stderr, + "AudioSoundIo::stopProcessing() :: pausing result error: %s\n", + soundio_strerror(err)); + } } if (m_outBuf) From 9d104b2205f63bd4425905e1261ffd63f57baba9 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Fri, 30 Oct 2020 13:34:15 -0400 Subject: [PATCH 124/180] Switch Sid to submodule (#5724) Also refactors to TitleCase, uses newer SID namespace --- .gitmodules | 3 + cmake/modules/PluginList.cmake | 2 +- plugins/{sid => Sid}/3off.png | Bin plugins/{sid => Sid}/3offred.png | Bin plugins/{sid => Sid}/6581.png | Bin plugins/{sid => Sid}/6581red.png | Bin plugins/{sid => Sid}/8580.png | Bin plugins/{sid => Sid}/8580red.png | Bin plugins/Sid/CMakeLists.txt | 51 + .../SidInstrument.cpp} | 50 +- .../sid_instrument.h => Sid/SidInstrument.h} | 22 +- plugins/{sid => Sid}/artwork.png | Bin plugins/{sid => Sid}/bp.png | Bin plugins/{sid => Sid}/bpred.png | Bin plugins/{sid => Sid}/filterred.png | Bin plugins/{sid => Sid}/hp.png | Bin plugins/{sid => Sid}/hpred.png | Bin plugins/{sid => Sid}/logo.png | Bin plugins/{sid => Sid}/lp.png | Bin plugins/{sid => Sid}/lpred.png | Bin plugins/{sid => Sid}/noise.png | Bin plugins/{sid => Sid}/noisered.png | Bin plugins/{sid => Sid}/pulse.png | Bin plugins/{sid => Sid}/pulsered.png | Bin plugins/Sid/resid | 1 + plugins/{sid => Sid}/ring.png | Bin plugins/{sid => Sid}/ringred.png | Bin plugins/{sid => Sid}/saw.png | Bin plugins/{sid => Sid}/sawred.png | Bin plugins/{sid => Sid}/sync.png | Bin plugins/{sid => Sid}/syncred.png | Bin plugins/{sid => Sid}/test.png | Bin plugins/{sid => Sid}/testred.png | Bin plugins/{sid => Sid}/triangle.png | Bin plugins/{sid => Sid}/trianglered.png | Bin plugins/sid/CMakeLists.txt | 5 - plugins/sid/envelope.cc | 227 ---- plugins/sid/envelope.h | 305 ----- plugins/sid/extfilt.cc | 79 -- plugins/sid/extfilt.h | 164 --- plugins/sid/filter.cc | 305 ----- plugins/sid/filter.h | 531 --------- plugins/sid/filter.png | Bin 1422 -> 0 bytes plugins/sid/pot.cc | 26 - plugins/sid/pot.h | 31 - plugins/sid/sid.cc | 1019 ----------------- plugins/sid/sid.h | 146 --- plugins/sid/siddefs.h | 69 -- plugins/sid/spline.h | 272 ----- plugins/sid/version.cc | 21 - plugins/sid/voice.cc | 132 --- plugins/sid/voice.h | 77 -- plugins/sid/wave.cc | 144 --- plugins/sid/wave.h | 503 -------- plugins/sid/wave6581_PST.cc | 536 --------- plugins/sid/wave6581_PS_.cc | 536 --------- plugins/sid/wave6581_P_T.cc | 536 --------- plugins/sid/wave6581__ST.cc | 536 --------- plugins/sid/wave8580_PST.cc | 536 --------- plugins/sid/wave8580_PS_.cc | 536 --------- plugins/sid/wave8580_P_T.cc | 536 --------- plugins/sid/wave8580__ST.cc | 536 --------- 62 files changed, 92 insertions(+), 8381 deletions(-) rename plugins/{sid => Sid}/3off.png (100%) rename plugins/{sid => Sid}/3offred.png (100%) rename plugins/{sid => Sid}/6581.png (100%) rename plugins/{sid => Sid}/6581red.png (100%) rename plugins/{sid => Sid}/8580.png (100%) rename plugins/{sid => Sid}/8580red.png (100%) create mode 100644 plugins/Sid/CMakeLists.txt rename plugins/{sid/sid_instrument.cpp => Sid/SidInstrument.cpp} (94%) rename plugins/{sid/sid_instrument.h => Sid/SidInstrument.h} (89%) rename plugins/{sid => Sid}/artwork.png (100%) rename plugins/{sid => Sid}/bp.png (100%) rename plugins/{sid => Sid}/bpred.png (100%) rename plugins/{sid => Sid}/filterred.png (100%) rename plugins/{sid => Sid}/hp.png (100%) rename plugins/{sid => Sid}/hpred.png (100%) rename plugins/{sid => Sid}/logo.png (100%) rename plugins/{sid => Sid}/lp.png (100%) rename plugins/{sid => Sid}/lpred.png (100%) rename plugins/{sid => Sid}/noise.png (100%) rename plugins/{sid => Sid}/noisered.png (100%) rename plugins/{sid => Sid}/pulse.png (100%) rename plugins/{sid => Sid}/pulsered.png (100%) create mode 160000 plugins/Sid/resid rename plugins/{sid => Sid}/ring.png (100%) rename plugins/{sid => Sid}/ringred.png (100%) rename plugins/{sid => Sid}/saw.png (100%) rename plugins/{sid => Sid}/sawred.png (100%) rename plugins/{sid => Sid}/sync.png (100%) rename plugins/{sid => Sid}/syncred.png (100%) rename plugins/{sid => Sid}/test.png (100%) rename plugins/{sid => Sid}/testred.png (100%) rename plugins/{sid => Sid}/triangle.png (100%) rename plugins/{sid => Sid}/trianglered.png (100%) delete mode 100644 plugins/sid/CMakeLists.txt delete mode 100644 plugins/sid/envelope.cc delete mode 100644 plugins/sid/envelope.h delete mode 100644 plugins/sid/extfilt.cc delete mode 100644 plugins/sid/extfilt.h delete mode 100644 plugins/sid/filter.cc delete mode 100644 plugins/sid/filter.h delete mode 100644 plugins/sid/filter.png delete mode 100644 plugins/sid/pot.cc delete mode 100644 plugins/sid/pot.h delete mode 100644 plugins/sid/sid.cc delete mode 100644 plugins/sid/sid.h delete mode 100644 plugins/sid/siddefs.h delete mode 100644 plugins/sid/spline.h delete mode 100644 plugins/sid/version.cc delete mode 100644 plugins/sid/voice.cc delete mode 100644 plugins/sid/voice.h delete mode 100644 plugins/sid/wave.cc delete mode 100644 plugins/sid/wave.h delete mode 100644 plugins/sid/wave6581_PST.cc delete mode 100644 plugins/sid/wave6581_PS_.cc delete mode 100644 plugins/sid/wave6581_P_T.cc delete mode 100644 plugins/sid/wave6581__ST.cc delete mode 100644 plugins/sid/wave8580_PST.cc delete mode 100644 plugins/sid/wave8580_PS_.cc delete mode 100644 plugins/sid/wave8580_P_T.cc delete mode 100644 plugins/sid/wave8580__ST.cc diff --git a/.gitmodules b/.gitmodules index ebc1502e265..5065883f18f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,3 +40,6 @@ [submodule "plugins/carlabase/carla"] path = plugins/carlabase/carla url = https://github.com/falktx/carla +[submodule "plugins/sid/resid"] + path = plugins/Sid/resid + url = https://github.com/simonowen/resid diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index a2871bf99ec..ed4ffd2eb10 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -56,7 +56,7 @@ SET(LMMS_PLUGIN_LIST ReverbSC sf2_player sfxr - sid + Sid SpectrumAnalyzer stereo_enhancer stereo_matrix diff --git a/plugins/sid/3off.png b/plugins/Sid/3off.png similarity index 100% rename from plugins/sid/3off.png rename to plugins/Sid/3off.png diff --git a/plugins/sid/3offred.png b/plugins/Sid/3offred.png similarity index 100% rename from plugins/sid/3offred.png rename to plugins/Sid/3offred.png diff --git a/plugins/sid/6581.png b/plugins/Sid/6581.png similarity index 100% rename from plugins/sid/6581.png rename to plugins/Sid/6581.png diff --git a/plugins/sid/6581red.png b/plugins/Sid/6581red.png similarity index 100% rename from plugins/sid/6581red.png rename to plugins/Sid/6581red.png diff --git a/plugins/sid/8580.png b/plugins/Sid/8580.png similarity index 100% rename from plugins/sid/8580.png rename to plugins/Sid/8580.png diff --git a/plugins/sid/8580red.png b/plugins/Sid/8580red.png similarity index 100% rename from plugins/sid/8580red.png rename to plugins/Sid/8580red.png diff --git a/plugins/Sid/CMakeLists.txt b/plugins/Sid/CMakeLists.txt new file mode 100644 index 00000000000..c9fce7bb77d --- /dev/null +++ b/plugins/Sid/CMakeLists.txt @@ -0,0 +1,51 @@ +INCLUDE(BuildPlugin) + +INCLUDE_DIRECTORIES(resid) + +BUILD_PLUGIN(sid + SidInstrument.cpp + SidInstrument.h + resid/envelope.h + resid/extfilt.h + resid/filter.h + resid/pot.h + resid/siddefs.h + resid/sid.h + resid/spline.h + resid/voice.h + resid/wave.h + resid/envelope.cc + resid/extfilt.cc + resid/filter.cc + resid/pot.cc + resid/sid.cc + resid/version.cc + resid/voice.cc + resid/wave6581_PS_.cc + resid/wave6581_PST.cc + resid/wave6581_P_T.cc + resid/wave6581__ST.cc + resid/wave8580_PS_.cc + resid/wave8580_PST.cc + resid/wave8580_P_T.cc + resid/wave8580__ST.cc + resid/wave.cc + MOCFILES SidInstrument.h + EMBEDDED_RESOURCES *.png) + +# Parse VERSION +FILE(READ "resid/CMakeLists.txt" lines) +STRING(REGEX MATCH "set\\(MAJOR_VER [A-Za-z0-9_]*\\)" MAJOR_RAW ${lines}) +STRING(REGEX MATCH "set\\(MINOR_VER [A-Za-z0-9_]*\\)" MINOR_RAW ${lines}) +STRING(REGEX MATCH "set\\(PATCH_VER [A-Za-z0-9_]*\\)" PATCH_RAW ${lines}) +SEPARATE_ARGUMENTS(MAJOR_RAW) +SEPARATE_ARGUMENTS(MINOR_RAW) +SEPARATE_ARGUMENTS(PATCH_RAW) +LIST(GET MAJOR_RAW 1 MAJOR_RAW) +LIST(GET MINOR_RAW 1 MINOR_RAW) +LIST(GET PATCH_RAW 1 PATCH_RAW) +STRING(REPLACE ")" "" MAJOR_VER "${MAJOR_RAW}") +STRING(REPLACE ")" "" MINOR_VER "${MINOR_RAW}") +STRING(REPLACE ")" "" PATCH_VER "${PATCH_RAW}") + +TARGET_COMPILE_DEFINITIONS(sid PRIVATE VERSION="${MAJOR_VER}.${MINOR_VER}.${PATCH_VER}") diff --git a/plugins/sid/sid_instrument.cpp b/plugins/Sid/SidInstrument.cpp similarity index 94% rename from plugins/sid/sid_instrument.cpp rename to plugins/Sid/SidInstrument.cpp index 485fdfffb58..c2620021499 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -1,5 +1,5 @@ /* - * sid_instrument.cpp - ResID based software-synthesizer + * SidInstrument.cpp - ResID based software-synthesizer * * Copyright (c) 2008 Csaba Hruska * Attila Herman @@ -32,7 +32,7 @@ #include "sid.h" -#include "sid_instrument.h" +#include "SidInstrument.h" #include "Engine.h" #include "InstrumentTrack.h" #include "Knob.h" @@ -119,7 +119,7 @@ voiceObject::~voiceObject() } -sidInstrument::sidInstrument( InstrumentTrack * _instrument_track ) : +SidInstrument::SidInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &sid_plugin_descriptor ), // filter m_filterFCModel( 1024.0f, 0.0f, 2047.0f, 1.0f, this, tr( "Cutoff frequency" ) ), @@ -138,12 +138,12 @@ sidInstrument::sidInstrument( InstrumentTrack * _instrument_track ) : } -sidInstrument::~sidInstrument() +SidInstrument::~SidInstrument() { } -void sidInstrument::saveSettings( QDomDocument & _doc, +void SidInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) { // voices @@ -189,7 +189,7 @@ void sidInstrument::saveSettings( QDomDocument & _doc, -void sidInstrument::loadSettings( const QDomElement & _this ) +void SidInstrument::loadSettings( const QDomElement & _this ) { // voices for( int i = 0; i < 3; ++i ) @@ -223,7 +223,7 @@ void sidInstrument::loadSettings( const QDomElement & _this ) -QString sidInstrument::nodeName() const +QString SidInstrument::nodeName() const { return( sid_plugin_descriptor.name ); } @@ -231,7 +231,7 @@ QString sidInstrument::nodeName() const -f_cnt_t sidInstrument::desiredReleaseFrames() const +f_cnt_t SidInstrument::desiredReleaseFrames() const { const float samplerate = Engine::mixer()->processingSampleRate(); int maxrel = 0; @@ -247,7 +247,7 @@ f_cnt_t sidInstrument::desiredReleaseFrames() const -static int sid_fillbuffer(unsigned char* sidreg, cSID *sid, int tdelta, short *ptr, int samples) +static int sid_fillbuffer(unsigned char* sidreg, SID *sid, int tdelta, short *ptr, int samples) { int tdelta2; int result; @@ -302,7 +302,7 @@ static int sid_fillbuffer(unsigned char* sidreg, cSID *sid, int tdelta, short *p -void sidInstrument::playNote( NotePlayHandle * _n, +void SidInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { const f_cnt_t tfp = _n->totalFramesPlayed(); @@ -312,7 +312,7 @@ void sidInstrument::playNote( NotePlayHandle * _n, if ( tfp == 0 ) { - cSID *sid = new cSID(); + SID *sid = new SID(); sid->set_sampling_parameters( clockrate, SAMPLE_FAST, samplerate ); sid->set_chip_model( MOS8580 ); sid->enable_filter( true ); @@ -322,7 +322,7 @@ void sidInstrument::playNote( NotePlayHandle * _n, const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - cSID *sid = static_cast( _n->m_pluginData ); + SID *sid = static_cast( _n->m_pluginData ); int delta_t = clockrate * frames / samplerate + 4; // avoid variable length array for msvc compat short* buf = reinterpret_cast(_working_buffer + offset); @@ -446,17 +446,17 @@ void sidInstrument::playNote( NotePlayHandle * _n, -void sidInstrument::deleteNotePluginData( NotePlayHandle * _n ) +void SidInstrument::deleteNotePluginData( NotePlayHandle * _n ) { - delete static_cast( _n->m_pluginData ); + delete static_cast( _n->m_pluginData ); } -PluginView * sidInstrument::instantiateView( QWidget * _parent ) +PluginView * SidInstrument::instantiateView( QWidget * _parent ) { - return( new sidInstrumentView( this, _parent ) ); + return( new SidInstrumentView( this, _parent ) ); } @@ -481,7 +481,7 @@ class sidKnob : public Knob -sidInstrumentView::sidInstrumentView( Instrument * _instrument, +SidInstrumentView::SidInstrumentView( Instrument * _instrument, QWidget * _parent ) : InstrumentViewFixedSize( _instrument, _parent ) { @@ -657,13 +657,13 @@ sidInstrumentView::sidInstrumentView( Instrument * _instrument, } -sidInstrumentView::~sidInstrumentView() +SidInstrumentView::~SidInstrumentView() { } -void sidInstrumentView::updateKnobHint() +void SidInstrumentView::updateKnobHint() { - sidInstrument * k = castModel(); + SidInstrument * k = castModel(); for( int i = 0; i < 3; ++i ) { @@ -702,9 +702,9 @@ void sidInstrumentView::updateKnobHint() -void sidInstrumentView::updateKnobToolTip() +void SidInstrumentView::updateKnobToolTip() { - sidInstrument * k = castModel(); + SidInstrument * k = castModel(); for( int i = 0; i < 3; ++i ) { ToolTip::add( m_voiceKnobs[i].m_sustKnob, @@ -722,9 +722,9 @@ void sidInstrumentView::updateKnobToolTip() -void sidInstrumentView::modelChanged() +void SidInstrumentView::modelChanged() { - sidInstrument * k = castModel(); + SidInstrument * k = castModel(); m_volKnob->setModel( &k->m_volumeModel ); m_resKnob->setModel( &k->m_filterResonanceModel ); @@ -796,7 +796,7 @@ extern "C" // necessary for getting instance out of shared lib PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new sidInstrument( static_cast( m ) ) ); + return( new SidInstrument( static_cast( m ) ) ); } diff --git a/plugins/sid/sid_instrument.h b/plugins/Sid/SidInstrument.h similarity index 89% rename from plugins/sid/sid_instrument.h rename to plugins/Sid/SidInstrument.h index 479d09c5df1..c0998f0265d 100644 --- a/plugins/sid/sid_instrument.h +++ b/plugins/Sid/SidInstrument.h @@ -1,5 +1,5 @@ /* - * sid_Instrument.h - ResID based software-synthesizer + * SidInstrument.h - ResID based software-synthesizer * * Copyright (c) 2008 Csaba Hruska * Attila Herman @@ -33,7 +33,7 @@ #include "Knob.h" -class sidInstrumentView; +class SidInstrumentView; class NotePlayHandle; class automatableButtonGroup; class PixmapButton; @@ -67,11 +67,11 @@ class voiceObject : public Model BoolModel m_filteredModel; BoolModel m_testModel; - friend class sidInstrument; - friend class sidInstrumentView; + friend class SidInstrument; + friend class SidInstrumentView; } ; -class sidInstrument : public Instrument +class SidInstrument : public Instrument { Q_OBJECT public: @@ -89,8 +89,8 @@ class sidInstrument : public Instrument }; - sidInstrument( InstrumentTrack * _instrument_track ); - virtual ~sidInstrument(); + SidInstrument( InstrumentTrack * _instrument_track ); + virtual ~SidInstrument(); virtual void playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ); @@ -126,18 +126,18 @@ class sidInstrument : public Instrument IntModel m_chipModel; - friend class sidInstrumentView; + friend class SidInstrumentView; } ; -class sidInstrumentView : public InstrumentViewFixedSize +class SidInstrumentView : public InstrumentViewFixedSize { Q_OBJECT public: - sidInstrumentView( Instrument * _instrument, QWidget * _parent ); - virtual ~sidInstrumentView(); + SidInstrumentView( Instrument * _instrument, QWidget * _parent ); + virtual ~SidInstrumentView(); private: virtual void modelChanged(); diff --git a/plugins/sid/artwork.png b/plugins/Sid/artwork.png similarity index 100% rename from plugins/sid/artwork.png rename to plugins/Sid/artwork.png diff --git a/plugins/sid/bp.png b/plugins/Sid/bp.png similarity index 100% rename from plugins/sid/bp.png rename to plugins/Sid/bp.png diff --git a/plugins/sid/bpred.png b/plugins/Sid/bpred.png similarity index 100% rename from plugins/sid/bpred.png rename to plugins/Sid/bpred.png diff --git a/plugins/sid/filterred.png b/plugins/Sid/filterred.png similarity index 100% rename from plugins/sid/filterred.png rename to plugins/Sid/filterred.png diff --git a/plugins/sid/hp.png b/plugins/Sid/hp.png similarity index 100% rename from plugins/sid/hp.png rename to plugins/Sid/hp.png diff --git a/plugins/sid/hpred.png b/plugins/Sid/hpred.png similarity index 100% rename from plugins/sid/hpred.png rename to plugins/Sid/hpred.png diff --git a/plugins/sid/logo.png b/plugins/Sid/logo.png similarity index 100% rename from plugins/sid/logo.png rename to plugins/Sid/logo.png diff --git a/plugins/sid/lp.png b/plugins/Sid/lp.png similarity index 100% rename from plugins/sid/lp.png rename to plugins/Sid/lp.png diff --git a/plugins/sid/lpred.png b/plugins/Sid/lpred.png similarity index 100% rename from plugins/sid/lpred.png rename to plugins/Sid/lpred.png diff --git a/plugins/sid/noise.png b/plugins/Sid/noise.png similarity index 100% rename from plugins/sid/noise.png rename to plugins/Sid/noise.png diff --git a/plugins/sid/noisered.png b/plugins/Sid/noisered.png similarity index 100% rename from plugins/sid/noisered.png rename to plugins/Sid/noisered.png diff --git a/plugins/sid/pulse.png b/plugins/Sid/pulse.png similarity index 100% rename from plugins/sid/pulse.png rename to plugins/Sid/pulse.png diff --git a/plugins/sid/pulsered.png b/plugins/Sid/pulsered.png similarity index 100% rename from plugins/sid/pulsered.png rename to plugins/Sid/pulsered.png diff --git a/plugins/Sid/resid b/plugins/Sid/resid new file mode 160000 index 00000000000..02afcc5cefa --- /dev/null +++ b/plugins/Sid/resid @@ -0,0 +1 @@ +Subproject commit 02afcc5cefac34bd0c665dc0fa6b748d238c1831 diff --git a/plugins/sid/ring.png b/plugins/Sid/ring.png similarity index 100% rename from plugins/sid/ring.png rename to plugins/Sid/ring.png diff --git a/plugins/sid/ringred.png b/plugins/Sid/ringred.png similarity index 100% rename from plugins/sid/ringred.png rename to plugins/Sid/ringred.png diff --git a/plugins/sid/saw.png b/plugins/Sid/saw.png similarity index 100% rename from plugins/sid/saw.png rename to plugins/Sid/saw.png diff --git a/plugins/sid/sawred.png b/plugins/Sid/sawred.png similarity index 100% rename from plugins/sid/sawred.png rename to plugins/Sid/sawred.png diff --git a/plugins/sid/sync.png b/plugins/Sid/sync.png similarity index 100% rename from plugins/sid/sync.png rename to plugins/Sid/sync.png diff --git a/plugins/sid/syncred.png b/plugins/Sid/syncred.png similarity index 100% rename from plugins/sid/syncred.png rename to plugins/Sid/syncred.png diff --git a/plugins/sid/test.png b/plugins/Sid/test.png similarity index 100% rename from plugins/sid/test.png rename to plugins/Sid/test.png diff --git a/plugins/sid/testred.png b/plugins/Sid/testred.png similarity index 100% rename from plugins/sid/testred.png rename to plugins/Sid/testred.png diff --git a/plugins/sid/triangle.png b/plugins/Sid/triangle.png similarity index 100% rename from plugins/sid/triangle.png rename to plugins/Sid/triangle.png diff --git a/plugins/sid/trianglered.png b/plugins/Sid/trianglered.png similarity index 100% rename from plugins/sid/trianglered.png rename to plugins/Sid/trianglered.png diff --git a/plugins/sid/CMakeLists.txt b/plugins/sid/CMakeLists.txt deleted file mode 100644 index 3198723c05e..00000000000 --- a/plugins/sid/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -INCLUDE(BuildPlugin) - -BUILD_PLUGIN(sid sid_instrument.cpp sid_instrument.h envelope.h extfilt.h filter.h pot.h siddefs.h sid.h spline.h voice.h wave.h envelope.cc extfilt.cc filter.cc pot.cc sid.cc version.cc voice.cc wave6581_PS_.cc wave6581_PST.cc wave6581_P_T.cc wave6581__ST.cc wave8580_PS_.cc wave8580_PST.cc wave8580_P_T.cc wave8580__ST.cc wave.cc MOCFILES sid_instrument.h EMBEDDED_RESOURCES *.png) - - diff --git a/plugins/sid/envelope.cc b/plugins/sid/envelope.cc deleted file mode 100644 index e466bd67dc4..00000000000 --- a/plugins/sid/envelope.cc +++ /dev/null @@ -1,227 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#define __ENVELOPE_CC__ -#include "envelope.h" - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -EnvelopeGenerator::EnvelopeGenerator() -{ - reset(); -} - -// ---------------------------------------------------------------------------- -// SID reset. -// ---------------------------------------------------------------------------- -void EnvelopeGenerator::reset() -{ - envelope_counter = 0; - - attack = 0; - decay = 0; - sustain = 0; - release = 0; - - gate = 0; - - rate_counter = 0; - exponential_counter = 0; - exponential_counter_period = 1; - - state = RELEASE; - rate_period = rate_counter_period[release]; - hold_zero = true; -} - - -// Rate counter periods are calculated from the Envelope Rates table in -// the Programmer's Reference Guide. The rate counter period is the number of -// cycles between each increment of the envelope counter. -// The rates have been verified by sampling ENV3. -// -// The rate counter is a 16 bit register which is incremented each cycle. -// When the counter reaches a specific comparison value, the envelope counter -// is incremented (attack) or decremented (decay/release) and the -// counter is zeroed. -// -// NB! Sampling ENV3 shows that the calculated values are not exact. -// It may seem like most calculated values have been rounded (.5 is rounded -// down) and 1 has beed added to the result. A possible explanation for this -// is that the SID designers have used the calculated values directly -// as rate counter comparison values, not considering a one cycle delay to -// zero the counter. This would yield an actual period of comparison value + 1. -// -// The time of the first envelope count can not be exactly controlled, except -// possibly by resetting the chip. Because of this we cannot do cycle exact -// sampling and must devise another method to calculate the rate counter -// periods. -// -// The exact rate counter periods can be determined e.g. by counting the number -// of cycles from envelope level 1 to envelope level 129, and dividing the -// number of cycles by 128. CIA1 timer A and B in linked mode can perform -// the cycle count. This is the method used to find the rates below. -// -// To avoid the ADSR delay bug, sampling of ENV3 should be done using -// sustain = release = 0. This ensures that the attack state will not lower -// the current rate counter period. -// -// The ENV3 sampling code below yields a maximum timing error of 14 cycles. -// lda #$01 -// l1: cmp $d41c -// bne l1 -// ... -// lda #$ff -// l2: cmp $d41c -// bne l2 -// -// This yields a maximum error for the calculated rate period of 14/128 cycles. -// The described method is thus sufficient for exact calculation of the rate -// periods. -// -reg16 EnvelopeGenerator::rate_counter_period[] = { - 9, // 2ms*1.0MHz/256 = 7.81 - 32, // 8ms*1.0MHz/256 = 31.25 - 63, // 16ms*1.0MHz/256 = 62.50 - 95, // 24ms*1.0MHz/256 = 93.75 - 149, // 38ms*1.0MHz/256 = 148.44 - 220, // 56ms*1.0MHz/256 = 218.75 - 267, // 68ms*1.0MHz/256 = 265.63 - 313, // 80ms*1.0MHz/256 = 312.50 - 392, // 100ms*1.0MHz/256 = 390.63 - 977, // 250ms*1.0MHz/256 = 976.56 - 1954, // 500ms*1.0MHz/256 = 1953.13 - 3126, // 800ms*1.0MHz/256 = 3125.00 - 3907, // 1 s*1.0MHz/256 = 3906.25 - 11720, // 3 s*1.0MHz/256 = 11718.75 - 19532, // 5 s*1.0MHz/256 = 19531.25 - 31251 // 8 s*1.0MHz/256 = 31250.00 -}; - - -// For decay and release, the clock to the envelope counter is sequentially -// divided by 1, 2, 4, 8, 16, 30, 1 to create a piece-wise linear approximation -// of an exponential. The exponential counter period is loaded at the envelope -// counter values 255, 93, 54, 26, 14, 6, 0. The period can be different for the -// same envelope counter value, depending on whether the envelope has been -// rising (attack -> release) or sinking (decay/release). -// -// Since it is not possible to reset the rate counter (the test bit has no -// influence on the envelope generator whatsoever) a method must be devised to -// do cycle exact sampling of ENV3 to do the investigation. This is possible -// with knowledge of the rate period for A=0, found above. -// -// The CPU can be synchronized with ENV3 by first synchronizing with the rate -// counter by setting A=0 and wait in a carefully timed loop for the envelope -// counter _not_ to change for 9 cycles. We can then wait for a specific value -// of ENV3 with another timed loop to fully synchronize with ENV3. -// -// At the first period when an exponential counter period larger than one -// is used (decay or release), one extra cycle is spent before the envelope is -// decremented. The envelope output is then delayed one cycle until the state -// is changed to attack. Now one cycle less will be spent before the envelope -// is incremented, and the situation is normalized. -// The delay is probably caused by the comparison with the exponential counter, -// and does not seem to affect the rate counter. This has been verified by -// timing 256 consecutive complete envelopes with A = D = R = 1, S = 0, using -// CIA1 timer A and B in linked mode. If the rate counter is not affected the -// period of each complete envelope is -// (255 + 162*1 + 39*2 + 28*4 + 12*8 + 8*16 + 6*30)*32 = 756*32 = 32352 -// which corresponds exactly to the timed value divided by the number of -// complete envelopes. -// NB! This one cycle delay is not modeled. - - -// From the sustain levels it follows that both the low and high 4 bits of the -// envelope counter are compared to the 4-bit sustain value. -// This has been verified by sampling ENV3. -// -reg8 EnvelopeGenerator::sustain_level[] = { - 0x00, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - 0xaa, - 0xbb, - 0xcc, - 0xdd, - 0xee, - 0xff, -}; - - -// ---------------------------------------------------------------------------- -// Register functions. -// ---------------------------------------------------------------------------- -void EnvelopeGenerator::writeCONTROL_REG(reg8 control) -{ - reg8 gate_next = control & 0x01; - - // The rate counter is never reset, thus there will be a delay before the - // envelope counter starts counting up (attack) or down (release). - - // Gate bit on: Start attack, decay, sustain. - if (!gate && gate_next) { - state = ATTACK; - rate_period = rate_counter_period[attack]; - - // Switching to attack state unlocks the zero freeze. - hold_zero = false; - } - // Gate bit off: Start release. - else if (gate && !gate_next) { - state = RELEASE; - rate_period = rate_counter_period[release]; - } - - gate = gate_next; -} - -void EnvelopeGenerator::writeATTACK_DECAY(reg8 attack_decay) -{ - attack = (attack_decay >> 4) & 0x0f; - decay = attack_decay & 0x0f; - if (state == ATTACK) { - rate_period = rate_counter_period[attack]; - } - else if (state == DECAY_SUSTAIN) { - rate_period = rate_counter_period[decay]; - } -} - -void EnvelopeGenerator::writeSUSTAIN_RELEASE(reg8 sustain_release) -{ - sustain = (sustain_release >> 4) & 0x0f; - release = sustain_release & 0x0f; - if (state == RELEASE) { - rate_period = rate_counter_period[release]; - } -} - -reg8 EnvelopeGenerator::readENV() -{ - return output(); -} diff --git a/plugins/sid/envelope.h b/plugins/sid/envelope.h deleted file mode 100644 index 63a30578b7f..00000000000 --- a/plugins/sid/envelope.h +++ /dev/null @@ -1,305 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __ENVELOPE_H__ -#define __ENVELOPE_H__ - -#include "siddefs.h" - -// ---------------------------------------------------------------------------- -// A 15 bit counter is used to implement the envelope rates, in effect -// dividing the clock to the envelope counter by the currently selected rate -// period. -// In addition, another counter is used to implement the exponential envelope -// decay, in effect further dividing the clock to the envelope counter. -// The period of this counter is set to 1, 2, 4, 8, 16, 30 at the envelope -// counter values 255, 93, 54, 26, 14, 6, respectively. -// ---------------------------------------------------------------------------- -class EnvelopeGenerator -{ -public: - EnvelopeGenerator(); - - enum State { ATTACK, DECAY_SUSTAIN, RELEASE }; - - RESID_INLINE void clock(); - RESID_INLINE void clock(cycle_count delta_t); - void reset(); - - void writeCONTROL_REG(reg8); - void writeATTACK_DECAY(reg8); - void writeSUSTAIN_RELEASE(reg8); - reg8 readENV(); - - // 8-bit envelope output. - RESID_INLINE reg8 output(); - -protected: - reg16 rate_counter; - reg16 rate_period; - reg8 exponential_counter; - reg8 exponential_counter_period; - reg8 envelope_counter; - bool hold_zero; - - reg4 attack; - reg4 decay; - reg4 sustain; - reg4 release; - - reg8 gate; - - State state; - - // Lookup table to convert from attack, decay, or release value to rate - // counter period. - static reg16 rate_counter_period[]; - - // The 16 selectable sustain levels. - static reg8 sustain_level[]; - -friend class cSID; -}; - - -// ---------------------------------------------------------------------------- -// Inline functions. -// The following functions are defined inline because they are called every -// time a sample is calculated. -// ---------------------------------------------------------------------------- - -#if RESID_INLINING || defined(__ENVELOPE_CC__) - -// ---------------------------------------------------------------------------- -// SID clocking - 1 cycle. -// ---------------------------------------------------------------------------- -RESID_INLINE -void EnvelopeGenerator::clock() -{ - // Check for ADSR delay bug. - // If the rate counter comparison value is set below the current value of the - // rate counter, the counter will continue counting up until it wraps around - // to zero at 2^15 = 0x8000, and then count rate_period - 1 before the - // envelope can finally be stepped. - // This has been verified by sampling ENV3. - // - if (++rate_counter & 0x8000) { - ++rate_counter &= 0x7fff; - } - - if (rate_counter != rate_period) { - return; - } - - rate_counter = 0; - - // The first envelope step in the attack state also resets the exponential - // counter. This has been verified by sampling ENV3. - // - if (state == ATTACK || ++exponential_counter == exponential_counter_period) - { - exponential_counter = 0; - - // Check whether the envelope counter is frozen at zero. - if (hold_zero) { - return; - } - - switch (state) { - case ATTACK: - // The envelope counter can flip from 0xff to 0x00 by changing state to - // release, then to attack. The envelope counter is then frozen at - // zero; to unlock this situation the state must be changed to release, - // then to attack. This has been verified by sampling ENV3. - // - ++envelope_counter &= 0xff; - if (envelope_counter == 0xff) { - state = DECAY_SUSTAIN; - rate_period = rate_counter_period[decay]; - } - break; - case DECAY_SUSTAIN: - if (envelope_counter != sustain_level[sustain]) { - --envelope_counter; - } - break; - case RELEASE: - // The envelope counter can flip from 0x00 to 0xff by changing state to - // attack, then to release. The envelope counter will then continue - // counting down in the release state. - // This has been verified by sampling ENV3. - // NB! The operation below requires two's complement integer. - // - --envelope_counter &= 0xff; - break; - } - - // Check for change of exponential counter period. - switch (envelope_counter) { - case 0xff: - exponential_counter_period = 1; - break; - case 0x5d: - exponential_counter_period = 2; - break; - case 0x36: - exponential_counter_period = 4; - break; - case 0x1a: - exponential_counter_period = 8; - break; - case 0x0e: - exponential_counter_period = 16; - break; - case 0x06: - exponential_counter_period = 30; - break; - case 0x00: - exponential_counter_period = 1; - - // When the envelope counter is changed to zero, it is frozen at zero. - // This has been verified by sampling ENV3. - hold_zero = true; - break; - } - } -} - - -// ---------------------------------------------------------------------------- -// SID clocking - delta_t cycles. -// ---------------------------------------------------------------------------- -RESID_INLINE -void EnvelopeGenerator::clock(cycle_count delta_t) -{ - // Check for ADSR delay bug. - // If the rate counter comparison value is set below the current value of the - // rate counter, the counter will continue counting up until it wraps around - // to zero at 2^15 = 0x8000, and then count rate_period - 1 before the - // envelope can finally be stepped. - // This has been verified by sampling ENV3. - // - - // NB! This requires two's complement integer. - int rate_step = rate_period - rate_counter; - if (rate_step <= 0) { - rate_step += 0x7fff; - } - - while (delta_t) { - if (delta_t < rate_step) { - rate_counter += delta_t; - if (rate_counter & 0x8000) { - ++rate_counter &= 0x7fff; - } - return; - } - - rate_counter = 0; - delta_t -= rate_step; - - // The first envelope step in the attack state also resets the exponential - // counter. This has been verified by sampling ENV3. - // - if (state == ATTACK || ++exponential_counter == exponential_counter_period) - { - exponential_counter = 0; - - // Check whether the envelope counter is frozen at zero. - if (hold_zero) { - rate_step = rate_period; - continue; - } - - switch (state) { - case ATTACK: - // The envelope counter can flip from 0xff to 0x00 by changing state to - // release, then to attack. The envelope counter is then frozen at - // zero; to unlock this situation the state must be changed to release, - // then to attack. This has been verified by sampling ENV3. - // - ++envelope_counter &= 0xff; - if (envelope_counter == 0xff) { - state = DECAY_SUSTAIN; - rate_period = rate_counter_period[decay]; - } - break; - case DECAY_SUSTAIN: - if (envelope_counter != sustain_level[sustain]) { - --envelope_counter; - } - break; - case RELEASE: - // The envelope counter can flip from 0x00 to 0xff by changing state to - // attack, then to release. The envelope counter will then continue - // counting down in the release state. - // This has been verified by sampling ENV3. - // NB! The operation below requires two's complement integer. - // - --envelope_counter &= 0xff; - break; - } - - // Check for change of exponential counter period. - switch (envelope_counter) { - case 0xff: - exponential_counter_period = 1; - break; - case 0x5d: - exponential_counter_period = 2; - break; - case 0x36: - exponential_counter_period = 4; - break; - case 0x1a: - exponential_counter_period = 8; - break; - case 0x0e: - exponential_counter_period = 16; - break; - case 0x06: - exponential_counter_period = 30; - break; - case 0x00: - exponential_counter_period = 1; - - // When the envelope counter is changed to zero, it is frozen at zero. - // This has been verified by sampling ENV3. - hold_zero = true; - break; - } - } - - rate_step = rate_period; - } -} - - -// ---------------------------------------------------------------------------- -// Read the envelope generator output. -// ---------------------------------------------------------------------------- -RESID_INLINE -reg8 EnvelopeGenerator::output() -{ - return envelope_counter; -} - -#endif // RESID_INLINING || defined(__ENVELOPE_CC__) - -#endif // not __ENVELOPE_H__ diff --git a/plugins/sid/extfilt.cc b/plugins/sid/extfilt.cc deleted file mode 100644 index b875ee2ac96..00000000000 --- a/plugins/sid/extfilt.cc +++ /dev/null @@ -1,79 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#define __EXTFILT_CC__ -#include "extfilt.h" - - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -ExternalFilter::ExternalFilter() -{ - reset(); - enable_filter(true); - set_chip_model(MOS6581); - - // Low-pass: R = 10kOhm, C = 1000pF; w0l = 1/RC = 1/(1e4*1e-9) = 100000 - // High-pass: R = 1kOhm, C = 10uF; w0h = 1/RC = 1/(1e3*1e-5) = 100 - // Multiply with 1.048576 to facilitate division by 1 000 000 by right- - // shifting 20 times (2 ^ 20 = 1048576). - - w0lp = 104858; - w0hp = 105; -} - - -// ---------------------------------------------------------------------------- -// Enable filter. -// ---------------------------------------------------------------------------- -void ExternalFilter::enable_filter(bool enable) -{ - enabled = enable; -} - - -// ---------------------------------------------------------------------------- -// Set chip model. -// ---------------------------------------------------------------------------- -void ExternalFilter::set_chip_model(chip_model model) -{ - if (model == MOS6581) { - // Maximum mixer DC output level; to be removed if the external - // filter is turned off: ((wave DC + voice DC)*voices + mixer DC)*volume - // See voice.cc and filter.cc for an explanation of the values. - mixer_DC = ((((0x800 - 0x380) + 0x800)*0xff*3 - 0xfff*0xff/18) >> 7)*0x0f; - } - else { - // No DC offsets in the MOS8580. - mixer_DC = 0; - } -} - - -// ---------------------------------------------------------------------------- -// SID reset. -// ---------------------------------------------------------------------------- -void ExternalFilter::reset() -{ - // State of filter. - Vlp = 0; - Vhp = 0; - Vo = 0; -} diff --git a/plugins/sid/extfilt.h b/plugins/sid/extfilt.h deleted file mode 100644 index adf602f191d..00000000000 --- a/plugins/sid/extfilt.h +++ /dev/null @@ -1,164 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __EXTFILT_H__ -#define __EXTFILT_H__ - -#include "siddefs.h" - -// ---------------------------------------------------------------------------- -// The audio output stage in a Commodore 64 consists of two STC networks, -// a low-pass filter with 3-dB frequency 16kHz followed by a high-pass -// filter with 3-dB frequency 16Hz (the latter provided an audio equipment -// input impedance of 1kOhm). -// The STC networks are connected with a BJT supposedly meant to act as -// a unity gain buffer, which is not really how it works. A more elaborate -// model would include the BJT, however DC circuit analysis yields BJT -// base-emitter and emitter-base impedances sufficiently low to produce -// additional low-pass and high-pass 3dB-frequencies in the order of hundreds -// of kHz. This calls for a sampling frequency of several MHz, which is far -// too high for practical use. -// ---------------------------------------------------------------------------- -class ExternalFilter -{ -public: - ExternalFilter(); - - void enable_filter(bool enable); - void set_chip_model(chip_model model); - - RESID_INLINE void clock(sound_sample Vi); - RESID_INLINE void clock(cycle_count delta_t, sound_sample Vi); - void reset(); - - // Audio output (20 bits). - RESID_INLINE sound_sample output(); - -protected: - // Filter enabled. - bool enabled; - - // Maximum mixer DC offset. - sound_sample mixer_DC; - - // State of filters. - sound_sample Vlp; // lowpass - sound_sample Vhp; // highpass - sound_sample Vo; - - // Cutoff frequencies. - sound_sample w0lp; - sound_sample w0hp; - -friend class cSID; -}; - - -// ---------------------------------------------------------------------------- -// Inline functions. -// The following functions are defined inline because they are called every -// time a sample is calculated. -// ---------------------------------------------------------------------------- - -#if RESID_INLINING || defined(__EXTFILT_CC__) - -// ---------------------------------------------------------------------------- -// SID clocking - 1 cycle. -// ---------------------------------------------------------------------------- -RESID_INLINE -void ExternalFilter::clock(sound_sample Vi) -{ - // This is handy for testing. - if (!enabled) { - // Remove maximum DC level since there is no filter to do it. - Vlp = Vhp = 0; - Vo = Vi - mixer_DC; - return; - } - - // delta_t is converted to seconds given a 1MHz clock by dividing - // with 1 000 000. - - // Calculate filter outputs. - // Vo = Vlp - Vhp; - // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t; - // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t; - - sound_sample dVlp = (w0lp >> 8)*(Vi - Vlp) >> 12; - sound_sample dVhp = w0hp*(Vlp - Vhp) >> 20; - Vo = Vlp - Vhp; - Vlp += dVlp; - Vhp += dVhp; -} - -// ---------------------------------------------------------------------------- -// SID clocking - delta_t cycles. -// ---------------------------------------------------------------------------- -RESID_INLINE -void ExternalFilter::clock(cycle_count delta_t, - sound_sample Vi) -{ - // This is handy for testing. - if (!enabled) { - // Remove maximum DC level since there is no filter to do it. - Vlp = Vhp = 0; - Vo = Vi - mixer_DC; - return; - } - - // Maximum delta cycles for the external filter to work satisfactorily - // is approximately 8. - cycle_count delta_t_flt = 8; - - while (delta_t) { - if (delta_t < delta_t_flt) { - delta_t_flt = delta_t; - } - - // delta_t is converted to seconds given a 1MHz clock by dividing - // with 1 000 000. - - // Calculate filter outputs. - // Vo = Vlp - Vhp; - // Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t; - // Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t; - - sound_sample dVlp = (w0lp*delta_t_flt >> 8)*(Vi - Vlp) >> 12; - sound_sample dVhp = w0hp*delta_t_flt*(Vlp - Vhp) >> 20; - Vo = Vlp - Vhp; - Vlp += dVlp; - Vhp += dVhp; - - delta_t -= delta_t_flt; - } -} - - -// ---------------------------------------------------------------------------- -// Audio output (19.5 bits). -// ---------------------------------------------------------------------------- -RESID_INLINE -sound_sample ExternalFilter::output() -{ - return Vo; -} - -#endif // RESID_INLINING || defined(__EXTFILT_CC__) - -#endif // not __EXTFILT_H__ diff --git a/plugins/sid/filter.cc b/plugins/sid/filter.cc deleted file mode 100644 index fb548e30acd..00000000000 --- a/plugins/sid/filter.cc +++ /dev/null @@ -1,305 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#define __FILTER_CC__ -#include "filter.h" - -// Maximum cutoff frequency is specified as -// FCmax = 2.6e-5/C = 2.6e-5/2200e-12 = 11818. -// -// Measurements indicate a cutoff frequency range of approximately -// 220Hz - 18kHz on a MOS6581 fitted with 470pF capacitors. The function -// mapping FC to cutoff frequency has the shape of the tanh function, with -// a discontinuity at FCHI = 0x80. -// In contrast, the MOS8580 almost perfectly corresponds with the -// specification of a linear mapping from 30Hz to 12kHz. -// -// The mappings have been measured by feeding the SID with an external -// signal since the chip itself is incapable of generating waveforms of -// higher fundamental frequency than 4kHz. It is best to use the bandpass -// output at full resonance to pick out the cutoff frequency at any given -// FC setting. -// -// The mapping function is specified with spline interpolation points and -// the function values are retrieved via table lookup. -// -// NB! Cutoff frequency characteristics may vary, we have modeled two -// particular Commodore 64s. - -fc_point Filter::f0_points_6581[] = -{ - // FC f FCHI FCLO - // ---------------------------- - { 0, 220 }, // 0x00 - repeated end point - { 0, 220 }, // 0x00 - { 128, 230 }, // 0x10 - { 256, 250 }, // 0x20 - { 384, 300 }, // 0x30 - { 512, 420 }, // 0x40 - { 640, 780 }, // 0x50 - { 768, 1600 }, // 0x60 - { 832, 2300 }, // 0x68 - { 896, 3200 }, // 0x70 - { 960, 4300 }, // 0x78 - { 992, 5000 }, // 0x7c - { 1008, 5400 }, // 0x7e - { 1016, 5700 }, // 0x7f - { 1023, 6000 }, // 0x7f 0x07 - { 1023, 6000 }, // 0x7f 0x07 - discontinuity - { 1024, 4600 }, // 0x80 - - { 1024, 4600 }, // 0x80 - { 1032, 4800 }, // 0x81 - { 1056, 5300 }, // 0x84 - { 1088, 6000 }, // 0x88 - { 1120, 6600 }, // 0x8c - { 1152, 7200 }, // 0x90 - { 1280, 9500 }, // 0xa0 - { 1408, 12000 }, // 0xb0 - { 1536, 14500 }, // 0xc0 - { 1664, 16000 }, // 0xd0 - { 1792, 17100 }, // 0xe0 - { 1920, 17700 }, // 0xf0 - { 2047, 18000 }, // 0xff 0x07 - { 2047, 18000 } // 0xff 0x07 - repeated end point -}; - -fc_point Filter::f0_points_8580[] = -{ - // FC f FCHI FCLO - // ---------------------------- - { 0, 0 }, // 0x00 - repeated end point - { 0, 0 }, // 0x00 - { 128, 800 }, // 0x10 - { 256, 1600 }, // 0x20 - { 384, 2500 }, // 0x30 - { 512, 3300 }, // 0x40 - { 640, 4100 }, // 0x50 - { 768, 4800 }, // 0x60 - { 896, 5600 }, // 0x70 - { 1024, 6500 }, // 0x80 - { 1152, 7500 }, // 0x90 - { 1280, 8400 }, // 0xa0 - { 1408, 9200 }, // 0xb0 - { 1536, 9800 }, // 0xc0 - { 1664, 10500 }, // 0xd0 - { 1792, 11000 }, // 0xe0 - { 1920, 11700 }, // 0xf0 - { 2047, 12500 }, // 0xff 0x07 - { 2047, 12500 } // 0xff 0x07 - repeated end point -}; - - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -Filter::Filter() -{ - fc = 0; - - res = 0; - - filt = 0; - - voice3off = 0; - - hp_bp_lp = 0; - - vol = 0; - - // State of filter. - Vhp = 0; - Vbp = 0; - Vlp = 0; - Vnf = 0; - - enable_filter(true); - - // Create mappings from FC to cutoff frequency. - interpolate(f0_points_6581, f0_points_6581 - + sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1, - PointPlotter(f0_6581), 1.0); - interpolate(f0_points_8580, f0_points_8580 - + sizeof(f0_points_8580)/sizeof(*f0_points_8580) - 1, - PointPlotter(f0_8580), 1.0); - - set_chip_model(MOS6581); -} - - -// ---------------------------------------------------------------------------- -// Enable filter. -// ---------------------------------------------------------------------------- -void Filter::enable_filter(bool enable) -{ - enabled = enable; -} - - -// ---------------------------------------------------------------------------- -// Set chip model. -// ---------------------------------------------------------------------------- -void Filter::set_chip_model(chip_model model) -{ - if (model == MOS6581) { - // The mixer has a small input DC offset. This is found as follows: - // - // The "zero" output level of the mixer measured on the SID audio - // output pin is 5.50V at zero volume, and 5.44 at full - // volume. This yields a DC offset of (5.44V - 5.50V) = -0.06V. - // - // The DC offset is thus -0.06V/1.05V ~ -1/18 of the dynamic range - // of one voice. See voice.cc for measurement of the dynamic - // range. - - mixer_DC = -0xfff*0xff/18 >> 7; - - f0 = f0_6581; - f0_points = f0_points_6581; - f0_count = sizeof(f0_points_6581)/sizeof(*f0_points_6581); - } - else { - // No DC offsets in the MOS8580. - mixer_DC = 0; - - f0 = f0_8580; - f0_points = f0_points_8580; - f0_count = sizeof(f0_points_8580)/sizeof(*f0_points_8580); - } - - set_w0(); - set_Q(); -} - - -// ---------------------------------------------------------------------------- -// SID reset. -// ---------------------------------------------------------------------------- -void Filter::reset() -{ - fc = 0; - - res = 0; - - filt = 0; - - voice3off = 0; - - hp_bp_lp = 0; - - vol = 0; - - // State of filter. - Vhp = 0; - Vbp = 0; - Vlp = 0; - Vnf = 0; - - set_w0(); - set_Q(); -} - - -// ---------------------------------------------------------------------------- -// Register functions. -// ---------------------------------------------------------------------------- -void Filter::writeFC_LO(reg8 fc_lo) -{ - fc = ( fc & 0x7f8 ) | ( fc_lo & 0x007 ); - set_w0(); -} - -void Filter::writeFC_HI(reg8 fc_hi) -{ - fc = ( (fc_hi << 3) & 0x7f8 ) | ( fc & 0x007 ); - set_w0(); -} - -void Filter::writeRES_FILT(reg8 res_filt) -{ - res = (res_filt >> 4) & 0x0f; - set_Q(); - - filt = res_filt & 0x0f; -} - -void Filter::writeMODE_VOL(reg8 mode_vol) -{ - voice3off = mode_vol & 0x80; - - hp_bp_lp = (mode_vol >> 4) & 0x07; - - vol = mode_vol & 0x0f; -} - -// Set filter cutoff frequency. -void Filter::set_w0() -{ - const double pi = 3.1415926535897932385; - - // Multiply with 1.048576 to facilitate division by 1 000 000 by right- - // shifting 20 times (2 ^ 20 = 1048576). - w0 = static_cast(2*pi*f0[fc]*1.048576); - - // Limit f0 to 16kHz to keep 1 cycle filter stable. - const sound_sample w0_max_1 = static_cast(2*pi*16000*1.048576); - w0_ceil_1 = w0 <= w0_max_1 ? w0 : w0_max_1; - - // Limit f0 to 4kHz to keep delta_t cycle filter stable. - const sound_sample w0_max_dt = static_cast(2*pi*4000*1.048576); - w0_ceil_dt = w0 <= w0_max_dt ? w0 : w0_max_dt; -} - -// Set filter resonance. -void Filter::set_Q() -{ - // Q is controlled linearly by res. Q has approximate range [0.707, 1.7]. - // As resonance is increased, the filter must be clocked more often to keep - // stable. - - // The coefficient 1024 is dispensed of later by right-shifting 10 times - // (2 ^ 10 = 1024). - _1024_div_Q = static_cast(1024.0/(0.707 + 1.0*res/0x0f)); -} - -// ---------------------------------------------------------------------------- -// Spline functions. -// ---------------------------------------------------------------------------- - -// ---------------------------------------------------------------------------- -// Return the array of spline interpolation points used to map the FC register -// to filter cutoff frequency. -// ---------------------------------------------------------------------------- -void Filter::fc_default(const fc_point*& points, int& count) -{ - points = f0_points; - count = f0_count; -} - -// ---------------------------------------------------------------------------- -// Given an array of interpolation points p with n points, the following -// statement will specify a new FC mapping: -// interpolate(p, p + n - 1, filter.fc_plotter(), 1.0); -// Note that the x range of the interpolation points *must* be [0, 2047], -// and that additional end points *must* be present since the end points -// are not interpolated. -// ---------------------------------------------------------------------------- -PointPlotter Filter::fc_plotter() -{ - return PointPlotter(f0); -} diff --git a/plugins/sid/filter.h b/plugins/sid/filter.h deleted file mode 100644 index 8d8aece3dbe..00000000000 --- a/plugins/sid/filter.h +++ /dev/null @@ -1,531 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __FILTER_H__ -#define __FILTER_H__ - -#include "siddefs.h" -#include "spline.h" - -// ---------------------------------------------------------------------------- -// The SID filter is modeled with a two-integrator-loop biquadratic filter, -// which has been confirmed by Bob Yannes to be the actual circuit used in -// the SID chip. -// -// Measurements show that excellent emulation of the SID filter is achieved, -// except when high resonance is combined with high sustain levels. -// In this case the SID op-amps are performing less than ideally and are -// causing some peculiar behavior of the SID filter. This however seems to -// have more effect on the overall amplitude than on the color of the sound. -// -// The theory for the filter circuit can be found in "Microelectric Circuits" -// by Adel S. Sedra and Kenneth C. Smith. -// The circuit is modeled based on the explanation found there except that -// an additional inverter is used in the feedback from the bandpass output, -// allowing the summer op-amp to operate in single-ended mode. This yields -// inverted filter outputs with levels independent of Q, which corresponds with -// the results obtained from a real SID. -// -// We have been able to model the summer and the two integrators of the circuit -// to form components of an IIR filter. -// Vhp is the output of the summer, Vbp is the output of the first integrator, -// and Vlp is the output of the second integrator in the filter circuit. -// -// According to Bob Yannes, the active stages of the SID filter are not really -// op-amps. Rather, simple NMOS inverters are used. By biasing an inverter -// into its region of quasi-linear operation using a feedback resistor from -// input to output, a MOS inverter can be made to act like an op-amp for -// small signals centered around the switching threshold. -// -// Qualified guesses at SID filter schematics are depicted below. -// -// SID filter -// ---------- -// -// ----------------------------------------------- -// | | -// | ---Rq-- | -// | | | | -// | --------------|--R-----[A>--|--R-----[A>--| -// | | | | -// vi -----R1-- | | | -// -// vhp vbp vlp -// -// -// vi - input voltage -// vhp - highpass output -// vbp - bandpass output -// vlp - lowpass output -// [A> - op-amp -// R1 - summer resistor -// Rq - resistor array controlling resonance (4 resistors) -// R - NMOS FET voltage controlled resistor controlling cutoff frequency -// Rs - shunt resitor -// C - capacitor -// -// -// -// SID integrator -// -------------- -// -// V+ -// -// | -// | -// -----| -// | | -// | ||-- -// -|| -// ---C--- ||-> -// | | | -// |---Rs-----------|---- vo -// | | -// | ||-- -// vi ---- -----|------------|| -// | ^ | ||-> -// |___| | | -// ----- | | -// | | | -// |---R2-- | -// | -// R1 V- -// | -// | -// -// Vw -// -// ---------------------------------------------------------------------------- -class Filter -{ -public: - Filter(); - - void enable_filter(bool enable); - void set_chip_model(chip_model model); - - RESID_INLINE - void clock(sound_sample voice1, sound_sample voice2, sound_sample voice3, - sound_sample ext_in); - RESID_INLINE - void clock(cycle_count delta_t, - sound_sample voice1, sound_sample voice2, sound_sample voice3, - sound_sample ext_in); - void reset(); - - // Write registers. - void writeFC_LO(reg8); - void writeFC_HI(reg8); - void writeRES_FILT(reg8); - void writeMODE_VOL(reg8); - - // SID audio output (16 bits). - sound_sample output(); - - // Spline functions. - void fc_default(const fc_point*& points, int& count); - PointPlotter fc_plotter(); - -protected: - void set_w0(); - void set_Q(); - - // Filter enabled. - bool enabled; - - // Filter cutoff frequency. - reg12 fc; - - // Filter resonance. - reg8 res; - - // Selects which inputs to route through filter. - reg8 filt; - - // Switch voice 3 off. - reg8 voice3off; - - // Highpass, bandpass, and lowpass filter modes. - reg8 hp_bp_lp; - - // Output master volume. - reg4 vol; - - // Mixer DC offset. - sound_sample mixer_DC; - - // State of filter. - sound_sample Vhp; // highpass - sound_sample Vbp; // bandpass - sound_sample Vlp; // lowpass - sound_sample Vnf; // not filtered - - // Cutoff frequency, resonance. - sound_sample w0, w0_ceil_1, w0_ceil_dt; - sound_sample _1024_div_Q; - - // Cutoff frequency tables. - // FC is an 11 bit register. - sound_sample f0_6581[2048]; - sound_sample f0_8580[2048]; - sound_sample* f0; - static fc_point f0_points_6581[]; - static fc_point f0_points_8580[]; - fc_point* f0_points; - int f0_count; - -friend class cSID; -}; - - -// ---------------------------------------------------------------------------- -// Inline functions. -// The following functions are defined inline because they are called every -// time a sample is calculated. -// ---------------------------------------------------------------------------- - -#if RESID_INLINING || defined(__FILTER_CC__) - -// ---------------------------------------------------------------------------- -// SID clocking - 1 cycle. -// ---------------------------------------------------------------------------- -RESID_INLINE -void Filter::clock(sound_sample voice1, - sound_sample voice2, - sound_sample voice3, - sound_sample ext_in) -{ - // Scale each voice down from 20 to 13 bits. - voice1 >>= 7; - voice2 >>= 7; - - // NB! Voice 3 is not silenced by voice3off if it is routed through - // the filter. - if (voice3off && !(filt & 0x04)) { - voice3 = 0; - } - else { - voice3 >>= 7; - } - - ext_in >>= 7; - - // This is handy for testing. - if (!enabled) { - Vnf = voice1 + voice2 + voice3 + ext_in; - Vhp = Vbp = Vlp = 0; - return; - } - - // Route voices into or around filter. - // The code below is expanded to a switch for faster execution. - // (filt1 ? Vi : Vnf) += voice1; - // (filt2 ? Vi : Vnf) += voice2; - // (filt3 ? Vi : Vnf) += voice3; - - sound_sample Vi; - - switch (filt) { - default: - case 0x0: - Vi = 0; - Vnf = voice1 + voice2 + voice3 + ext_in; - break; - case 0x1: - Vi = voice1; - Vnf = voice2 + voice3 + ext_in; - break; - case 0x2: - Vi = voice2; - Vnf = voice1 + voice3 + ext_in; - break; - case 0x3: - Vi = voice1 + voice2; - Vnf = voice3 + ext_in; - break; - case 0x4: - Vi = voice3; - Vnf = voice1 + voice2 + ext_in; - break; - case 0x5: - Vi = voice1 + voice3; - Vnf = voice2 + ext_in; - break; - case 0x6: - Vi = voice2 + voice3; - Vnf = voice1 + ext_in; - break; - case 0x7: - Vi = voice1 + voice2 + voice3; - Vnf = ext_in; - break; - case 0x8: - Vi = ext_in; - Vnf = voice1 + voice2 + voice3; - break; - case 0x9: - Vi = voice1 + ext_in; - Vnf = voice2 + voice3; - break; - case 0xa: - Vi = voice2 + ext_in; - Vnf = voice1 + voice3; - break; - case 0xb: - Vi = voice1 + voice2 + ext_in; - Vnf = voice3; - break; - case 0xc: - Vi = voice3 + ext_in; - Vnf = voice1 + voice2; - break; - case 0xd: - Vi = voice1 + voice3 + ext_in; - Vnf = voice2; - break; - case 0xe: - Vi = voice2 + voice3 + ext_in; - Vnf = voice1; - break; - case 0xf: - Vi = voice1 + voice2 + voice3 + ext_in; - Vnf = 0; - break; - } - - // delta_t = 1 is converted to seconds given a 1MHz clock by dividing - // with 1 000 000. - - // Calculate filter outputs. - // Vhp = Vbp/Q - Vlp - Vi; - // dVbp = -w0*Vhp*dt; - // dVlp = -w0*Vbp*dt; - - sound_sample dVbp = (w0_ceil_1*Vhp >> 20); - sound_sample dVlp = (w0_ceil_1*Vbp >> 20); - Vbp -= dVbp; - Vlp -= dVlp; - Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi; -} - -// ---------------------------------------------------------------------------- -// SID clocking - delta_t cycles. -// ---------------------------------------------------------------------------- -RESID_INLINE -void Filter::clock(cycle_count delta_t, - sound_sample voice1, - sound_sample voice2, - sound_sample voice3, - sound_sample ext_in) -{ - // Scale each voice down from 20 to 13 bits. - voice1 >>= 7; - voice2 >>= 7; - - // NB! Voice 3 is not silenced by voice3off if it is routed through - // the filter. - if (voice3off && !(filt & 0x04)) { - voice3 = 0; - } - else { - voice3 >>= 7; - } - - ext_in >>= 7; - - // Enable filter on/off. - // This is not really part of SID, but is useful for testing. - // On slow CPUs it may be necessary to bypass the filter to lower the CPU - // load. - if (!enabled) { - Vnf = voice1 + voice2 + voice3 + ext_in; - Vhp = Vbp = Vlp = 0; - return; - } - - // Route voices into or around filter. - // The code below is expanded to a switch for faster execution. - // (filt1 ? Vi : Vnf) += voice1; - // (filt2 ? Vi : Vnf) += voice2; - // (filt3 ? Vi : Vnf) += voice3; - - sound_sample Vi; - - switch (filt) { - default: - case 0x0: - Vi = 0; - Vnf = voice1 + voice2 + voice3 + ext_in; - break; - case 0x1: - Vi = voice1; - Vnf = voice2 + voice3 + ext_in; - break; - case 0x2: - Vi = voice2; - Vnf = voice1 + voice3 + ext_in; - break; - case 0x3: - Vi = voice1 + voice2; - Vnf = voice3 + ext_in; - break; - case 0x4: - Vi = voice3; - Vnf = voice1 + voice2 + ext_in; - break; - case 0x5: - Vi = voice1 + voice3; - Vnf = voice2 + ext_in; - break; - case 0x6: - Vi = voice2 + voice3; - Vnf = voice1 + ext_in; - break; - case 0x7: - Vi = voice1 + voice2 + voice3; - Vnf = ext_in; - break; - case 0x8: - Vi = ext_in; - Vnf = voice1 + voice2 + voice3; - break; - case 0x9: - Vi = voice1 + ext_in; - Vnf = voice2 + voice3; - break; - case 0xa: - Vi = voice2 + ext_in; - Vnf = voice1 + voice3; - break; - case 0xb: - Vi = voice1 + voice2 + ext_in; - Vnf = voice3; - break; - case 0xc: - Vi = voice3 + ext_in; - Vnf = voice1 + voice2; - break; - case 0xd: - Vi = voice1 + voice3 + ext_in; - Vnf = voice2; - break; - case 0xe: - Vi = voice2 + voice3 + ext_in; - Vnf = voice1; - break; - case 0xf: - Vi = voice1 + voice2 + voice3 + ext_in; - Vnf = 0; - break; - } - - // Maximum delta cycles for the filter to work satisfactorily under current - // cutoff frequency and resonance constraints is approximately 8. - cycle_count delta_t_flt = 8; - - while (delta_t) { - if (delta_t < delta_t_flt) { - delta_t_flt = delta_t; - } - - // delta_t is converted to seconds given a 1MHz clock by dividing - // with 1 000 000. This is done in two operations to avoid integer - // multiplication overflow. - - // Calculate filter outputs. - // Vhp = Vbp/Q - Vlp - Vi; - // dVbp = -w0*Vhp*dt; - // dVlp = -w0*Vbp*dt; - sound_sample w0_delta_t = w0_ceil_dt*delta_t_flt >> 6; - - sound_sample dVbp = (w0_delta_t*Vhp >> 14); - sound_sample dVlp = (w0_delta_t*Vbp >> 14); - Vbp -= dVbp; - Vlp -= dVlp; - Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi; - - delta_t -= delta_t_flt; - } -} - - -// ---------------------------------------------------------------------------- -// SID audio output (20 bits). -// ---------------------------------------------------------------------------- -RESID_INLINE -sound_sample Filter::output() -{ - // This is handy for testing. - if (!enabled) { - return (Vnf + mixer_DC)*static_cast(vol); - } - - // Mix highpass, bandpass, and lowpass outputs. The sum is not - // weighted, this can be confirmed by sampling sound output for - // e.g. bandpass, lowpass, and bandpass+lowpass from a SID chip. - - // The code below is expanded to a switch for faster execution. - // if (hp) Vf += Vhp; - // if (bp) Vf += Vbp; - // if (lp) Vf += Vlp; - - sound_sample Vf; - - switch (hp_bp_lp) { - default: - case 0x0: - Vf = 0; - break; - case 0x1: - Vf = Vlp; - break; - case 0x2: - Vf = Vbp; - break; - case 0x3: - Vf = Vlp + Vbp; - break; - case 0x4: - Vf = Vhp; - break; - case 0x5: - Vf = Vlp + Vhp; - break; - case 0x6: - Vf = Vbp + Vhp; - break; - case 0x7: - Vf = Vlp + Vbp + Vhp; - break; - } - - // Sum non-filtered and filtered output. - // Multiply the sum with volume. - return (Vnf + Vf + mixer_DC)*static_cast(vol); -} - -#endif // RESID_INLINING || defined(__FILTER_CC__) - -#endif // not __FILTER_H__ diff --git a/plugins/sid/filter.png b/plugins/sid/filter.png deleted file mode 100644 index ff5c760db9b5a3ac31c164751132ec05da1cae5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1422 zcmV;91#$X`P)V>IRB3Hx05CEyGB7VPFflal!xDV}002aEMObu0Z*X~XX=iA307F9{ zL3DI-X<~JBX>V>IXmoUNIxjC{a%Ew3X?A5}Z*6UFZgVbga%V4WX=7z>b7gZcVtFq! zE-)|j$M56-00at2L_t(Ijdhh>Y?Eaa$A8cBzTMiwR`&ty1BVJ5IN6+?D47{f6A8x8 zXfU9Xn5Y+O)Wi$o1tBP0NJ64_-gu`m1~q|!28c1+%DOKW<~9;>2##Pk&48}kx^``! z|@Bj4^1f0YGq1#uzNET#<9vi*)iP&es?L5!c@m zRWc^C$Vb}oIRP?=qCGtH$5$dE04fhq&{{16XhhH|FS0C~)Iut+y98X$1|uk~QQ3b) z1f`st$rVE!T>-Sp`_r}^%A`|!!$X(tX!bp8)K9`dM09+W>`9aPr6F+`DlTjZHxS5{Y?^ z9XZ7QgNKR7U1XVFtp^i-6{tyv~-&yvgQ z!QeJ#W@7Y*&(qV@0YFP@865N}boe4apE`l1 zG^>k>Nu^SJ|Lxb*ZoZ%9N466Vbvjkg5k~R6)|O2?nWi+6AP@+!wXTi?kji8zOC*>} zrVw#>v;DDF-hAhM%Ga!23K4w=KwM6#MY5J1ZM^;N2d=$Rir}_Kxo=Y~BSRNm7Q1v5 znr*MoheT%p=!-b`7}PIJZm&7cPG7TByRv^iH%!IhE{hDu90UQvOq!C&Zc zNSilW5kT#hdUieY0u4<;XU*jFvh)CGYzp$+OS^gEsaS`(~tLXam7b>f&= -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "pot.h" - -reg8 Potentiometer::readPOT() -{ - // NB! Not modeled. - return 0xff; -} diff --git a/plugins/sid/pot.h b/plugins/sid/pot.h deleted file mode 100644 index 6d44cb1a070..00000000000 --- a/plugins/sid/pot.h +++ /dev/null @@ -1,31 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __POT_H__ -#define __POT_H__ - -#include "siddefs.h" - -class Potentiometer -{ -public: - reg8 readPOT(); -}; - -#endif diff --git a/plugins/sid/sid.cc b/plugins/sid/sid.cc deleted file mode 100644 index c6e234fe0ca..00000000000 --- a/plugins/sid/sid.cc +++ /dev/null @@ -1,1019 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "sid.h" -#include - - -const int cSID::FIR_N = 125; -const int cSID::FIR_RES_INTERPOLATE = 285; -const int cSID::FIR_RES_FAST = 51473; -const int cSID::FIR_SHIFT = 15; -const int cSID::RINGSIZE = 16384; - - // Fixpoint constants (16.16 bits). -const int cSID::FIXP_SHIFT = 16; -const int cSID::FIXP_MASK = 0xffff; - - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -cSID::cSID() -{ - // Initialize pointers. - sample = 0; - fir = 0; - - voice[0].set_sync_source(&voice[2]); - voice[1].set_sync_source(&voice[0]); - voice[2].set_sync_source(&voice[1]); - - set_sampling_parameters(985248, SAMPLE_FAST, 44100); - - bus_value = 0; - bus_value_ttl = 0; - - ext_in = 0; -} - - -// ---------------------------------------------------------------------------- -// Destructor. -// ---------------------------------------------------------------------------- -cSID::~cSID() -{ - delete[] sample; - delete[] fir; -} - - -// ---------------------------------------------------------------------------- -// Set chip model. -// ---------------------------------------------------------------------------- -void cSID::set_chip_model(chip_model model) -{ - for (int i = 0; i < 3; i++) { - voice[i].set_chip_model(model); - } - - filter.set_chip_model(model); - extfilt.set_chip_model(model); -} - - -// ---------------------------------------------------------------------------- -// SID reset. -// ---------------------------------------------------------------------------- -void cSID::reset() -{ - for (int i = 0; i < 3; i++) { - voice[i].reset(); - } - filter.reset(); - extfilt.reset(); - - bus_value = 0; - bus_value_ttl = 0; -} - - -// ---------------------------------------------------------------------------- -// Write 16-bit sample to audio input. -// NB! The caller is responsible for keeping the value within 16 bits. -// Note that to mix in an external audio signal, the signal should be -// resampled to 1MHz first to avoid sampling noise. -// ---------------------------------------------------------------------------- -void cSID::input(int sample) -{ - // Voice outputs are 20 bits. Scale up to match three voices in order - // to facilitate simulation of the MOS8580 "digi boost" hardware hack. - ext_in = (sample << 4)*3; -} - -// ---------------------------------------------------------------------------- -// Read sample from audio output. -// Both 16-bit and n-bit output is provided. -// ---------------------------------------------------------------------------- -int cSID::output() -{ - const int range = 1 << 16; - const int half = range >> 1; - int sample = extfilt.output()/((4095*255 >> 7)*3*15*2/range); - if (sample >= half) { - return half - 1; - } - if (sample < -half) { - return -half; - } - return sample; -} - -int cSID::output(int bits) -{ - const int range = 1 << bits; - const int half = range >> 1; - int sample = extfilt.output()/((4095*255 >> 7)*3*15*2/range); - if (sample >= half) { - return half - 1; - } - if (sample < -half) { - return -half; - } - return sample; -} - - -// ---------------------------------------------------------------------------- -// Read registers. -// -// Reading a write only register returns the last byte written to any SID -// register. The individual bits in this value start to fade down towards -// zero after a few cycles. All bits reach zero within approximately -// $2000 - $4000 cycles. -// It has been claimed that this fading happens in an orderly fashion, however -// sampling of write only registers reveals that this is not the case. -// NB! This is not correctly modeled. -// The actual use of write only registers has largely been made in the belief -// that all SID registers are readable. To support this belief the read -// would have to be done immediately after a write to the same register -// (remember that an intermediate write to another register would yield that -// value instead). With this in mind we return the last value written to -// any SID register for $2000 cycles without modeling the bit fading. -// ---------------------------------------------------------------------------- -reg8 cSID::read(reg8 offset) -{ - switch (offset) { - case 0x19: - return potx.readPOT(); - case 0x1a: - return poty.readPOT(); - case 0x1b: - return voice[2].wave.readOSC(); - case 0x1c: - return voice[2].envelope.readENV(); - default: - return bus_value; - } -} - - -// ---------------------------------------------------------------------------- -// Write registers. -// ---------------------------------------------------------------------------- -void cSID::write(reg8 offset, reg8 value) -{ - bus_value = value; - bus_value_ttl = 0x2000; - - switch (offset) { - case 0x00: - voice[0].wave.writeFREQ_LO(value); - break; - case 0x01: - voice[0].wave.writeFREQ_HI(value); - break; - case 0x02: - voice[0].wave.writePW_LO(value); - break; - case 0x03: - voice[0].wave.writePW_HI(value); - break; - case 0x04: - voice[0].writeCONTROL_REG(value); - break; - case 0x05: - voice[0].envelope.writeATTACK_DECAY(value); - break; - case 0x06: - voice[0].envelope.writeSUSTAIN_RELEASE(value); - break; - case 0x07: - voice[1].wave.writeFREQ_LO(value); - break; - case 0x08: - voice[1].wave.writeFREQ_HI(value); - break; - case 0x09: - voice[1].wave.writePW_LO(value); - break; - case 0x0a: - voice[1].wave.writePW_HI(value); - break; - case 0x0b: - voice[1].writeCONTROL_REG(value); - break; - case 0x0c: - voice[1].envelope.writeATTACK_DECAY(value); - break; - case 0x0d: - voice[1].envelope.writeSUSTAIN_RELEASE(value); - break; - case 0x0e: - voice[2].wave.writeFREQ_LO(value); - break; - case 0x0f: - voice[2].wave.writeFREQ_HI(value); - break; - case 0x10: - voice[2].wave.writePW_LO(value); - break; - case 0x11: - voice[2].wave.writePW_HI(value); - break; - case 0x12: - voice[2].writeCONTROL_REG(value); - break; - case 0x13: - voice[2].envelope.writeATTACK_DECAY(value); - break; - case 0x14: - voice[2].envelope.writeSUSTAIN_RELEASE(value); - break; - case 0x15: - filter.writeFC_LO(value); - break; - case 0x16: - filter.writeFC_HI(value); - break; - case 0x17: - filter.writeRES_FILT(value); - break; - case 0x18: - filter.writeMODE_VOL(value); - break; - default: - break; - } -} - - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -cSID::State::State() -{ - int i; - - for (i = 0; i < 0x20; i++) { - sid_register[i] = 0; - } - - bus_value = 0; - bus_value_ttl = 0; - - for (i = 0; i < 3; i++) { - accumulator[i] = 0; - shift_register[i] = 0x7ffff8; - rate_counter[i] = 0; - rate_counter_period[i] = 9; - exponential_counter[i] = 0; - exponential_counter_period[i] = 1; - envelope_counter[i] = 0; - envelope_state[i] = EnvelopeGenerator::RELEASE; - hold_zero[i] = true; - } -} - - -// ---------------------------------------------------------------------------- -// Read state. -// ---------------------------------------------------------------------------- -cSID::State cSID::read_state() -{ - State state; - int i, j; - - for (i = 0, j = 0; i < 3; i++, j += 7) { - WaveformGenerator& wave = voice[i].wave; - EnvelopeGenerator& envelope = voice[i].envelope; - state.sid_register[j + 0] = wave.freq & 0xff; - state.sid_register[j + 1] = wave.freq >> 8; - state.sid_register[j + 2] = wave.pw & 0xff; - state.sid_register[j + 3] = wave.pw >> 8; - state.sid_register[j + 4] = - (wave.waveform << 4) - | (wave.test ? 0x08 : 0) - | (wave.ring_mod ? 0x04 : 0) - | (wave.sync ? 0x02 : 0) - | (envelope.gate ? 0x01 : 0); - state.sid_register[j + 5] = (envelope.attack << 4) | envelope.decay; - state.sid_register[j + 6] = (envelope.sustain << 4) | envelope.release; - } - - state.sid_register[j++] = filter.fc & 0x007; - state.sid_register[j++] = filter.fc >> 3; - state.sid_register[j++] = (filter.res << 4) | filter.filt; - state.sid_register[j++] = - (filter.voice3off ? 0x80 : 0) - | (filter.hp_bp_lp << 4) - | filter.vol; - - // These registers are superfluous, but included for completeness. - for (; j < 0x1d; j++) { - state.sid_register[j] = read(j); - } - for (; j < 0x20; j++) { - state.sid_register[j] = 0; - } - - state.bus_value = bus_value; - state.bus_value_ttl = bus_value_ttl; - - for (i = 0; i < 3; i++) { - state.accumulator[i] = voice[i].wave.accumulator; - state.shift_register[i] = voice[i].wave.shift_register; - state.rate_counter[i] = voice[i].envelope.rate_counter; - state.rate_counter_period[i] = voice[i].envelope.rate_period; - state.exponential_counter[i] = voice[i].envelope.exponential_counter; - state.exponential_counter_period[i] = voice[i].envelope.exponential_counter_period; - state.envelope_counter[i] = voice[i].envelope.envelope_counter; - state.envelope_state[i] = voice[i].envelope.state; - state.hold_zero[i] = voice[i].envelope.hold_zero; - } - - return state; -} - - -// ---------------------------------------------------------------------------- -// Write state. -// ---------------------------------------------------------------------------- -void cSID::write_state(const State& state) -{ - int i; - - for (i = 0; i <= 0x18; i++) { - write(i, state.sid_register[i]); - } - - bus_value = state.bus_value; - bus_value_ttl = state.bus_value_ttl; - - for (i = 0; i < 3; i++) { - voice[i].wave.accumulator = state.accumulator[i]; - voice[i].wave.shift_register = state.shift_register[i]; - voice[i].envelope.rate_counter = state.rate_counter[i]; - voice[i].envelope.rate_period = state.rate_counter_period[i]; - voice[i].envelope.exponential_counter = state.exponential_counter[i]; - voice[i].envelope.exponential_counter_period = state.exponential_counter_period[i]; - voice[i].envelope.envelope_counter = state.envelope_counter[i]; - voice[i].envelope.state = state.envelope_state[i]; - voice[i].envelope.hold_zero = state.hold_zero[i]; - } -} - - -// ---------------------------------------------------------------------------- -// Enable filter. -// ---------------------------------------------------------------------------- -void cSID::enable_filter(bool enable) -{ - filter.enable_filter(enable); -} - - -// ---------------------------------------------------------------------------- -// Enable external filter. -// ---------------------------------------------------------------------------- -void cSID::enable_external_filter(bool enable) -{ - extfilt.enable_filter(enable); -} - - -// ---------------------------------------------------------------------------- -// I0() computes the 0th order modified Bessel function of the first kind. -// This function is originally from resample-1.5/filterkit.c by J. O. Smith. -// ---------------------------------------------------------------------------- -double cSID::I0(double x) -{ - // Max error acceptable in I0. - const double I0e = 1e-6; - - double sum, u, halfx, temp; - int n; - - sum = u = n = 1; - halfx = x/2.0; - - do { - temp = halfx/n++; - u *= temp*temp; - sum += u; - } while (u >= I0e*sum); - - return sum; -} - - -// ---------------------------------------------------------------------------- -// Setting of SID sampling parameters. -// -// Use a clock freqency of 985248Hz for PAL C64, 1022730Hz for NTSC C64. -// The default end of passband frequency is pass_freq = 0.9*sample_freq/2 -// for sample frequencies up to ~ 44.1kHz, and 20kHz for higher sample -// frequencies. -// -// For resampling, the ratio between the clock frequency and the sample -// frequency is limited as follows: -// 125*clock_freq/sample_freq < 16384 -// E.g. provided a clock frequency of ~ 1MHz, the sample frequency can not -// be set lower than ~ 8kHz. A lower sample frequency would make the -// resampling code overfill its 16k sample ring buffer. -// -// The end of passband frequency is also limited: -// pass_freq <= 0.9*sample_freq/2 - -// E.g. for a 44.1kHz sampling rate the end of passband frequency is limited -// to slightly below 20kHz. This constraint ensures that the FIR table is -// not overfilled. -// ---------------------------------------------------------------------------- -bool cSID::set_sampling_parameters(double clock_freq, sampling_method method, - double sample_freq, double pass_freq, - double filter_scale) -{ - // Check resampling constraints. - if (method == SAMPLE_RESAMPLE_INTERPOLATE || method == SAMPLE_RESAMPLE_FAST) - { - // Check whether the sample ring buffer would overfill. - if (FIR_N*clock_freq/sample_freq >= RINGSIZE) { - return false; - } - - // The default passband limit is 0.9*sample_freq/2 for sample - // frequencies below ~ 44.1kHz, and 20kHz for higher sample frequencies. - if (pass_freq < 0) { - pass_freq = 20000; - if (2*pass_freq/sample_freq >= 0.9) { - pass_freq = 0.9*sample_freq/2; - } - } - // Check whether the FIR table would overfill. - else if (pass_freq > 0.9*sample_freq/2) { - return false; - } - - // The filter scaling is only included to avoid clipping, so keep - // it sane. - if (filter_scale < 0.9 || filter_scale > 1.0) { - return false; - } - } - - clock_frequency = clock_freq; - sampling = method; - - cycles_per_sample = - cycle_count(clock_freq/sample_freq*(1 << FIXP_SHIFT) + 0.5); - - sample_offset = 0; - sample_prev = 0; - - // FIR initialization is only necessary for resampling. - if (method != SAMPLE_RESAMPLE_INTERPOLATE && method != SAMPLE_RESAMPLE_FAST) - { - delete[] sample; - delete[] fir; - sample = 0; - fir = 0; - return true; - } - - const double pi = 3.1415926535897932385; - - // 16 bits -> -96dB stopband attenuation. - const double A = -20*log10(1.0/(1 << 16)); - // A fraction of the bandwidth is allocated to the transition band, - double dw = (1 - 2*pass_freq/sample_freq)*pi; - // The cutoff frequency is midway through the transition band. - double wc = (2*pass_freq/sample_freq + 1)*pi/2; - - // For calculation of beta and N see the reference for the kaiserord - // function in the MATLAB Signal Processing Toolbox: - // http://www.mathworks.com/access/helpdesk/help/toolbox/signal/kaiserord.html - const double beta = 0.1102*(A - 8.7); - const double I0beta = I0(beta); - - // The filter order will maximally be 124 with the current constraints. - // N >= (96.33 - 7.95)/(2.285*0.1*pi) -> N >= 123 - // The filter order is equal to the number of zero crossings, i.e. - // it should be an even number (sinc is symmetric about x = 0). - int N = int((A - 7.95)/(2.285*dw) + 0.5); - N += N & 1; - - double f_samples_per_cycle = sample_freq/clock_freq; - double f_cycles_per_sample = clock_freq/sample_freq; - - // The filter length is equal to the filter order + 1. - // The filter length must be an odd number (sinc is symmetric about x = 0). - fir_N = int(N*f_cycles_per_sample) + 1; - fir_N |= 1; - - // We clamp the filter table resolution to 2^n, making the fixpoint - // sample_offset a whole multiple of the filter table resolution. - int res = method == SAMPLE_RESAMPLE_INTERPOLATE ? - FIR_RES_INTERPOLATE : FIR_RES_FAST; - int n = (int)ceil(log(res/f_cycles_per_sample)/log(2)); - fir_RES = 1 << n; - - // Allocate memory for FIR tables. - delete[] fir; - fir = new short[fir_N*fir_RES]; - - // Calculate fir_RES FIR tables for linear interpolation. - for (int i = 0; i < fir_RES; i++) { - int fir_offset = i*fir_N + fir_N/2; - double j_offset = double(i)/fir_RES; - // Calculate FIR table. This is the sinc function, weighted by the - // Kaiser window. - for (int j = -fir_N/2; j <= fir_N/2; j++) { - double jx = j - j_offset; - double wt = wc*jx/f_cycles_per_sample; - double temp = jx/(fir_N/2); - double Kaiser = - fabs(temp) <= 1 ? I0(beta*sqrt(1 - temp*temp))/I0beta : 0; - double sincwt = - fabs(wt) >= 1e-6 ? sin(wt)/wt : 1; - double val = - (1 << FIR_SHIFT)*filter_scale*f_samples_per_cycle*wc/pi*sincwt*Kaiser; - fir[fir_offset + j] = short(val + 0.5); - } - } - - // Allocate sample buffer. - if (!sample) { - sample = new short[RINGSIZE*2]; - } - // Clear sample buffer. - for (int j = 0; j < RINGSIZE*2; j++) { - sample[j] = 0; - } - sample_index = 0; - - return true; -} - - -// ---------------------------------------------------------------------------- -// Adjustment of SID sampling frequency. -// -// In some applications, e.g. a C64 emulator, it can be desirable to -// synchronize sound with a timer source. This is supported by adjustment of -// the SID sampling frequency. -// -// NB! Adjustment of the sampling frequency may lead to noticeable shifts in -// frequency, and should only be used for interactive applications. Note also -// that any adjustment of the sampling frequency will change the -// characteristics of the resampling filter, since the filter is not rebuilt. -// ---------------------------------------------------------------------------- -void cSID::adjust_sampling_frequency(double sample_freq) -{ - cycles_per_sample = - cycle_count(clock_frequency/sample_freq*(1 << FIXP_SHIFT) + 0.5); -} - - -// ---------------------------------------------------------------------------- -// Return array of default spline interpolation points to map FC to -// filter cutoff frequency. -// ---------------------------------------------------------------------------- -void cSID::fc_default(const fc_point*& points, int& count) -{ - filter.fc_default(points, count); -} - - -// ---------------------------------------------------------------------------- -// Return FC spline plotter object. -// ---------------------------------------------------------------------------- -PointPlotter cSID::fc_plotter() -{ - return filter.fc_plotter(); -} - - -// ---------------------------------------------------------------------------- -// SID clocking - 1 cycle. -// ---------------------------------------------------------------------------- -void cSID::clock() -{ - int i; - - // Age bus value. - if (--bus_value_ttl <= 0) { - bus_value = 0; - bus_value_ttl = 0; - } - - // Clock amplitude modulators. - for (i = 0; i < 3; i++) { - voice[i].envelope.clock(); - } - - // Clock oscillators. - for (i = 0; i < 3; i++) { - voice[i].wave.clock(); - } - - // Synchronize oscillators. - for (i = 0; i < 3; i++) { - voice[i].wave.synchronize(); - } - - // Clock filter. - filter.clock(voice[0].output(), voice[1].output(), voice[2].output(), ext_in); - - // Clock external filter. - extfilt.clock(filter.output()); -} - - -// ---------------------------------------------------------------------------- -// SID clocking - delta_t cycles. -// ---------------------------------------------------------------------------- -void cSID::clock(cycle_count delta_t) -{ - int i; - - if (delta_t <= 0) { - return; - } - - // Age bus value. - bus_value_ttl -= delta_t; - if (bus_value_ttl <= 0) { - bus_value = 0; - bus_value_ttl = 0; - } - - // Clock amplitude modulators. - for (i = 0; i < 3; i++) { - voice[i].envelope.clock(delta_t); - } - - // Clock and synchronize oscillators. - // Loop until we reach the current cycle. - cycle_count delta_t_osc = delta_t; - while (delta_t_osc) { - cycle_count delta_t_min = delta_t_osc; - - // Find minimum number of cycles to an oscillator accumulator MSB toggle. - // We have to clock on each MSB on / MSB off for hard sync to operate - // correctly. - for (i = 0; i < 3; i++) { - WaveformGenerator& wave = voice[i].wave; - - // It is only necessary to clock on the MSB of an oscillator that is - // a sync source and has freq != 0. - if (!(wave.sync_dest->sync && wave.freq)) { - continue; - } - - reg16 freq = wave.freq; - reg24 accumulator = wave.accumulator; - - // Clock on MSB off if MSB is on, clock on MSB on if MSB is off. - reg24 delta_accumulator = - (accumulator & 0x800000 ? 0x1000000 : 0x800000) - accumulator; - - cycle_count delta_t_next = delta_accumulator/freq; - if (delta_accumulator%freq) { - ++delta_t_next; - } - - if (delta_t_next < delta_t_min) { - delta_t_min = delta_t_next; - } - } - - // Clock oscillators. - for (i = 0; i < 3; i++) { - voice[i].wave.clock(delta_t_min); - } - - // Synchronize oscillators. - for (i = 0; i < 3; i++) { - voice[i].wave.synchronize(); - } - - delta_t_osc -= delta_t_min; - } - - // Clock filter. - filter.clock(delta_t, - voice[0].output(), voice[1].output(), voice[2].output(), ext_in); - - // Clock external filter. - extfilt.clock(delta_t, filter.output()); -} - - -// ---------------------------------------------------------------------------- -// SID clocking with audio sampling. -// Fixpoint arithmetics is used. -// -// The example below shows how to clock the SID a specified amount of cycles -// while producing audio output: -// -// while (delta_t) { -// bufindex += sid.clock(delta_t, buf + bufindex, buflength - bufindex); -// write(dsp, buf, bufindex*2); -// bufindex = 0; -// } -// -// ---------------------------------------------------------------------------- -int cSID::clock(cycle_count& delta_t, short* buf, int n, int interleave) -{ - switch (sampling) { - default: - case SAMPLE_FAST: - return clock_fast(delta_t, buf, n, interleave); - case SAMPLE_INTERPOLATE: - return clock_interpolate(delta_t, buf, n, interleave); - case SAMPLE_RESAMPLE_INTERPOLATE: - return clock_resample_interpolate(delta_t, buf, n, interleave); - case SAMPLE_RESAMPLE_FAST: - return clock_resample_fast(delta_t, buf, n, interleave); - } -} - -// ---------------------------------------------------------------------------- -// SID clocking with audio sampling - delta clocking picking nearest sample. -// ---------------------------------------------------------------------------- -RESID_INLINE -int cSID::clock_fast(cycle_count& delta_t, short* buf, int n, - int interleave) -{ - int s = 0; - - for (;;) { - cycle_count next_sample_offset = sample_offset + cycles_per_sample + (1 << (FIXP_SHIFT - 1)); - cycle_count delta_t_sample = next_sample_offset >> FIXP_SHIFT; - if (delta_t_sample > delta_t) { - break; - } - if (s >= n) { - return s; - } - clock(delta_t_sample); - delta_t -= delta_t_sample; - sample_offset = (next_sample_offset & FIXP_MASK) - (1 << (FIXP_SHIFT - 1)); - buf[s++*interleave] = output(); - } - - clock(delta_t); - sample_offset -= delta_t << FIXP_SHIFT; - delta_t = 0; - return s; -} - - -// ---------------------------------------------------------------------------- -// SID clocking with audio sampling - cycle based with linear sample -// interpolation. -// -// Here the chip is clocked every cycle. This yields higher quality -// sound since the samples are linearly interpolated, and since the -// external filter attenuates frequencies above 16kHz, thus reducing -// sampling noise. -// ---------------------------------------------------------------------------- -RESID_INLINE -int cSID::clock_interpolate(cycle_count& delta_t, short* buf, int n, - int interleave) -{ - int s = 0; - int i; - - for (;;) { - cycle_count next_sample_offset = sample_offset + cycles_per_sample; - cycle_count delta_t_sample = next_sample_offset >> FIXP_SHIFT; - if (delta_t_sample > delta_t) { - break; - } - if (s >= n) { - return s; - } - for (i = 0; i < delta_t_sample - 1; i++) { - clock(); - } - if (i < delta_t_sample) { - sample_prev = output(); - clock(); - } - - delta_t -= delta_t_sample; - sample_offset = next_sample_offset & FIXP_MASK; - - short sample_now = output(); - buf[s++*interleave] = - sample_prev + (sample_offset*(sample_now - sample_prev) >> FIXP_SHIFT); - sample_prev = sample_now; - } - - for (i = 0; i < delta_t - 1; i++) { - clock(); - } - if (i < delta_t) { - sample_prev = output(); - clock(); - } - sample_offset -= delta_t << FIXP_SHIFT; - delta_t = 0; - return s; -} - - -// ---------------------------------------------------------------------------- -// SID clocking with audio sampling - cycle based with audio resampling. -// -// This is the theoretically correct (and computationally intensive) audio -// sample generation. The samples are generated by resampling to the specified -// sampling frequency. The work rate is inversely proportional to the -// percentage of the bandwidth allocated to the filter transition band. -// -// This implementation is based on the paper "A Flexible Sampling-Rate -// Conversion Method", by J. O. Smith and P. Gosset, or rather on the -// expanded tutorial on the "Digital Audio Resampling Home Page": -// http://www-ccrma.stanford.edu/~jos/resample/ -// -// By building shifted FIR tables with samples according to the -// sampling frequency, this implementation dramatically reduces the -// computational effort in the filter convolutions, without any loss -// of accuracy. The filter convolutions are also vectorizable on -// current hardware. -// -// Further possible optimizations are: -// * An equiripple filter design could yield a lower filter order, see -// http://www.mwrf.com/Articles/ArticleID/7229/7229.html -// * The Convolution Theorem could be used to bring the complexity of -// convolution down from O(n*n) to O(n*log(n)) using the Fast Fourier -// Transform, see http://en.wikipedia.org/wiki/Convolution_theorem -// * Simply resampling in two steps can also yield computational -// savings, since the transition band will be wider in the first step -// and the required filter order is thus lower in this step. -// Laurent Ganier has found the optimal intermediate sampling frequency -// to be (via derivation of sum of two steps): -// 2 * pass_freq + sqrt [ 2 * pass_freq * orig_sample_freq -// * (dest_sample_freq - 2 * pass_freq) / dest_sample_freq ] -// -// NB! the result of right shifting negative numbers is really -// implementation dependent in the C++ standard. -// ---------------------------------------------------------------------------- -RESID_INLINE -int cSID::clock_resample_interpolate(cycle_count& delta_t, short* buf, int n, - int interleave) -{ - int s = 0; - - for (;;) { - cycle_count next_sample_offset = sample_offset + cycles_per_sample; - cycle_count delta_t_sample = next_sample_offset >> FIXP_SHIFT; - if (delta_t_sample > delta_t) { - break; - } - if (s >= n) { - return s; - } - for (int i = 0; i < delta_t_sample; i++) { - clock(); - sample[sample_index] = sample[sample_index + RINGSIZE] = output(); - ++sample_index; - sample_index &= 0x3fff; - } - delta_t -= delta_t_sample; - sample_offset = next_sample_offset & FIXP_MASK; - - int fir_offset = sample_offset*fir_RES >> FIXP_SHIFT; - int fir_offset_rmd = sample_offset*fir_RES & FIXP_MASK; - short* fir_start = fir + fir_offset*fir_N; - short* sample_start = sample + sample_index - fir_N + RINGSIZE; - - // Convolution with filter impulse response. - int v1 = 0; - for (int j = 0; j < fir_N; j++) { - v1 += sample_start[j]*fir_start[j]; - } - - // Use next FIR table, wrap around to first FIR table using - // previous sample. - if (++fir_offset == fir_RES) { - fir_offset = 0; - --sample_start; - } - fir_start = fir + fir_offset*fir_N; - - // Convolution with filter impulse response. - int v2 = 0; - for (int j = 0; j < fir_N; j++) { - v2 += sample_start[j]*fir_start[j]; - } - - // Linear interpolation. - // fir_offset_rmd is equal for all samples, it can thus be factorized out: - // sum(v1 + rmd*(v2 - v1)) = sum(v1) + rmd*(sum(v2) - sum(v1)) - int v = v1 + (fir_offset_rmd*(v2 - v1) >> FIXP_SHIFT); - - v >>= FIR_SHIFT; - - // Saturated arithmetics to guard against 16 bit sample overflow. - const int half = 1 << 15; - if (v >= half) { - v = half - 1; - } - else if (v < -half) { - v = -half; - } - - buf[s++*interleave] = v; - } - - for (int i = 0; i < delta_t; i++) { - clock(); - sample[sample_index] = sample[sample_index + RINGSIZE] = output(); - ++sample_index; - sample_index &= 0x3fff; - } - sample_offset -= delta_t << FIXP_SHIFT; - delta_t = 0; - return s; -} - - -// ---------------------------------------------------------------------------- -// SID clocking with audio sampling - cycle based with audio resampling. -// ---------------------------------------------------------------------------- -RESID_INLINE -int cSID::clock_resample_fast(cycle_count& delta_t, short* buf, int n, - int interleave) -{ - int s = 0; - - for (;;) { - cycle_count next_sample_offset = sample_offset + cycles_per_sample; - cycle_count delta_t_sample = next_sample_offset >> FIXP_SHIFT; - if (delta_t_sample > delta_t) { - break; - } - if (s >= n) { - return s; - } - for (int i = 0; i < delta_t_sample; i++) { - clock(); - sample[sample_index] = sample[sample_index + RINGSIZE] = output(); - ++sample_index; - sample_index &= 0x3fff; - } - delta_t -= delta_t_sample; - sample_offset = next_sample_offset & FIXP_MASK; - - int fir_offset = sample_offset*fir_RES >> FIXP_SHIFT; - short* fir_start = fir + fir_offset*fir_N; - short* sample_start = sample + sample_index - fir_N + RINGSIZE; - - // Convolution with filter impulse response. - int v = 0; - for (int j = 0; j < fir_N; j++) { - v += sample_start[j]*fir_start[j]; - } - - v >>= FIR_SHIFT; - - // Saturated arithmetics to guard against 16 bit sample overflow. - const int half = 1 << 15; - if (v >= half) { - v = half - 1; - } - else if (v < -half) { - v = -half; - } - - buf[s++*interleave] = v; - } - - for (int i = 0; i < delta_t; i++) { - clock(); - sample[sample_index] = sample[sample_index + RINGSIZE] = output(); - ++sample_index; - sample_index &= 0x3fff; - } - sample_offset -= delta_t << FIXP_SHIFT; - delta_t = 0; - return s; -} diff --git a/plugins/sid/sid.h b/plugins/sid/sid.h deleted file mode 100644 index 0f596a55604..00000000000 --- a/plugins/sid/sid.h +++ /dev/null @@ -1,146 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __SID_H__ -#define __SID_H__ - -#include "siddefs.h" -#include "voice.h" -#include "filter.h" -#include "extfilt.h" -#include "pot.h" - -class cSID -{ -public: - cSID(); - ~cSID(); - - void set_chip_model(chip_model model); - void enable_filter(bool enable); - void enable_external_filter(bool enable); - bool set_sampling_parameters(double clock_freq, sampling_method method, - double sample_freq, double pass_freq = -1, - double filter_scale = 0.97); - void adjust_sampling_frequency(double sample_freq); - - void fc_default(const fc_point*& points, int& count); - PointPlotter fc_plotter(); - - void clock(); - void clock(cycle_count delta_t); - int clock(cycle_count& delta_t, short* buf, int n, int interleave = 1); - void reset(); - - // Read/write registers. - reg8 read(reg8 offset); - void write(reg8 offset, reg8 value); - - // Read/write state. - class State - { - public: - State(); - - char sid_register[0x20]; - - reg8 bus_value; - cycle_count bus_value_ttl; - - reg24 accumulator[3]; - reg24 shift_register[3]; - reg16 rate_counter[3]; - reg16 rate_counter_period[3]; - reg16 exponential_counter[3]; - reg16 exponential_counter_period[3]; - reg8 envelope_counter[3]; - EnvelopeGenerator::State envelope_state[3]; - bool hold_zero[3]; - }; - - State read_state(); - void write_state(const State& state); - - // 16-bit input (EXT IN). - void input(int sample); - - // 16-bit output (AUDIO OUT). - int output(); - // n-bit output. - int output(int bits); - -protected: - static double I0(double x); - RESID_INLINE int clock_fast(cycle_count& delta_t, short* buf, int n, - int interleave); - RESID_INLINE int clock_interpolate(cycle_count& delta_t, short* buf, int n, - int interleave); - RESID_INLINE int clock_resample_interpolate(cycle_count& delta_t, short* buf, - int n, int interleave); - RESID_INLINE int clock_resample_fast(cycle_count& delta_t, short* buf, - int n, int interleave); - - Voice voice[3]; - Filter filter; - ExternalFilter extfilt; - Potentiometer potx; - Potentiometer poty; - - reg8 bus_value; - cycle_count bus_value_ttl; - - double clock_frequency; - - // External audio input. - int ext_in; - - // Resampling constants. - // The error in interpolated lookup is bounded by 1.234/L^2, - // while the error in non-interpolated lookup is bounded by - // 0.7854/L + 0.4113/L^2, see - // http://www-ccrma.stanford.edu/~jos/resample/Choice_Table_Size.html - // For a resolution of 16 bits this yields L >= 285 and L >= 51473, - // respectively. - static const int FIR_N; - static const int FIR_RES_INTERPOLATE; - static const int FIR_RES_FAST; - static const int FIR_SHIFT; - static const int RINGSIZE; - - // Fixpoint constants (16.16 bits). - static const int FIXP_SHIFT; - static const int FIXP_MASK; - - // Sampling variables. - sampling_method sampling; - cycle_count cycles_per_sample; - cycle_count sample_offset; - int sample_index; - short sample_prev; - int fir_N; - int fir_RES; - - // Ring buffer with overflow for contiguous storage of RINGSIZE samples. - short* sample; - - // FIR_RES filter tables (FIR_N*FIR_RES). - short* fir; -}; - -#endif // not __SID_H__ diff --git a/plugins/sid/siddefs.h b/plugins/sid/siddefs.h deleted file mode 100644 index e8a9ef57816..00000000000 --- a/plugins/sid/siddefs.h +++ /dev/null @@ -1,69 +0,0 @@ -#define VERSION "0.16" - -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 1999 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __SIDDEFS_H__ -#define __SIDDEFS_H__ - -// Define bool, true, and false for C++ compilers that lack these keywords. -#define RESID_HAVE_BOOL 1 - -#if !RESID_HAVE_BOOL -typedef int bool; -const bool true = 1; -const bool false = 0; -#endif - -// We could have used the smallest possible data type for each SID register, -// however this would give a slower engine because of data type conversions. -// An int is assumed to be at least 32 bits (necessary in the types reg24, -// cycle_count, and sound_sample). GNU does not support 16-bit machines -// (GNU Coding Standards: Portability between CPUs), so this should be -// a valid assumption. - -typedef unsigned int reg4; -typedef unsigned int reg8; -typedef unsigned int reg12; -typedef unsigned int reg16; -typedef unsigned int reg24; - -typedef int cycle_count; -typedef int sound_sample; -typedef sound_sample fc_point[2]; - -enum chip_model { MOS6581, MOS8580 }; - -enum sampling_method { SAMPLE_FAST, SAMPLE_INTERPOLATE, - SAMPLE_RESAMPLE_INTERPOLATE, SAMPLE_RESAMPLE_FAST }; - -extern "C" -{ -#ifndef __VERSION_CC__ -extern const char* resid_version_string; -#else -const char* resid_version_string = VERSION; -#endif -} - -// Inlining on/off. -#define RESID_INLINING 1 -#define RESID_INLINE inline - -#endif // not __SIDDEFS_H__ diff --git a/plugins/sid/spline.h b/plugins/sid/spline.h deleted file mode 100644 index 93ba2776aa0..00000000000 --- a/plugins/sid/spline.h +++ /dev/null @@ -1,272 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __SPLINE_H__ -#define __SPLINE_H__ - -// Our objective is to construct a smooth interpolating single-valued function -// y = f(x). -// -// Catmull-Rom splines are widely used for interpolation, however these are -// parametric curves [x(t) y(t) ...] and can not be used to directly calculate -// y = f(x). -// For a discussion of Catmull-Rom splines see Catmull, E., and R. Rom, -// "A Class of Local Interpolating Splines", Computer Aided Geometric Design. -// -// Natural cubic splines are single-valued functions, and have been used in -// several applications e.g. to specify gamma curves for image display. -// These splines do not afford local control, and a set of linear equations -// including all interpolation points must be solved before any point on the -// curve can be calculated. The lack of local control makes the splines -// more difficult to handle than e.g. Catmull-Rom splines, and real-time -// interpolation of a stream of data points is not possible. -// For a discussion of natural cubic splines, see e.g. Kreyszig, E., "Advanced -// Engineering Mathematics". -// -// Our approach is to approximate the properties of Catmull-Rom splines for -// piecewice cubic polynomials f(x) = ax^3 + bx^2 + cx + d as follows: -// Each curve segment is specified by four interpolation points, -// p0, p1, p2, p3. -// The curve between p1 and p2 must interpolate both p1 and p2, and in addition -// f'(p1.x) = k1 = (p2.y - p0.y)/(p2.x - p0.x) and -// f'(p2.x) = k2 = (p3.y - p1.y)/(p3.x - p1.x). -// -// The constraints are expressed by the following system of linear equations -// -// [ 1 xi xi^2 xi^3 ] [ d ] [ yi ] -// [ 1 2*xi 3*xi^2 ] * [ c ] = [ ki ] -// [ 1 xj xj^2 xj^3 ] [ b ] [ yj ] -// [ 1 2*xj 3*xj^2 ] [ a ] [ kj ] -// -// Solving using Gaussian elimination and back substitution, setting -// dy = yj - yi, dx = xj - xi, we get -// -// a = ((ki + kj) - 2*dy/dx)/(dx*dx); -// b = ((kj - ki)/dx - 3*(xi + xj)*a)/2; -// c = ki - (3*xi*a + 2*b)*xi; -// d = yi - ((xi*a + b)*xi + c)*xi; -// -// Having calculated the coefficients of the cubic polynomial we have the -// choice of evaluation by brute force -// -// for (x = x1; x <= x2; x += res) { -// y = ((a*x + b)*x + c)*x + d; -// plot(x, y); -// } -// -// or by forward differencing -// -// y = ((a*x1 + b)*x1 + c)*x1 + d; -// dy = (3*a*(x1 + res) + 2*b)*x1*res + ((a*res + b)*res + c)*res; -// d2y = (6*a*(x1 + res) + 2*b)*res*res; -// d3y = 6*a*res*res*res; -// -// for (x = x1; x <= x2; x += res) { -// plot(x, y); -// y += dy; dy += d2y; d2y += d3y; -// } -// -// See Foley, Van Dam, Feiner, Hughes, "Computer Graphics, Principles and -// Practice" for a discussion of forward differencing. -// -// If we have a set of interpolation points p0, ..., pn, we may specify -// curve segments between p0 and p1, and between pn-1 and pn by using the -// following constraints: -// f''(p0.x) = 0 and -// f''(pn.x) = 0. -// -// Substituting the results for a and b in -// -// 2*b + 6*a*xi = 0 -// -// we get -// -// ki = (3*dy/dx - kj)/2; -// -// or by substituting the results for a and b in -// -// 2*b + 6*a*xj = 0 -// -// we get -// -// kj = (3*dy/dx - ki)/2; -// -// Finally, if we have only two interpolation points, the cubic polynomial -// will degenerate to a straight line if we set -// -// ki = kj = dy/dx; -// - - -#if SPLINE_BRUTE_FORCE -#define interpolate_segment interpolate_brute_force -#else -#define interpolate_segment interpolate_forward_difference -#endif - - -// ---------------------------------------------------------------------------- -// Calculation of coefficients. -// ---------------------------------------------------------------------------- -inline -void cubic_coefficients(double x1, double y1, double x2, double y2, - double k1, double k2, - double& a, double& b, double& c, double& d) -{ - double dx = x2 - x1, dy = y2 - y1; - - a = ((k1 + k2) - 2*dy/dx)/(dx*dx); - b = ((k2 - k1)/dx - 3*(x1 + x2)*a)/2; - c = k1 - (3*x1*a + 2*b)*x1; - d = y1 - ((x1*a + b)*x1 + c)*x1; -} - -// ---------------------------------------------------------------------------- -// Evaluation of cubic polynomial by brute force. -// ---------------------------------------------------------------------------- -template -inline -void interpolate_brute_force(double x1, double y1, double x2, double y2, - double k1, double k2, - PointPlotter plot, double res) -{ - double a, b, c, d; - cubic_coefficients(x1, y1, x2, y2, k1, k2, a, b, c, d); - - // Calculate each point. - for (double x = x1; x <= x2; x += res) { - double y = ((a*x + b)*x + c)*x + d; - plot(x, y); - } -} - -// ---------------------------------------------------------------------------- -// Evaluation of cubic polynomial by forward differencing. -// ---------------------------------------------------------------------------- -template -inline -void interpolate_forward_difference(double x1, double y1, double x2, double y2, - double k1, double k2, - PointPlotter plot, double res) -{ - double a, b, c, d; - cubic_coefficients(x1, y1, x2, y2, k1, k2, a, b, c, d); - - double y = ((a*x1 + b)*x1 + c)*x1 + d; - double dy = (3*a*(x1 + res) + 2*b)*x1*res + ((a*res + b)*res + c)*res; - double d2y = (6*a*(x1 + res) + 2*b)*res*res; - double d3y = 6*a*res*res*res; - - // Calculate each point. - for (double x = x1; x <= x2; x += res) { - plot(x, y); - y += dy; dy += d2y; d2y += d3y; - } -} - -template -inline -double x(PointIter p) -{ - return (*p)[0]; -} - -template -inline -double y(PointIter p) -{ - return (*p)[1]; -} - -// ---------------------------------------------------------------------------- -// Evaluation of complete interpolating function. -// Note that since each curve segment is controlled by four points, the -// end points will not be interpolated. If extra control points are not -// desirable, the end points can simply be repeated to ensure interpolation. -// Note also that points of non-differentiability and discontinuity can be -// introduced by repeating points. -// ---------------------------------------------------------------------------- -template -inline -void interpolate(PointIter p0, PointIter pn, PointPlotter plot, double res) -{ - double k1, k2; - - // Set up points for first curve segment. - PointIter p1 = p0; ++p1; - PointIter p2 = p1; ++p2; - PointIter p3 = p2; ++p3; - - // Draw each curve segment. - for (; p2 != pn; ++p0, ++p1, ++p2, ++p3) { - // p1 and p2 equal; single point. - if (x(p1) == x(p2)) { - continue; - } - // Both end points repeated; straight line. - if (x(p0) == x(p1) && x(p2) == x(p3)) { - k1 = k2 = (y(p2) - y(p1))/(x(p2) - x(p1)); - } - // p0 and p1 equal; use f''(x1) = 0. - else if (x(p0) == x(p1)) { - k2 = (y(p3) - y(p1))/(x(p3) - x(p1)); - k1 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k2)/2; - } - // p2 and p3 equal; use f''(x2) = 0. - else if (x(p2) == x(p3)) { - k1 = (y(p2) - y(p0))/(x(p2) - x(p0)); - k2 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k1)/2; - } - // Normal curve. - else { - k1 = (y(p2) - y(p0))/(x(p2) - x(p0)); - k2 = (y(p3) - y(p1))/(x(p3) - x(p1)); - } - - interpolate_segment(x(p1), y(p1), x(p2), y(p2), k1, k2, plot, res); - } -} - -// ---------------------------------------------------------------------------- -// Class for plotting integers into an array. -// ---------------------------------------------------------------------------- -template -class PointPlotter -{ - protected: - F* f; - - public: - PointPlotter(F* arr) : f(arr) - { - } - - void operator ()(double x, double y) - { - // Clamp negative values to zero. - if (y < 0) { - y = 0; - } - - f[F(x)] = F(y); - } -}; - - -#endif // not __SPLINE_H__ diff --git a/plugins/sid/version.cc b/plugins/sid/version.cc deleted file mode 100644 index 3b61afd2499..00000000000 --- a/plugins/sid/version.cc +++ /dev/null @@ -1,21 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#define __VERSION_CC__ -#include "siddefs.h" diff --git a/plugins/sid/voice.cc b/plugins/sid/voice.cc deleted file mode 100644 index 9c0017f99b2..00000000000 --- a/plugins/sid/voice.cc +++ /dev/null @@ -1,132 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#define __VOICE_CC__ -#include "voice.h" - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -Voice::Voice() -{ - set_chip_model(MOS6581); -} - -// ---------------------------------------------------------------------------- -// Set chip model. -// ---------------------------------------------------------------------------- -void Voice::set_chip_model(chip_model model) -{ - wave.set_chip_model(model); - - if (model == MOS6581) { - // The waveform D/A converter introduces a DC offset in the signal - // to the envelope multiplying D/A converter. The "zero" level of - // the waveform D/A converter can be found as follows: - // - // Measure the "zero" voltage of voice 3 on the SID audio output - // pin, routing only voice 3 to the mixer ($d417 = $0b, $d418 = - // $0f, all other registers zeroed). - // - // Then set the sustain level for voice 3 to maximum and search for - // the waveform output value yielding the same voltage as found - // above. This is done by trying out different waveform output - // values until the correct value is found, e.g. with the following - // program: - // - // lda #$08 - // sta $d412 - // lda #$0b - // sta $d417 - // lda #$0f - // sta $d418 - // lda #$f0 - // sta $d414 - // lda #$21 - // sta $d412 - // lda #$01 - // sta $d40e - // - // ldx #$00 - // lda #$38 ; Tweak this to find the "zero" level - //l cmp $d41b - // bne l - // stx $d40e ; Stop frequency counter - freeze waveform output - // brk - // - // The waveform output range is 0x000 to 0xfff, so the "zero" - // level should ideally have been 0x800. In the measured chip, the - // waveform output "zero" level was found to be 0x380 (i.e. $d41b - // = 0x38) at 5.94V. - - wave_zero = 0x380; - - // The envelope multiplying D/A converter introduces another DC - // offset. This is isolated by the following measurements: - // - // * The "zero" output level of the mixer at full volume is 5.44V. - // * Routing one voice to the mixer at full volume yields - // 6.75V at maximum voice output (wave = 0xfff, sustain = 0xf) - // 5.94V at "zero" voice output (wave = any, sustain = 0x0) - // 5.70V at minimum voice output (wave = 0x000, sustain = 0xf) - // * The DC offset of one voice is (5.94V - 5.44V) = 0.50V - // * The dynamic range of one voice is |6.75V - 5.70V| = 1.05V - // * The DC offset is thus 0.50V/1.05V ~ 1/2 of the dynamic range. - // - // Note that by removing the DC offset, we get the following ranges for - // one voice: - // y > 0: (6.75V - 5.44V) - 0.50V = 0.81V - // y < 0: (5.70V - 5.44V) - 0.50V = -0.24V - // The scaling of the voice amplitude is not symmetric about y = 0; - // this follows from the DC level in the waveform output. - - voice_DC = 0x800*0xff; - } - else { - // No DC offsets in the MOS8580. - wave_zero = 0x800; - voice_DC = 0; - } -} - -// ---------------------------------------------------------------------------- -// Set sync source. -// ---------------------------------------------------------------------------- -void Voice::set_sync_source(Voice* source) -{ - wave.set_sync_source(&source->wave); -} - -// ---------------------------------------------------------------------------- -// Register functions. -// ---------------------------------------------------------------------------- -void Voice::writeCONTROL_REG(reg8 control) -{ - wave.writeCONTROL_REG(control); - envelope.writeCONTROL_REG(control); -} - -// ---------------------------------------------------------------------------- -// SID reset. -// ---------------------------------------------------------------------------- -void Voice::reset() -{ - wave.reset(); - envelope.reset(); -} diff --git a/plugins/sid/voice.h b/plugins/sid/voice.h deleted file mode 100644 index 599a77ab4a0..00000000000 --- a/plugins/sid/voice.h +++ /dev/null @@ -1,77 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __VOICE_H__ -#define __VOICE_H__ - -#include "siddefs.h" -#include "wave.h" -#include "envelope.h" - -class Voice -{ -public: - Voice(); - - void set_chip_model(chip_model model); - void set_sync_source(Voice*); - void reset(); - - void writeCONTROL_REG(reg8); - - // Amplitude modulated waveform output. - // Range [-2048*255, 2047*255]. - RESID_INLINE sound_sample output(); - -protected: - WaveformGenerator wave; - EnvelopeGenerator envelope; - - // Waveform D/A zero level. - sound_sample wave_zero; - - // Multiplying D/A DC offset. - sound_sample voice_DC; - -friend class cSID; -}; - - -// ---------------------------------------------------------------------------- -// Inline functions. -// The following function is defined inline because it is called every -// time a sample is calculated. -// ---------------------------------------------------------------------------- - -#if RESID_INLINING || defined(__VOICE_CC__) - -// ---------------------------------------------------------------------------- -// Amplitude modulated waveform output. -// Ideal range [-2048*255, 2047*255]. -// ---------------------------------------------------------------------------- -RESID_INLINE -sound_sample Voice::output() -{ - // Multiply oscillator output with envelope output. - return (wave.output() - wave_zero)*envelope.output() + voice_DC; -} - -#endif // RESID_INLINING || defined(__VOICE_CC__) - -#endif // not __VOICE_H__ diff --git a/plugins/sid/wave.cc b/plugins/sid/wave.cc deleted file mode 100644 index a72abd6450a..00000000000 --- a/plugins/sid/wave.cc +++ /dev/null @@ -1,144 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#define __WAVE_CC__ -#include "wave.h" - -// ---------------------------------------------------------------------------- -// Constructor. -// ---------------------------------------------------------------------------- -WaveformGenerator::WaveformGenerator() -{ - sync_source = this; - - set_chip_model(MOS6581); - - reset(); -} - - -// ---------------------------------------------------------------------------- -// Set sync source. -// ---------------------------------------------------------------------------- -void WaveformGenerator::set_sync_source(WaveformGenerator* source) -{ - sync_source = source; - source->sync_dest = this; -} - - -// ---------------------------------------------------------------------------- -// Set chip model. -// ---------------------------------------------------------------------------- -void WaveformGenerator::set_chip_model(chip_model model) -{ - if (model == MOS6581) { - wave__ST = wave6581__ST; - wave_P_T = wave6581_P_T; - wave_PS_ = wave6581_PS_; - wave_PST = wave6581_PST; - } - else { - wave__ST = wave8580__ST; - wave_P_T = wave8580_P_T; - wave_PS_ = wave8580_PS_; - wave_PST = wave8580_PST; - } -} - - -// ---------------------------------------------------------------------------- -// Register functions. -// ---------------------------------------------------------------------------- -void WaveformGenerator::writeFREQ_LO(reg8 freq_lo) -{ - freq = ( freq & 0xff00 ) | ( freq_lo & 0x00ff ); -} - -void WaveformGenerator::writeFREQ_HI(reg8 freq_hi) -{ - freq = ( (freq_hi << 8) & 0xff00 ) | ( freq & 0x00ff ); -} - -void WaveformGenerator::writePW_LO(reg8 pw_lo) -{ - pw = ( pw & 0xf00 ) | ( pw_lo & 0x0ff ); -} - -void WaveformGenerator::writePW_HI(reg8 pw_hi) -{ - pw = ( (pw_hi << 8) & 0xf00 ) | ( pw & 0x0ff ); -} - -void WaveformGenerator::writeCONTROL_REG(reg8 control) -{ - waveform = (control >> 4) & 0x0f; - ring_mod = control & 0x04; - sync = control & 0x02; - - reg8 test_next = control & 0x08; - - // Test bit set. - // The accumulator and the shift register are both cleared. - // NB! The shift register is not really cleared immediately. It seems like - // the individual bits in the shift register start to fade down towards - // zero when test is set. All bits reach zero within approximately - // $2000 - $4000 cycles. - // This is not modeled. There should fortunately be little audible output - // from this peculiar behavior. - if (test_next) { - accumulator = 0; - shift_register = 0; - } - // Test bit cleared. - // The accumulator starts counting, and the shift register is reset to - // the value 0x7ffff8. - // NB! The shift register will not actually be set to this exact value if the - // shift register bits have not had time to fade to zero. - // This is not modeled. - else if (test) { - shift_register = 0x7ffff8; - } - - test = test_next; - - // The gate bit is handled by the EnvelopeGenerator. -} - -reg8 WaveformGenerator::readOSC() -{ - return output() >> 4; -} - -// ---------------------------------------------------------------------------- -// SID reset. -// ---------------------------------------------------------------------------- -void WaveformGenerator::reset() -{ - accumulator = 0; - shift_register = 0x7ffff8; - freq = 0; - pw = 0; - - test = 0; - ring_mod = 0; - sync = 0; - - msb_rising = false; -} diff --git a/plugins/sid/wave.h b/plugins/sid/wave.h deleted file mode 100644 index 9101b30851c..00000000000 --- a/plugins/sid/wave.h +++ /dev/null @@ -1,503 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#ifndef __WAVE_H__ -#define __WAVE_H__ - -#include "siddefs.h" - -// ---------------------------------------------------------------------------- -// A 24 bit accumulator is the basis for waveform generation. FREQ is added to -// the lower 16 bits of the accumulator each cycle. -// The accumulator is set to zero when TEST is set, and starts counting -// when TEST is cleared. -// The noise waveform is taken from intermediate bits of a 23 bit shift -// register. This register is clocked by bit 19 of the accumulator. -// ---------------------------------------------------------------------------- -class WaveformGenerator -{ -public: - WaveformGenerator(); - - void set_sync_source(WaveformGenerator*); - void set_chip_model(chip_model model); - - RESID_INLINE void clock(); - RESID_INLINE void clock(cycle_count delta_t); - RESID_INLINE void synchronize(); - void reset(); - - void writeFREQ_LO(reg8); - void writeFREQ_HI(reg8); - void writePW_LO(reg8); - void writePW_HI(reg8); - void writeCONTROL_REG(reg8); - reg8 readOSC(); - - // 12-bit waveform output. - RESID_INLINE reg12 output(); - -protected: - const WaveformGenerator* sync_source; - WaveformGenerator* sync_dest; - - // Tell whether the accumulator MSB was set high on this cycle. - bool msb_rising; - - reg24 accumulator; - reg24 shift_register; - - // Fout = (Fn*Fclk/16777216)Hz - reg16 freq; - // PWout = (PWn/40.95)% - reg12 pw; - - // The control register right-shifted 4 bits; used for output function - // table lookup. - reg8 waveform; - - // The remaining control register bits. - reg8 test; - reg8 ring_mod; - reg8 sync; - // The gate bit is handled by the EnvelopeGenerator. - - // 16 possible combinations of waveforms. - RESID_INLINE reg12 output____(); - RESID_INLINE reg12 output___T(); - RESID_INLINE reg12 output__S_(); - RESID_INLINE reg12 output__ST(); - RESID_INLINE reg12 output_P__(); - RESID_INLINE reg12 output_P_T(); - RESID_INLINE reg12 output_PS_(); - RESID_INLINE reg12 output_PST(); - RESID_INLINE reg12 outputN___(); - RESID_INLINE reg12 outputN__T(); - RESID_INLINE reg12 outputN_S_(); - RESID_INLINE reg12 outputN_ST(); - RESID_INLINE reg12 outputNP__(); - RESID_INLINE reg12 outputNP_T(); - RESID_INLINE reg12 outputNPS_(); - RESID_INLINE reg12 outputNPST(); - - // Sample data for combinations of waveforms. - static reg8 wave6581__ST[]; - static reg8 wave6581_P_T[]; - static reg8 wave6581_PS_[]; - static reg8 wave6581_PST[]; - - static reg8 wave8580__ST[]; - static reg8 wave8580_P_T[]; - static reg8 wave8580_PS_[]; - static reg8 wave8580_PST[]; - - reg8* wave__ST; - reg8* wave_P_T; - reg8* wave_PS_; - reg8* wave_PST; - -friend class Voice; -friend class cSID; -}; - - -// ---------------------------------------------------------------------------- -// Inline functions. -// The following functions are defined inline because they are called every -// time a sample is calculated. -// ---------------------------------------------------------------------------- - -#if RESID_INLINING || defined(__WAVE_CC__) - -// ---------------------------------------------------------------------------- -// SID clocking - 1 cycle. -// ---------------------------------------------------------------------------- -RESID_INLINE -void WaveformGenerator::clock() -{ - // No operation if test bit is set. - if (test) { - return; - } - - reg24 accumulator_prev = accumulator; - - // Calculate new accumulator value; - accumulator += freq; - accumulator &= 0xffffff; - - // Check whether the MSB is set high. This is used for synchronization. - msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000); - - // Shift noise register once for each time accumulator bit 19 is set high. - if (!(accumulator_prev & 0x080000) && (accumulator & 0x080000)) { - reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1; - shift_register <<= 1; - shift_register &= 0x7fffff; - shift_register |= bit0; - } -} - -// ---------------------------------------------------------------------------- -// SID clocking - delta_t cycles. -// ---------------------------------------------------------------------------- -RESID_INLINE -void WaveformGenerator::clock(cycle_count delta_t) -{ - // No operation if test bit is set. - if (test) { - return; - } - - reg24 accumulator_prev = accumulator; - - // Calculate new accumulator value; - reg24 delta_accumulator = delta_t*freq; - accumulator += delta_accumulator; - accumulator &= 0xffffff; - - // Check whether the MSB is set high. This is used for synchronization. - msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000); - - // Shift noise register once for each time accumulator bit 19 is set high. - // Bit 19 is set high each time 2^20 (0x100000) is added to the accumulator. - reg24 shift_period = 0x100000; - - while (delta_accumulator) { - if (delta_accumulator < shift_period) { - shift_period = delta_accumulator; - // Determine whether bit 19 is set on the last period. - // NB! Requires two's complement integer. - if (shift_period <= 0x080000) { - // Check for flip from 0 to 1. - if (((accumulator - shift_period) & 0x080000) || !(accumulator & 0x080000)) - { - break; - } - } - else { - // Check for flip from 0 (to 1 or via 1 to 0) or from 1 via 0 to 1. - if (((accumulator - shift_period) & 0x080000) && !(accumulator & 0x080000)) - { - break; - } - } - } - - // Shift the noise/random register. - // NB! The shift is actually delayed 2 cycles, this is not modeled. - reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1; - shift_register <<= 1; - shift_register &= 0x7fffff; - shift_register |= bit0; - - delta_accumulator -= shift_period; - } -} - - -// ---------------------------------------------------------------------------- -// Synchronize oscillators. -// This must be done after all the oscillators have been clock()'ed since the -// oscillators operate in parallel. -// Note that the oscillators must be clocked exactly on the cycle when the -// MSB is set high for hard sync to operate correctly. See SID::clock(). -// ---------------------------------------------------------------------------- -RESID_INLINE -void WaveformGenerator::synchronize() -{ - // A special case occurs when a sync source is synced itself on the same - // cycle as when its MSB is set high. In this case the destination will - // not be synced. This has been verified by sampling OSC3. - if (msb_rising && sync_dest->sync && !(sync && sync_source->msb_rising)) { - sync_dest->accumulator = 0; - } -} - - -// ---------------------------------------------------------------------------- -// Output functions. -// NB! The output from SID 8580 is delayed one cycle compared to SID 6581, -// this is not modeled. -// ---------------------------------------------------------------------------- - -// No waveform: -// Zero output. -// -RESID_INLINE -reg12 WaveformGenerator::output____() -{ - return 0x000; -} - -// Triangle: -// The upper 12 bits of the accumulator are used. -// The MSB is used to create the falling edge of the triangle by inverting -// the lower 11 bits. The MSB is thrown away and the lower 11 bits are -// left-shifted (half the resolution, full amplitude). -// Ring modulation substitutes the MSB with MSB EOR sync_source MSB. -// -RESID_INLINE -reg12 WaveformGenerator::output___T() -{ - reg24 msb = (ring_mod ? accumulator ^ sync_source->accumulator : accumulator) - & 0x800000; - return ((msb ? ~accumulator : accumulator) >> 11) & 0xfff; -} - -// Sawtooth: -// The output is identical to the upper 12 bits of the accumulator. -// -RESID_INLINE -reg12 WaveformGenerator::output__S_() -{ - return accumulator >> 12; -} - -// Pulse: -// The upper 12 bits of the accumulator are used. -// These bits are compared to the pulse width register by a 12 bit digital -// comparator; output is either all one or all zero bits. -// NB! The output is actually delayed one cycle after the compare. -// This is not modeled. -// -// The test bit, when set to one, holds the pulse waveform output at 0xfff -// regardless of the pulse width setting. -// -RESID_INLINE -reg12 WaveformGenerator::output_P__() -{ - return (test || (accumulator >> 12) >= pw) ? 0xfff : 0x000; -} - -// Noise: -// The noise output is taken from intermediate bits of a 23-bit shift register -// which is clocked by bit 19 of the accumulator. -// NB! The output is actually delayed 2 cycles after bit 19 is set high. -// This is not modeled. -// -// Operation: Calculate EOR result, shift register, set bit 0 = result. -// -// ----------------------->--------------------- -// | | -// ----EOR---- | -// | | | -// 2 2 2 1 1 1 1 1 1 1 1 1 1 | -// Register bits: 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 <--- -// | | | | | | | | -// OSC3 bits : 7 6 5 4 3 2 1 0 -// -// Since waveform output is 12 bits the output is left-shifted 4 times. -// -RESID_INLINE -reg12 WaveformGenerator::outputN___() -{ - return - ((shift_register & 0x400000) >> 11) | - ((shift_register & 0x100000) >> 10) | - ((shift_register & 0x010000) >> 7) | - ((shift_register & 0x002000) >> 5) | - ((shift_register & 0x000800) >> 4) | - ((shift_register & 0x000080) >> 1) | - ((shift_register & 0x000010) << 1) | - ((shift_register & 0x000004) << 2); -} - -// Combined waveforms: -// By combining waveforms, the bits of each waveform are effectively short -// circuited. A zero bit in one waveform will result in a zero output bit -// (thus the infamous claim that the waveforms are AND'ed). -// However, a zero bit in one waveform will also affect the neighboring bits -// in the output. The reason for this has not been determined. -// -// Example: -// -// 1 1 -// Bit # 1 0 9 8 7 6 5 4 3 2 1 0 -// ----------------------- -// Sawtooth 0 0 0 1 1 1 1 1 1 0 0 0 -// -// Triangle 0 0 1 1 1 1 1 1 0 0 0 0 -// -// AND 0 0 0 1 1 1 1 1 0 0 0 0 -// -// Output 0 0 0 0 1 1 1 0 0 0 0 0 -// -// -// This behavior would be quite difficult to model exactly, since the SID -// in this case does not act as a digital state machine. Tests show that minor -// (1 bit) differences can actually occur in the output from otherwise -// identical samples from OSC3 when waveforms are combined. To further -// complicate the situation the output changes slightly with time (more -// neighboring bits are successively set) when the 12-bit waveform -// registers are kept unchanged. -// -// It is probably possible to come up with a valid model for the -// behavior, however this would be far too slow for practical use since it -// would have to be based on the mutual influence of individual bits. -// -// The output is instead approximated by using the upper bits of the -// accumulator as an index to look up the combined output in a table -// containing actual combined waveform samples from OSC3. -// These samples are 8 bit, so 4 bits of waveform resolution is lost. -// All OSC3 samples are taken with FREQ=0x1000, adding a 1 to the upper 12 -// bits of the accumulator each cycle for a sample period of 4096 cycles. -// -// Sawtooth+Triangle: -// The sawtooth output is used to look up an OSC3 sample. -// -// Pulse+Triangle: -// The triangle output is right-shifted and used to look up an OSC3 sample. -// The sample is output if the pulse output is on. -// The reason for using the triangle output as the index is to handle ring -// modulation. Only the first half of the sample is used, which should be OK -// since the triangle waveform has half the resolution of the accumulator. -// -// Pulse+Sawtooth: -// The sawtooth output is used to look up an OSC3 sample. -// The sample is output if the pulse output is on. -// -// Pulse+Sawtooth+Triangle: -// The sawtooth output is used to look up an OSC3 sample. -// The sample is output if the pulse output is on. -// -RESID_INLINE -reg12 WaveformGenerator::output__ST() -{ - return wave__ST[output__S_()] << 4; -} - -RESID_INLINE -reg12 WaveformGenerator::output_P_T() -{ - return (wave_P_T[output___T() >> 1] << 4) & output_P__(); -} - -RESID_INLINE -reg12 WaveformGenerator::output_PS_() -{ - return (wave_PS_[output__S_()] << 4) & output_P__(); -} - -RESID_INLINE -reg12 WaveformGenerator::output_PST() -{ - return (wave_PST[output__S_()] << 4) & output_P__(); -} - -// Combined waveforms including noise: -// All waveform combinations including noise output zero after a few cycles. -// NB! The effects of such combinations are not fully explored. It is claimed -// that the shift register may be filled with zeroes and locked up, which -// seems to be true. -// We have not attempted to model this behavior, suffice to say that -// there is very little audible output from waveform combinations including -// noise. We hope that nobody is actually using it. -// -RESID_INLINE -reg12 WaveformGenerator::outputN__T() -{ - return 0; -} - -RESID_INLINE -reg12 WaveformGenerator::outputN_S_() -{ - return 0; -} - -RESID_INLINE -reg12 WaveformGenerator::outputN_ST() -{ - return 0; -} - -RESID_INLINE -reg12 WaveformGenerator::outputNP__() -{ - return 0; -} - -RESID_INLINE -reg12 WaveformGenerator::outputNP_T() -{ - return 0; -} - -RESID_INLINE -reg12 WaveformGenerator::outputNPS_() -{ - return 0; -} - -RESID_INLINE -reg12 WaveformGenerator::outputNPST() -{ - return 0; -} - -// ---------------------------------------------------------------------------- -// Select one of 16 possible combinations of waveforms. -// ---------------------------------------------------------------------------- -RESID_INLINE -reg12 WaveformGenerator::output() -{ - // It may seem cleaner to use an array of member functions to return - // waveform output; however a switch with inline functions is faster. - - switch (waveform) { - default: - case 0x0: - return output____(); - case 0x1: - return output___T(); - case 0x2: - return output__S_(); - case 0x3: - return output__ST(); - case 0x4: - return output_P__(); - case 0x5: - return output_P_T(); - case 0x6: - return output_PS_(); - case 0x7: - return output_PST(); - case 0x8: - return outputN___(); - case 0x9: - return outputN__T(); - case 0xa: - return outputN_S_(); - case 0xb: - return outputN_ST(); - case 0xc: - return outputNP__(); - case 0xd: - return outputNP_T(); - case 0xe: - return outputNPS_(); - case 0xf: - return outputNPST(); - } -} - -#endif // RESID_INLINING || defined(__WAVE_CC__) - -#endif // not __WAVE_H__ diff --git a/plugins/sid/wave6581_PST.cc b/plugins/sid/wave6581_PST.cc deleted file mode 100644 index 49a4ea246d9..00000000000 --- a/plugins/sid/wave6581_PST.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave6581_PST[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x378: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x578: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x670: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x678: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x698: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x718: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x728: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x730: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x738: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x740: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x748: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x750: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x758: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x760: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x768: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x770: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x778: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x780: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x788: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x790: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x798: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, -/* 0x7f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, -/* 0x7f8: */ 0x00, 0x00, 0x00, 0x78, 0x78, 0x7e, 0x7f, 0x7f, -/* 0x800: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x808: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x810: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x818: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x820: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x828: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x830: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x838: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x840: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x848: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x850: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x858: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x860: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x868: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x870: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x878: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x880: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x888: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x890: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x898: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x900: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x908: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x910: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x918: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x920: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x928: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x930: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x938: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x940: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x948: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x950: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x958: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x968: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x980: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x988: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x990: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, -/* 0xc00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, -/* 0xff0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, -/* 0xff8: */ 0x00, 0x00, 0x00, 0x78, 0x78, 0x7e, 0x7f, 0x7f, -}; diff --git a/plugins/sid/wave6581_PS_.cc b/plugins/sid/wave6581_PS_.cc deleted file mode 100644 index ffccd23e8b1..00000000000 --- a/plugins/sid/wave6581_PS_.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave6581_PS_[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1f, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x378: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, -/* 0x3f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3f, -/* 0x3f8: */ 0x00, 0x30, 0x38, 0x3f, 0x3e, 0x3f, 0x3f, 0x3f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x578: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, -/* 0x5f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5f, -/* 0x5f8: */ 0x00, 0x40, 0x40, 0x5f, 0x5c, 0x5f, 0x5f, 0x5f, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x670: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x678: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x698: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6b, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x6d, -/* 0x6e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x6e8: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x6e, -/* 0x6f0: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x60, 0x60, 0x6f, -/* 0x6f8: */ 0x00, 0x60, 0x60, 0x6f, 0x60, 0x6f, 0x6f, 0x6f, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x718: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x728: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x730: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x738: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x60, 0x73, -/* 0x740: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x748: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x750: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x758: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x60, 0x60, 0x75, -/* 0x760: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x768: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x76, -/* 0x770: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x77, -/* 0x778: */ 0x00, 0x70, 0x70, 0x77, 0x70, 0x77, 0x77, 0x77, -/* 0x780: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x788: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x790: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x798: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x79, -/* 0x7a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x7a8: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x70, 0x70, 0x7a, -/* 0x7b0: */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x70, 0x7b, -/* 0x7b8: */ 0x40, 0x70, 0x70, 0x7b, 0x78, 0x7b, 0x7b, 0x7b, -/* 0x7c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, -/* 0x7c8: */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x70, 0x7c, -/* 0x7d0: */ 0x00, 0x00, 0x00, 0x70, 0x40, 0x70, 0x70, 0x7d, -/* 0x7d8: */ 0x40, 0x70, 0x78, 0x7d, 0x78, 0x7d, 0x7d, 0x7d, -/* 0x7e0: */ 0x00, 0x40, 0x40, 0x78, 0x60, 0x78, 0x78, 0x7e, -/* 0x7e8: */ 0x60, 0x78, 0x78, 0x7e, 0x7c, 0x7e, 0x7e, 0x7e, -/* 0x7f0: */ 0x70, 0x7c, 0x7c, 0x7f, 0x7e, 0x7f, 0x7f, 0x7f, -/* 0x7f8: */ 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, -/* 0x800: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x808: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x810: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x818: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x820: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x828: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x830: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x838: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x840: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x848: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x850: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x858: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x860: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x868: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x870: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x878: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x880: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x888: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x890: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x898: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, -/* 0x900: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x908: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x910: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x918: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x920: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x928: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x930: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x938: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x940: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x948: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x950: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x958: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x968: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x980: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x988: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x990: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x9c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1f, -/* 0xa00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0xa80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xac0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, -/* 0xb00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, -/* 0xb80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, -/* 0xbf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3f, -/* 0xbf8: */ 0x00, 0x30, 0x38, 0x3f, 0x3e, 0x3f, 0x3f, 0x3f, -/* 0xc00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0xc80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, -/* 0xd00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5f, -/* 0xdf8: */ 0x00, 0x40, 0x40, 0x5f, 0x5c, 0x5f, 0x5f, 0x5f, -/* 0xe00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, -/* 0xe80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6b, -/* 0xec0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6d, -/* 0xee0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0xee8: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x6e, -/* 0xef0: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x60, 0x60, 0x6f, -/* 0xef8: */ 0x00, 0x60, 0x60, 0x6f, 0x60, 0x6f, 0x6f, 0x6f, -/* 0xf00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0xf20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0xf30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0xf38: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x60, 0x73, -/* 0xf40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0xf50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0xf58: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x60, 0x60, 0x75, -/* 0xf60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0xf68: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x76, -/* 0xf70: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x77, -/* 0xf78: */ 0x00, 0x70, 0x70, 0x77, 0x70, 0x77, 0x77, 0x77, -/* 0xf80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0xf90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0xf98: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x79, -/* 0xfa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0xfa8: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x70, 0x70, 0x7a, -/* 0xfb0: */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x70, 0x7b, -/* 0xfb8: */ 0x40, 0x70, 0x70, 0x7b, 0x78, 0x7b, 0x7b, 0x7b, -/* 0xfc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, -/* 0xfc8: */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x70, 0x7c, -/* 0xfd0: */ 0x00, 0x00, 0x00, 0x70, 0x40, 0x70, 0x70, 0x7d, -/* 0xfd8: */ 0x40, 0x70, 0x78, 0x7d, 0x78, 0x7d, 0x7d, 0x7d, -/* 0xfe0: */ 0x00, 0x40, 0x40, 0x78, 0x60, 0x78, 0x78, 0x7e, -/* 0xfe8: */ 0x60, 0x78, 0x78, 0x7e, 0x7c, 0x7e, 0x7e, 0x7e, -/* 0xff0: */ 0x70, 0x7c, 0x7c, 0x7f, 0x7c, 0x7f, 0x7f, 0x7f, -/* 0xff8: */ 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, -}; diff --git a/plugins/sid/wave6581_P_T.cc b/plugins/sid/wave6581_P_T.cc deleted file mode 100644 index a8109eeb115..00000000000 --- a/plugins/sid/wave6581_P_T.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave6581_P_T[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x38, 0x3f, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x5f, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x378: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x60, 0x6f, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x70, 0x77, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x70, 0x40, 0x70, 0x70, 0x7b, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x70, -/* 0x3e8: */ 0x00, 0x40, 0x40, 0x70, 0x60, 0x70, 0x78, 0x7d, -/* 0x3f0: */ 0x00, 0x40, 0x60, 0x78, 0x60, 0x78, 0x78, 0x7e, -/* 0x3f8: */ 0x70, 0x7c, 0x7c, 0x7f, 0x7e, 0x7f, 0x7f, 0x7f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x9f, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x578: */ 0x00, 0x80, 0x80, 0x80, 0x80, 0xa0, 0xa0, 0xaf, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xa0, -/* 0x5b8: */ 0x00, 0x80, 0x80, 0xa0, 0x80, 0xa0, 0xb0, 0xb7, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xa0, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xa0, -/* 0x5d8: */ 0x00, 0x80, 0x80, 0xa0, 0x80, 0xb0, 0xb0, 0xbb, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0xb0, -/* 0x5e8: */ 0x80, 0x80, 0x80, 0xb0, 0x80, 0xb0, 0xb8, 0xbd, -/* 0x5f0: */ 0x80, 0x80, 0x80, 0xb8, 0xa0, 0xb8, 0xb8, 0xbe, -/* 0x5f8: */ 0xa0, 0xb8, 0xbc, 0xbf, 0xbe, 0xbf, 0xbf, 0xbf, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, -/* 0x670: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, -/* 0x678: */ 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xcf, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x698: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0xc0, 0xc0, -/* 0x6b8: */ 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd0, 0xd7, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xc0, 0xc0, -/* 0x6d0: */ 0x00, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xc0, -/* 0x6d8: */ 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xd0, 0xd0, 0xdb, -/* 0x6e0: */ 0x00, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xd0, -/* 0x6e8: */ 0x80, 0xc0, 0xc0, 0xd0, 0xc0, 0xd0, 0xd8, 0xdd, -/* 0x6f0: */ 0xc0, 0xc0, 0xc0, 0xd0, 0xc0, 0xd8, 0xd8, 0xde, -/* 0x6f8: */ 0xc0, 0xd8, 0xdc, 0xdf, 0xdc, 0xdf, 0xdf, 0xdf, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x718: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xe0, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x728: */ 0x00, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xe0, -/* 0x730: */ 0x00, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xe0, -/* 0x738: */ 0x80, 0xc0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, 0xe7, -/* 0x740: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, -/* 0x748: */ 0x00, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xe0, -/* 0x750: */ 0x00, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xe0, -/* 0x758: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xeb, -/* 0x760: */ 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, -/* 0x768: */ 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xed, -/* 0x770: */ 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe8, 0xe8, 0xee, -/* 0x778: */ 0xe0, 0xe8, 0xec, 0xef, 0xec, 0xef, 0xef, 0xef, -/* 0x780: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0x788: */ 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf0, -/* 0x790: */ 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, -/* 0x798: */ 0xc0, 0xe0, 0xe0, 0xf0, 0xe0, 0xf0, 0xf0, 0xf3, -/* 0x7a0: */ 0x80, 0xc0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, 0xf0, -/* 0x7a8: */ 0xc0, 0xe0, 0xe0, 0xf0, 0xe0, 0xf0, 0xf0, 0xf5, -/* 0x7b0: */ 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf6, -/* 0x7b8: */ 0xf0, 0xf0, 0xf4, 0xf7, 0xf4, 0xf7, 0xf7, 0xf7, -/* 0x7c0: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, -/* 0x7c8: */ 0xe0, 0xe0, 0xe0, 0xf8, 0xf0, 0xf8, 0xf8, 0xf9, -/* 0x7d0: */ 0xe0, 0xf0, 0xf0, 0xf8, 0xf0, 0xf8, 0xf8, 0xfa, -/* 0x7d8: */ 0xf0, 0xf8, 0xf8, 0xfb, 0xf8, 0xfb, 0xfb, 0xfb, -/* 0x7e0: */ 0xe0, 0xf0, 0xf0, 0xf8, 0xf0, 0xf8, 0xfc, 0xfc, -/* 0x7e8: */ 0xf8, 0xfc, 0xfc, 0xfd, 0xfc, 0xfd, 0xfd, 0xfd, -/* 0x7f0: */ 0xf8, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, -/* 0x7f8: */ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -/* 0x800: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, -/* 0x808: */ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfc, 0xf8, -/* 0x810: */ 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, 0xfc, 0xfc, 0xf8, -/* 0x818: */ 0xfc, 0xfc, 0xfc, 0xf0, 0xf8, 0xf0, 0xf0, 0xe0, -/* 0x820: */ 0xfb, 0xfb, 0xfb, 0xf8, 0xfb, 0xf8, 0xf8, 0xf0, -/* 0x828: */ 0xfa, 0xf8, 0xf8, 0xf0, 0xf8, 0xf0, 0xf0, 0xe0, -/* 0x830: */ 0xf9, 0xf8, 0xf8, 0xf0, 0xf8, 0xf0, 0xe0, 0xe0, -/* 0x838: */ 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, -/* 0x840: */ 0xf7, 0xf7, 0xf7, 0xf4, 0xf7, 0xf4, 0xf0, 0xf0, -/* 0x848: */ 0xf6, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, -/* 0x850: */ 0xf5, 0xf0, 0xf0, 0xe0, 0xf0, 0xe0, 0xe0, 0xc0, -/* 0x858: */ 0xf0, 0xe0, 0xe0, 0xc0, 0xe0, 0xc0, 0xc0, 0x80, -/* 0x860: */ 0xf3, 0xf0, 0xf0, 0xe0, 0xf0, 0xe0, 0xe0, 0xc0, -/* 0x868: */ 0xf0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, -/* 0x870: */ 0xf0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, -/* 0x878: */ 0xc0, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0x880: */ 0xef, 0xef, 0xef, 0xec, 0xef, 0xec, 0xe8, 0xe0, -/* 0x888: */ 0xee, 0xe8, 0xe8, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, -/* 0x890: */ 0xed, 0xe8, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, -/* 0x898: */ 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, -/* 0x8a0: */ 0xeb, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, -/* 0x8a8: */ 0xe0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x00, -/* 0x8b0: */ 0xe0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x00, -/* 0x8b8: */ 0xc0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0x8c0: */ 0xe7, 0xe0, 0xe0, 0xc0, 0xe0, 0xc0, 0xc0, 0x80, -/* 0x8c8: */ 0xe0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x00, -/* 0x8d0: */ 0xe0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x00, -/* 0x8d8: */ 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0xe0, 0xc0, 0xc0, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x900: */ 0xdf, 0xdf, 0xdf, 0xdc, 0xdf, 0xdc, 0xd8, 0xc0, -/* 0x908: */ 0xde, 0xd8, 0xd8, 0xc0, 0xd8, 0xc0, 0xc0, 0xc0, -/* 0x910: */ 0xdd, 0xd8, 0xd0, 0xc0, 0xd0, 0xc0, 0xc0, 0x80, -/* 0x918: */ 0xd0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x00, -/* 0x920: */ 0xdb, 0xd0, 0xd0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, -/* 0x928: */ 0xc0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x00, -/* 0x930: */ 0xc0, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0x938: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x940: */ 0xd7, 0xd0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, -/* 0x948: */ 0xc0, 0xc0, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0x950: */ 0xc0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0x958: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0xc0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0x968: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x980: */ 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x00, -/* 0x988: */ 0xc0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0x990: */ 0xc0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0xc0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c0: */ 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa00: */ 0xbf, 0xbf, 0xbf, 0xbe, 0xbf, 0xbc, 0xbc, 0xa0, -/* 0xa08: */ 0xbe, 0xbc, 0xb8, 0xa0, 0xb8, 0xa0, 0x80, 0x80, -/* 0xa10: */ 0xbd, 0xb8, 0xb0, 0x80, 0xb0, 0x80, 0x80, 0x80, -/* 0xa18: */ 0xb0, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0xa20: */ 0xbb, 0xb0, 0xb0, 0x80, 0xa0, 0x80, 0x80, 0x00, -/* 0xa28: */ 0xa0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0xa30: */ 0xa0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa40: */ 0xb7, 0xb0, 0xa0, 0x80, 0xa0, 0x80, 0x80, 0x00, -/* 0xa48: */ 0xa0, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa80: */ 0xaf, 0xa0, 0xa0, 0x80, 0x80, 0x80, 0x80, 0x00, -/* 0xa88: */ 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa0: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac0: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb00: */ 0x9f, 0x90, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb10: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb40: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb80: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc00: */ 0x7f, 0x7f, 0x7f, 0x7e, 0x7f, 0x7c, 0x7c, 0x70, -/* 0xc08: */ 0x7e, 0x7c, 0x78, 0x60, 0x78, 0x60, 0x60, 0x00, -/* 0xc10: */ 0x7d, 0x78, 0x78, 0x60, 0x70, 0x40, 0x40, 0x00, -/* 0xc18: */ 0x70, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc20: */ 0x7b, 0x78, 0x70, 0x40, 0x70, 0x40, 0x00, 0x00, -/* 0xc28: */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc30: */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x77, 0x70, 0x70, 0x00, 0x60, 0x00, 0x00, 0x00, -/* 0xc48: */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc80: */ 0x6f, 0x60, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd00: */ 0x5f, 0x58, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe00: */ 0x3f, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xff0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xff8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; diff --git a/plugins/sid/wave6581__ST.cc b/plugins/sid/wave6581__ST.cc deleted file mode 100644 index add4d387ad4..00000000000 --- a/plugins/sid/wave6581__ST.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave6581__ST[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x378: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3f0: */ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, -/* 0x3f8: */ 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x3f, 0x3f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x578: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x1f, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x670: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x678: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x698: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x718: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x728: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x730: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x738: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x740: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x748: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x750: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x758: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x760: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x768: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x770: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x778: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x780: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x788: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x790: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x798: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x7c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7e0: */ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, -/* 0x7e8: */ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, -/* 0x7f0: */ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, -/* 0x7f8: */ 0x3e, 0x3e, 0x3f, 0x3f, 0x7f, 0x7f, 0x7f, 0x7f, -/* 0x800: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x808: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x810: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x818: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x820: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x828: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x830: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x838: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x840: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x848: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x850: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x858: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x860: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x868: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x870: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x878: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x880: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x888: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x890: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x898: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x8c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x900: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x908: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x910: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x918: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x920: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x928: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x930: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x938: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x940: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x948: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x950: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x958: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x968: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x980: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x988: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x990: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x9c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, -/* 0xa00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xa80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xac0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0xb00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xb40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xb80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf0: */ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, -/* 0xbf8: */ 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x3f, 0x3f, -/* 0xc00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xc80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0xd00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x1f, -/* 0xe00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xe80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xec0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0xf00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xf40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xf80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xfc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe0: */ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, -/* 0xfe8: */ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, -/* 0xff0: */ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, -/* 0xff8: */ 0x3e, 0x3e, 0x3f, 0x3f, 0x7f, 0x7f, 0x7f, 0x7f, -}; diff --git a/plugins/sid/wave8580_PST.cc b/plugins/sid/wave8580_PST.cc deleted file mode 100644 index a3958c1929f..00000000000 --- a/plugins/sid/wave8580_PST.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave8580_PST[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x378: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x578: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x670: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x678: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x698: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x718: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x728: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x730: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x738: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x740: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x748: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x750: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x758: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x760: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x768: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x770: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x778: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x780: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x788: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x790: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x798: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x70, -/* 0x7f0: */ 0x60, 0x20, 0x70, 0x70, 0x70, 0x70, 0x70, 0x78, -/* 0x7f8: */ 0x78, 0x78, 0x7c, 0x7c, 0x7e, 0x7e, 0x7f, 0x7f, -/* 0x800: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x808: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x810: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x818: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x820: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x828: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x830: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x838: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x840: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x848: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x850: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x858: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x860: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x868: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x870: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x878: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x880: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x888: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x890: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x898: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x900: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x908: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x910: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x918: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x920: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x928: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x930: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x938: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x940: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x948: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x950: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x958: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x968: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x980: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x988: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x990: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1e, 0x3f, -/* 0xc00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xdf8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8c, 0x9f, -/* 0xe00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe70: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe78: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe80: */ 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, -/* 0xe88: */ 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe90: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe98: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xea0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xea8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xeb0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xeb8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xec0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xec8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xed0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xed8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xee0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xee8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0xc0, -/* 0xef0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xef8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xcf, -/* 0xf00: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf08: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf10: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf18: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf20: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf28: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf30: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf38: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf40: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf48: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf50: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf58: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf60: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf68: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, -/* 0xf70: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf78: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe3, -/* 0xf80: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf88: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf90: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf98: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xfa0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xfa8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xfb0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, -/* 0xfb8: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfc0: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfc8: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfd0: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfd8: */ 0xf0, 0xf0, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfe0: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfe8: */ 0xf8, 0xf8, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, -/* 0xff0: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, -/* 0xff8: */ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; diff --git a/plugins/sid/wave8580_PS_.cc b/plugins/sid/wave8580_PS_.cc deleted file mode 100644 index 795044d6064..00000000000 --- a/plugins/sid/wave8580_PS_.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave8580_PS_[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x1f, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x378: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, -/* 0x3f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, -/* 0x3f8: */ 0x00, 0x0c, 0x1c, 0x3f, 0x1e, 0x3f, 0x3f, 0x3f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x578: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, -/* 0x5f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, -/* 0x5f8: */ 0x00, 0x00, 0x00, 0x5f, 0x0c, 0x5f, 0x5f, 0x5f, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x670: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x678: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x698: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, -/* 0x6e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, -/* 0x6f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, -/* 0x6f8: */ 0x00, 0x40, 0x40, 0x6f, 0x40, 0x6f, 0x6f, 0x6f, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x718: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x728: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x730: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x738: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, -/* 0x740: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x748: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x750: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x758: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x61, -/* 0x760: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x768: */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x70, -/* 0x770: */ 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70, -/* 0x778: */ 0x40, 0x60, 0x60, 0x77, 0x60, 0x77, 0x77, 0x77, -/* 0x780: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x788: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x790: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, -/* 0x798: */ 0x00, 0x40, 0x40, 0x60, 0x40, 0x60, 0x60, 0x79, -/* 0x7a0: */ 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x60, -/* 0x7a8: */ 0x40, 0x40, 0x40, 0x60, 0x60, 0x60, 0x60, 0x78, -/* 0x7b0: */ 0x40, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, -/* 0x7b8: */ 0x60, 0x70, 0x70, 0x78, 0x70, 0x79, 0x7b, 0x7b, -/* 0x7c0: */ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x70, -/* 0x7c8: */ 0x60, 0x60, 0x60, 0x70, 0x60, 0x70, 0x70, 0x7c, -/* 0x7d0: */ 0x60, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x7c, -/* 0x7d8: */ 0x70, 0x78, 0x78, 0x7c, 0x78, 0x7c, 0x7c, 0x7d, -/* 0x7e0: */ 0x70, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x7c, -/* 0x7e8: */ 0x78, 0x7c, 0x7c, 0x7e, 0x7c, 0x7e, 0x7e, 0x7e, -/* 0x7f0: */ 0x7c, 0x7c, 0x7c, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, -/* 0x7f8: */ 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, -/* 0x800: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x808: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x810: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x818: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x820: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x828: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x830: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x838: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x840: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x848: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x850: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x858: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x860: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x868: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x870: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x878: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -/* 0x880: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x888: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x890: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x898: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x8c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, -/* 0x900: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x908: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x910: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x918: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x920: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x928: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x930: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x938: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x940: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x948: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x950: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x958: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x968: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, -/* 0x980: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x988: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x990: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, -/* 0x9c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8d, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x8e, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x8f, -/* 0x9f8: */ 0x80, 0x80, 0x80, 0x9f, 0x80, 0x9f, 0x9f, 0x9f, -/* 0xa00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xa40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x87, -/* 0xa80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xaa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x83, -/* 0xac0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, -/* 0xad8: */ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, -/* 0xae0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xae8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x84, -/* 0xaf0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x87, -/* 0xaf8: */ 0x80, 0x80, 0x80, 0x87, 0x80, 0x8f, 0xaf, 0xaf, -/* 0xb00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xb10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb20: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, -/* 0xb28: */ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb30: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb38: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x83, -/* 0xb40: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb48: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb50: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb58: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, -/* 0xb60: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb68: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa0, -/* 0xb70: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa0, -/* 0xb78: */ 0x80, 0x80, 0x80, 0xa0, 0x80, 0xa3, 0xb7, 0xb7, -/* 0xb80: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb88: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb90: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb98: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb1, -/* 0xba0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xba8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb0, -/* 0xbb0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb0, -/* 0xbb8: */ 0x80, 0xa0, 0xa0, 0xb0, 0xa0, 0xb8, 0xb9, 0xbb, -/* 0xbc0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa0, -/* 0xbc8: */ 0x80, 0x80, 0x80, 0xa0, 0x80, 0xa0, 0xa0, 0xb8, -/* 0xbd0: */ 0x80, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xb8, -/* 0xbd8: */ 0xa0, 0xb0, 0xb0, 0xb8, 0xb0, 0xbc, 0xbc, 0xbd, -/* 0xbe0: */ 0xa0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb8, 0xb8, 0xbc, -/* 0xbe8: */ 0xb0, 0xb8, 0xb8, 0xbc, 0xb8, 0xbc, 0xbe, 0xbe, -/* 0xbf0: */ 0xb8, 0xbc, 0xbc, 0xbe, 0xbc, 0xbe, 0xbe, 0xbf, -/* 0xbf8: */ 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, -/* 0xc00: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, -/* 0xc08: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, -/* 0xc10: */ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc18: */ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc20: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc28: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc30: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc38: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, -/* 0xc40: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc48: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc50: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc58: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc60: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc68: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc70: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc78: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc7, -/* 0xc80: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc88: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc90: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xc98: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xca0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xca8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xcb0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xcb8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0xc3, -/* 0xcc0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xcc8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0xcd0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0xcd8: */ 0x80, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xc1, -/* 0xce0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0xce8: */ 0x80, 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xcf0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc7, -/* 0xcf8: */ 0xc0, 0xc0, 0xc0, 0xc7, 0xc0, 0xcf, 0xcf, 0xcf, -/* 0xd00: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xd08: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xd10: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xd18: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0xd20: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xd28: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0xd30: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0xc0, -/* 0xd38: */ 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc3, -/* 0xd40: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0xd48: */ 0x80, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xc0, -/* 0xd50: */ 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd58: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, -/* 0xd60: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd68: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd70: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd78: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc7, 0xd7, -/* 0xd80: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd88: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd90: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xd98: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xda0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xda8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd0, -/* 0xdb0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd0, -/* 0xdb8: */ 0xc0, 0xc0, 0xc0, 0xd0, 0xc0, 0xd0, 0xd8, 0xdb, -/* 0xdc0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xdc8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd8, -/* 0xdd0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd8, -/* 0xdd8: */ 0xc0, 0xc0, 0xc0, 0xd8, 0xd0, 0xd8, 0xd8, 0xdd, -/* 0xde0: */ 0xc0, 0xc0, 0xc0, 0xd0, 0xc0, 0xd0, 0xd0, 0xdc, -/* 0xde8: */ 0xd0, 0xd8, 0xd8, 0xdc, 0xd8, 0xdc, 0xdc, 0xde, -/* 0xdf0: */ 0xd8, 0xdc, 0xdc, 0xde, 0xdc, 0xde, 0xde, 0xdf, -/* 0xdf8: */ 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, -/* 0xe00: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe08: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe10: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe18: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe20: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe28: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe30: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe38: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe3, -/* 0xe40: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe48: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xe50: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, -/* 0xe58: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, 0xe1, -/* 0xe60: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, -/* 0xe68: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xe70: */ 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xe78: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe3, 0xe7, -/* 0xe80: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, -/* 0xe88: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, 0xe0, -/* 0xe90: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, 0xe0, -/* 0xe98: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xea0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xea8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xeb0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xeb8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xeb, -/* 0xec0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xec8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xed0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xed8: */ 0xe0, 0xe0, 0xe0, 0xe8, 0xe0, 0xe8, 0xe8, 0xed, -/* 0xee0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xec, -/* 0xee8: */ 0xe0, 0xe0, 0xe0, 0xec, 0xe8, 0xec, 0xec, 0xee, -/* 0xef0: */ 0xe8, 0xe8, 0xe8, 0xec, 0xec, 0xee, 0xee, 0xef, -/* 0xef8: */ 0xec, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, -/* 0xf00: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf08: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf10: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf18: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, -/* 0xf20: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, -/* 0xf28: */ 0xe0, 0xe0, 0xe0, 0xf0, 0xe0, 0xf0, 0xf0, 0xf0, -/* 0xf30: */ 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf38: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, -/* 0xf40: */ 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf48: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf50: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf58: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf5, -/* 0xf60: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf68: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, 0xf4, 0xf6, -/* 0xf70: */ 0xf0, 0xf0, 0xf0, 0xf4, 0xf0, 0xf4, 0xf6, 0xf7, -/* 0xf78: */ 0xf4, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, -/* 0xf80: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, -/* 0xf88: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xf8, 0xf8, -/* 0xf90: */ 0xf0, 0xf0, 0xf0, 0xf8, 0xf0, 0xf8, 0xf8, 0xf8, -/* 0xf98: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, -/* 0xfa0: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfa8: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xfa, -/* 0xfb0: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, -/* 0xfb8: */ 0xf8, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, -/* 0xfc0: */ 0xf8, 0xf8, 0xf8, 0xfc, 0xf8, 0xfc, 0xfc, 0xfc, -/* 0xfc8: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, -/* 0xfd0: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, -/* 0xfd8: */ 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, -/* 0xfe0: */ 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, -/* 0xfe8: */ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, -/* 0xff0: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -/* 0xff8: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; diff --git a/plugins/sid/wave8580_P_T.cc b/plugins/sid/wave8580_P_T.cc deleted file mode 100644 index a2fff3a231d..00000000000 --- a/plugins/sid/wave8580_P_T.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave8580_P_T[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x00, 0x00, 0x00, 0x1c, 0x00, 0x3c, 0x3f, 0x3f, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x5e, 0x5f, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x378: */ 0x00, 0x00, 0x00, 0x40, 0x40, 0x60, 0x60, 0x6f, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x60, -/* 0x3b8: */ 0x40, 0x40, 0x60, 0x60, 0x60, 0x60, 0x70, 0x77, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, -/* 0x3c8: */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x60, 0x60, 0x60, -/* 0x3d0: */ 0x40, 0x40, 0x40, 0x60, 0x60, 0x60, 0x60, 0x70, -/* 0x3d8: */ 0x60, 0x60, 0x60, 0x70, 0x70, 0x70, 0x78, 0x7b, -/* 0x3e0: */ 0x60, 0x60, 0x60, 0x70, 0x60, 0x70, 0x70, 0x70, -/* 0x3e8: */ 0x70, 0x70, 0x70, 0x78, 0x78, 0x78, 0x78, 0x7c, -/* 0x3f0: */ 0x78, 0x78, 0x78, 0x7c, 0x78, 0x7c, 0x7c, 0x7e, -/* 0x3f8: */ 0x7c, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x4d8: */ 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x4e8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x4f0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x4f8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8e, 0x9f, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, -/* 0x530: */ 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x538: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x540: */ 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, -/* 0x548: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x550: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x558: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x560: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x568: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x570: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x578: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xaf, -/* 0x580: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x588: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x590: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x598: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x5a0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x5a8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x5b0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x5b8: */ 0x80, 0x80, 0x80, 0xa0, 0xa0, 0xa0, 0xa0, 0xb7, -/* 0x5c0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x5c8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa0, -/* 0x5d0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa0, 0xa0, -/* 0x5d8: */ 0xa0, 0xa0, 0xa0, 0xb0, 0xa0, 0xb0, 0xb0, 0xbb, -/* 0x5e0: */ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xb0, 0xb0, -/* 0x5e8: */ 0xa0, 0xb0, 0xb0, 0xb8, 0xb0, 0xb8, 0xb8, 0xbc, -/* 0x5f0: */ 0xb0, 0xb8, 0xb8, 0xb8, 0xb8, 0xbc, 0xbc, 0xbe, -/* 0x5f8: */ 0xbc, 0xbc, 0xbe, 0xbf, 0xbe, 0xbf, 0xbf, 0xbf, -/* 0x600: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x608: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x610: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x618: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x620: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x628: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x630: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x638: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0xc0, -/* 0x640: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x648: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x650: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, -/* 0x658: */ 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x660: */ 0x80, 0x80, 0x80, 0xc0, 0x80, 0xc0, 0xc0, 0xc0, -/* 0x668: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x670: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x678: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xcf, -/* 0x680: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0xc0, -/* 0x688: */ 0xc0, 0x80, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x690: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x698: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6a0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6a8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6b0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6b8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd7, -/* 0x6c0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6c8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6d0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x6d8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd0, 0xd0, 0xd9, -/* 0x6e0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xd0, -/* 0x6e8: */ 0xc0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd8, 0xd8, 0xdc, -/* 0x6f0: */ 0xd0, 0xd0, 0xd8, 0xd8, 0xd8, 0xdc, 0xdc, 0xde, -/* 0x6f8: */ 0xdc, 0xdc, 0xde, 0xdf, 0xde, 0xdf, 0xdf, 0xdf, -/* 0x700: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x708: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x710: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x718: */ 0xc0, 0xc0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, 0xe0, -/* 0x720: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, -/* 0x728: */ 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x730: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x738: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe7, -/* 0x740: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x748: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x750: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x758: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe8, -/* 0x760: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x768: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe8, 0xec, -/* 0x770: */ 0xe0, 0xe0, 0xe0, 0xe8, 0xe8, 0xe8, 0xec, 0xee, -/* 0x778: */ 0xec, 0xec, 0xec, 0xee, 0xee, 0xef, 0xef, 0xef, -/* 0x780: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x788: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, -/* 0x790: */ 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x798: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x7a0: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x7a8: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, -/* 0x7b0: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, -/* 0x7b8: */ 0xf0, 0xf4, 0xf4, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, -/* 0x7c0: */ 0xf0, 0xf0, 0xf0, 0xf8, 0xf0, 0xf8, 0xf8, 0xf8, -/* 0x7c8: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0x7d0: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0x7d8: */ 0xf8, 0xf8, 0xf8, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, -/* 0x7e0: */ 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, -/* 0x7e8: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, -/* 0x7f0: */ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, -/* 0x7f8: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -/* 0x800: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -/* 0x808: */ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfc, -/* 0x810: */ 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, -/* 0x818: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, -/* 0x820: */ 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xf8, 0xf8, 0xf8, -/* 0x828: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0x830: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0x838: */ 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x840: */ 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf4, 0xf4, 0xf0, -/* 0x848: */ 0xf4, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x850: */ 0xf4, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x858: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x860: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0x868: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, -/* 0x870: */ 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x878: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x880: */ 0xef, 0xef, 0xef, 0xee, 0xee, 0xec, 0xec, 0xe8, -/* 0x888: */ 0xee, 0xec, 0xe8, 0xe8, 0xe8, 0xe0, 0xe0, 0xe0, -/* 0x890: */ 0xec, 0xe8, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x898: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8a0: */ 0xe8, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8a8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8b0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8b8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8c0: */ 0xe7, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8c8: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0x8d0: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, -/* 0x8d8: */ 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x8e0: */ 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x8e8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x8f0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x8f8: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x900: */ 0xdf, 0xdf, 0xdf, 0xde, 0xdf, 0xde, 0xdc, 0xdc, -/* 0x908: */ 0xde, 0xdc, 0xdc, 0xd8, 0xd8, 0xd8, 0xd0, 0xd0, -/* 0x910: */ 0xdc, 0xd8, 0xd8, 0xd0, 0xd0, 0xd0, 0xd0, 0xc0, -/* 0x918: */ 0xd0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x920: */ 0xd9, 0xd0, 0xd0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x928: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x930: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x938: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x940: */ 0xd7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x948: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x950: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x958: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x960: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x968: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x970: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, -/* 0x978: */ 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x980: */ 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x988: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x990: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0x998: */ 0xc0, 0xc0, 0xc0, 0x80, 0xc0, 0x80, 0x80, 0x80, -/* 0x9a0: */ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, -/* 0x9a8: */ 0xc0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9b0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9b8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9c0: */ 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9c8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9d0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9d8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9e0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9e8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9f0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0x9f8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa00: */ 0xbf, 0xbf, 0xbf, 0xbe, 0xbf, 0xbe, 0xbc, 0xbc, -/* 0xa08: */ 0xbe, 0xbc, 0xbc, 0xb8, 0xb8, 0xb8, 0xb8, 0xb0, -/* 0xa10: */ 0xbc, 0xb8, 0xb8, 0xb0, 0xb8, 0xb0, 0xb0, 0xb0, -/* 0xa18: */ 0xb0, 0xb0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, -/* 0xa20: */ 0xbb, 0xb0, 0xb0, 0xa0, 0xb0, 0xa0, 0xa0, 0xa0, -/* 0xa28: */ 0xa0, 0xa0, 0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa30: */ 0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa38: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa40: */ 0xb7, 0xb0, 0xa0, 0xa0, 0xa0, 0x80, 0x80, 0x80, -/* 0xa48: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa50: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa58: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa60: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa68: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa70: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa78: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa80: */ 0xaf, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa88: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa90: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xa98: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xaa0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xaa8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xab0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xab8: */ 0x80, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0xac0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xac8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, -/* 0xad0: */ 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb00: */ 0x9f, 0x9e, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb08: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb10: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xb18: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb40: */ 0x80, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb80: */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc00: */ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7e, 0x7c, -/* 0xc08: */ 0x7e, 0x7c, 0x7c, 0x78, 0x7c, 0x78, 0x78, 0x78, -/* 0xc10: */ 0x7c, 0x78, 0x78, 0x78, 0x78, 0x70, 0x70, 0x70, -/* 0xc18: */ 0x78, 0x70, 0x70, 0x60, 0x70, 0x60, 0x60, 0x60, -/* 0xc20: */ 0x7b, 0x78, 0x70, 0x70, 0x70, 0x60, 0x60, 0x60, -/* 0xc28: */ 0x70, 0x60, 0x60, 0x60, 0x60, 0x40, 0x40, 0x40, -/* 0xc30: */ 0x60, 0x60, 0x60, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 0xc38: */ 0x40, 0x40, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x77, 0x70, 0x60, 0x60, 0x60, 0x60, 0x40, 0x40, -/* 0xc48: */ 0x60, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc80: */ 0x6f, 0x64, 0x60, 0x40, 0x40, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd00: */ 0x5f, 0x5e, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe00: */ 0x3f, 0x3f, 0x3e, 0x00, 0x1c, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xea8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xeb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xec8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xed8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xee8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xef8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf00: */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xf98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xfe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xff0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xff8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; diff --git a/plugins/sid/wave8580__ST.cc b/plugins/sid/wave8580__ST.cc deleted file mode 100644 index e50a21d8173..00000000000 --- a/plugins/sid/wave8580__ST.cc +++ /dev/null @@ -1,536 +0,0 @@ -// --------------------------------------------------------------------------- -// This file is part of reSID, a MOS6581 SID emulator engine. -// Copyright (C) 2004 Dag Lem -// -// 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; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// --------------------------------------------------------------------------- - -#include "wave.h" - -reg8 WaveformGenerator::wave8580__ST[] = -{ -/* 0x000: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x008: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x010: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x018: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x020: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x080: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x088: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x090: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x098: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x0f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x100: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x108: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x110: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x118: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x120: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x128: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x130: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x138: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x140: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x148: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x150: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x158: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x160: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x168: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x170: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x178: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x180: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x188: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x190: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x198: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x1f8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, -/* 0x200: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x208: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x210: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x218: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x220: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x228: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x230: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x238: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x240: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x248: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x250: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x258: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x260: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x268: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x270: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x278: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x280: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x288: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x290: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x298: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x2f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x300: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x308: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x310: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x318: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x320: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x328: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x330: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x338: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x340: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x348: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x350: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x358: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x360: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x368: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x370: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x378: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x380: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x388: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x390: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x398: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x3c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x3f0: */ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, -/* 0x3f8: */ 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, -/* 0x400: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x408: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x410: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x418: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x420: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x428: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x430: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x438: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x440: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x448: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x450: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x458: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x460: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x468: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x470: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x478: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x480: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x488: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x490: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x498: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x4f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x500: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x508: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x510: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x518: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x520: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x528: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x530: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x538: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x540: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x548: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x550: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x558: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x560: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x568: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x570: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x578: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x580: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x588: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x590: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x598: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x5f8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x1f, -/* 0x600: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x608: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x610: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x618: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x620: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x628: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x630: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x638: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x640: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x648: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x650: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x658: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x660: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x668: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x670: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x678: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x680: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x688: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x690: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x698: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x6f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x700: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x708: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x710: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x718: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x720: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x728: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x730: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x738: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x740: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x748: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x750: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x758: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x760: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x768: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x770: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x778: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x780: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x788: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x790: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x798: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0x7c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x7e0: */ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, -/* 0x7e8: */ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, -/* 0x7f0: */ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3e, -/* 0x7f8: */ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, -/* 0x800: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x808: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x810: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x818: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x820: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x828: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x830: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x838: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x840: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x848: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x850: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x858: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x860: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x868: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x870: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x878: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x880: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x888: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x890: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x898: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x8f8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0x900: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x908: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x910: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x918: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x920: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x928: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x930: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x938: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x940: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x948: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x950: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x958: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x960: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x968: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x970: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x978: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0x980: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x988: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x990: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x998: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9a8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9b8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9c8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9d8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9e8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0x9f8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, -/* 0xa00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xa80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xa98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaa8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xab8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xac8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xad8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xae8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xaf8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0xb00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xb80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xb98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xba8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xbc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbe8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xbf0: */ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, -/* 0xbf8: */ 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x3f, -/* 0xc00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xc80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xc98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xca8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xce8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xcf8: */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, -/* 0xd00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd78: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, -/* 0xd80: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd88: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd90: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xd98: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xda8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdb8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -/* 0xdc0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdc8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdd8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xde8: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf0: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xdf8: */ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x1f, 0x1f, -/* 0xe00: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe08: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe10: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe18: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe28: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe30: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe38: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe40: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe48: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe50: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe58: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe60: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe68: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe70: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 0xe78: */ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x83, 0x83, -/* 0xe80: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe88: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe90: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xe98: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xea0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xea8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xeb0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xeb8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xec0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xec8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xed0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xed8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xee0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xee8: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xef0: */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* 0xef8: */ 0x80, 0x80, 0x80, 0x80, 0x87, 0x87, 0x87, 0x8f, -/* 0xf00: */ 0xc0, 0xe0, 0xe0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, -/* 0xf08: */ 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf10: */ 0xc0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf18: */ 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, -/* 0xf20: */ 0xc0, 0xe0, 0xe0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, -/* 0xf28: */ 0xe0, 0xe0, 0xe0, 0xc0, 0xe0, 0xc0, 0xe0, 0xe0, -/* 0xf30: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf38: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf40: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf48: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf50: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf58: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf60: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf68: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf70: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, -/* 0xf78: */ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe3, 0xe3, -/* 0xf80: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf88: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf90: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xf98: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfa0: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfa8: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfb0: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* 0xfb8: */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, -/* 0xfc0: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfc8: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfd0: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfd8: */ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, -/* 0xfe0: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, -/* 0xfe8: */ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, -/* 0xff0: */ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, -/* 0xff8: */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; From 69a35b58d39478a61dc431066e1b31bae79fffec Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Fri, 30 Oct 2020 19:51:04 +0100 Subject: [PATCH 125/180] Fix more background colors of selected text (#5687) --- data/themes/classic/style.css | 9 +++++++-- data/themes/default/style.css | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 65b496617f1..36f28a51af4 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -34,8 +34,6 @@ QLineEdit { border-radius: 4px; border: 2px inset rgba(91,101,113,128); background: #49515b; - color: #e0e0e0; - selection-background-color: #202020; } @@ -50,6 +48,13 @@ QLineEdit:focus { border: 1px solid rgba(0,0,0, 128); } +/* Set color and selection background color for various inputs. + SpinBoxes are used in QInputDialogs */ + +QTextEdit, QLineEdit:focus, QComboBox:focus, QSpinBox:focus, QDoubleSpinBox:focus { + color: #e0e0e0; + selection-background-color: #202020; +} QToolTip { border-radius: 4px; diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 832da176f28..03de62023d8 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -61,8 +61,6 @@ QLineEdit { border-radius: 4px; border: 1px; background: #101213; - color: #d1d8e4; - selection-background-color: #17793b; } QLineEdit:read-only { @@ -76,6 +74,13 @@ QLineEdit:focus { border: 1px solid #0bd556; } +/* Set color and selection background color for various inputs. + SpinBoxes are used in QInputDialogs */ + +QTextEdit, QLineEdit:focus, QComboBox:focus, QSpinBox:focus, QDoubleSpinBox:focus { + color: #d1d8e4; + selection-background-color: #17793b; +} QToolTip { border-radius: 4px; From 2d51eaef3d832c6ed1187c93558e11d5b22a51ab Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Fri, 30 Oct 2020 21:52:32 +0100 Subject: [PATCH 126/180] Theming of disabled knobs (#5549) Co-authored-by: Hyunjin Song Co-authored-by: Dominic Clark --- data/themes/classic/style.css | 5 ++ data/themes/default/style.css | 5 ++ include/Knob.h | 26 ++++---- src/gui/widgets/Knob.cpp | 110 ++++++++++++++++------------------ 4 files changed, 79 insertions(+), 67 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 36f28a51af4..5935b7749fc 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -11,6 +11,11 @@ QMdiArea { background-image: url(resources:background_artwork.png); } +Knob { + qproperty-lineInactiveColor: rgb(120, 120, 120); + qproperty-arcInactiveColor: rgba(120, 120, 120, 70); +} + AutomationEditor { background-color: rgb(0, 0, 0); color: #e0e0e0; diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 03de62023d8..39d32fcf6cf 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -40,6 +40,11 @@ QMdiArea { background-color: #111314; } +Knob { + qproperty-lineInactiveColor: rgb(120, 120, 120); + qproperty-arcInactiveColor: rgba(120, 120, 120, 70); +} + AutomationEditor { color: #ffffff; background-color: #141616; diff --git a/include/Knob.h b/include/Knob.h index b129bc8d690..3c730e3dd60 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -26,6 +26,8 @@ #ifndef KNOB_H #define KNOB_H +#include +#include #include #include @@ -41,6 +43,7 @@ enum knobTypes } ; +void convertPixmapToGrayScale(QPixmap &pixMap); class LMMS_EXPORT Knob : public QWidget, public FloatModelView { @@ -58,8 +61,12 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView // Unfortunately, the gradient syntax doesn't create our gradient // correctly so we need to do this: Q_PROPERTY(QColor outerColor READ outerColor WRITE setOuterColor) - Q_PROPERTY(QColor lineColor READ lineColor WRITE setlineColor) - Q_PROPERTY(QColor arcColor READ arcColor WRITE setarcColor) + + Q_PROPERTY(QColor lineActiveColor MEMBER m_lineActiveColor) + Q_PROPERTY(QColor lineInactiveColor MEMBER m_lineInactiveColor) + Q_PROPERTY(QColor arcActiveColor MEMBER m_arcActiveColor) + Q_PROPERTY(QColor arcInactiveColor MEMBER m_arcInactiveColor) + mapPropertyFromModel(bool,isVolumeKnob,setVolumeKnob,m_volumeKnob); mapPropertyFromModel(float,volumeRatio,setVolumeRatio,m_volumeRatio); @@ -74,7 +81,6 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView Knob( knobTypes _knob_num, QWidget * _parent = NULL, const QString & _name = QString() ); Knob( QWidget * _parent = NULL, const QString & _name = QString() ); //!< default ctor Knob( const Knob& other ) = delete; - virtual ~Knob(); // TODO: remove inline void setHintText( const QString & _txt_before, @@ -108,10 +114,6 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView QColor outerColor() const; void setOuterColor( const QColor & c ); - QColor lineColor() const; - void setlineColor( const QColor & c ); - QColor arcColor() const; - void setarcColor( const QColor & c ); QColor textColor() const; void setTextColor( const QColor & c ); @@ -134,6 +136,7 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView void mouseDoubleClickEvent( QMouseEvent * _me ) override; void paintEvent( QPaintEvent * _me ) override; void wheelEvent( QWheelEvent * _me ) override; + void changeEvent(QEvent * ev) override; virtual float getValue( const QPoint & _p ); @@ -169,7 +172,7 @@ private slots: QString m_label; - QPixmap * m_knobPixmap; + std::unique_ptr m_knobPixmap; BoolModel m_volumeKnob; FloatModel m_volumeRatio; @@ -187,8 +190,11 @@ private slots: float m_outerRadius; float m_lineWidth; QColor m_outerColor; - QColor m_lineColor; //!< unused yet - QColor m_arcColor; //!< unused yet + + QColor m_lineActiveColor; + QColor m_lineInactiveColor; + QColor m_arcActiveColor; + QColor m_arcInactiveColor; QColor m_textColor; diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index e3d22f69ab4..362207653be 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -22,6 +22,7 @@ * */ +#include #include #include #include @@ -46,6 +47,7 @@ #include "MainWindow.h" #include "ProjectJournal.h" #include "Song.h" +#include "stdshims.h" #include "StringPairDrag.h" #include "TextFloat.h" @@ -58,7 +60,6 @@ Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) : QWidget( _parent ), FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ), m_label( "" ), - m_knobPixmap( NULL ), m_volumeKnob( false ), m_volumeRatio( 100.0, 0.0, 1000000.0 ), m_buttonPressed( false ), @@ -105,10 +106,16 @@ void Knob::initUi( const QString & _name ) case knobSmall_17: case knobBright_26: case knobDark_28: - setlineColor(QApplication::palette().color( QPalette::Active, QPalette::WindowText )); + m_lineActiveColor = QApplication::palette().color(QPalette::Active, QPalette::WindowText); + m_arcActiveColor = QColor(QApplication::palette().color( + QPalette::Active, QPalette::WindowText)); + m_arcActiveColor.setAlpha(70); break; case knobVintage_32: - setlineColor(QApplication::palette().color( QPalette::Active, QPalette::Shadow )); + m_lineActiveColor = QApplication::palette().color(QPalette::Active, QPalette::Shadow); + m_arcActiveColor = QColor(QApplication::palette().color( + QPalette::Active, QPalette::Shadow)); + m_arcActiveColor.setAlpha(70); break; default: break; @@ -144,8 +151,11 @@ void Knob::onKnobNumUpdated() } // If knobFilename is still empty here we should get the fallback pixmap of size 1x1 - m_knobPixmap = new QPixmap( embed::getIconPixmap( knobFilename.toUtf8().constData() ) ); - + m_knobPixmap = make_unique(QPixmap(embed::getIconPixmap(knobFilename.toUtf8().constData()))); + if (!this->isEnabled()) + { + convertPixmapToGrayScale(*m_knobPixmap.get()); + } setFixedSize( m_knobPixmap->width(), m_knobPixmap->height() ); } } @@ -153,17 +163,6 @@ void Knob::onKnobNumUpdated() -Knob::~Knob() -{ - if( m_knobPixmap ) - { - delete m_knobPixmap; - } -} - - - - void Knob::setLabel( const QString & txt ) { m_label = txt; @@ -308,35 +307,6 @@ void Knob::setOuterColor( const QColor & c ) -QColor Knob::lineColor() const -{ - return m_lineColor; -} - - - -void Knob::setlineColor( const QColor & c ) -{ - m_lineColor = c; -} - - - -QColor Knob::arcColor() const -{ - return m_arcColor; -} - - - -void Knob::setarcColor( const QColor & c ) -{ - m_arcColor = c; -} - - - - QColor Knob::textColor() const { return m_textColor; @@ -383,6 +353,10 @@ bool Knob::updateAngle() void Knob::drawKnob( QPainter * _p ) { + bool enabled = this->isEnabled(); + QColor currentArcColor = enabled ? m_arcActiveColor : m_arcInactiveColor; + QColor currentLineColor = enabled ? m_lineActiveColor : m_lineInactiveColor; + if( updateAngle() == false && !m_cache.isNull() ) { _p->drawImage( 0, 0, m_cache ); @@ -441,33 +415,24 @@ void Knob::drawKnob( QPainter * _p ) const int arcLineWidth = 2; const int arcRectSize = m_knobPixmap->width() - arcLineWidth; - QColor col; - if( m_knobNum == knobVintage_32 ) - { col = QApplication::palette().color( QPalette::Active, QPalette::Shadow ); } - else - { col = QApplication::palette().color( QPalette::Active, QPalette::WindowText ); } - col.setAlpha( 70 ); - - p.setPen( QPen( col, 2 ) ); + p.setPen(QPen(currentArcColor, 2)); p.drawArc( mid.x() - arcRectSize/2, 1, arcRectSize, arcRectSize, 315*16, 16*m_totalAngle ); + p.setPen(QPen(currentLineColor, 2)); switch( m_knobNum ) { case knobSmall_17: { - p.setPen( QPen( lineColor(), 2 ) ); p.drawLine( calculateLine( mid, radius-2 ) ); break; } case knobBright_26: { - p.setPen( QPen( lineColor(), 2 ) ); p.drawLine( calculateLine( mid, radius-5 ) ); break; } case knobDark_28: { - p.setPen( QPen( lineColor(), 2 ) ); const float rb = qMax( ( radius - 10 ) / 3.0, 0.0 ); const float re = qMax( ( radius - 4 ), 0.0 ); @@ -478,7 +443,6 @@ void Knob::drawKnob( QPainter * _p ) } case knobVintage_32: { - p.setPen( QPen( lineColor(), 2 ) ); p.drawLine( calculateLine( mid, radius-2, 2 ) ); break; } @@ -840,3 +804,35 @@ void Knob::doConnections() this, SLOT( update() ) ); } } + + +void Knob::changeEvent(QEvent * ev) +{ + if (ev->type() == QEvent::EnabledChange) + { + onKnobNumUpdated(); + if (!m_label.isEmpty()) + { + setLabel(m_label); + } + m_cache = QImage(); + update(); + } +} + + +void convertPixmapToGrayScale(QPixmap& pixMap) +{ + QImage temp = pixMap.toImage().convertToFormat(QImage::Format_ARGB32); + for (int i = 0; i < temp.height(); ++i) + { + for (int j = 0; j < temp.width(); ++j) + { + const auto pix = temp.pixelColor(i, j); + const auto gscale = 0.2126 * pix.redF() + 0.7152 * pix.greenF() + 0.0722 * pix.blueF(); + const auto pixGray = QColor::fromRgbF(gscale, gscale, gscale, pix.alphaF()); + temp.setPixelColor(i, j, pixGray); + } + } + pixMap.convertFromImage(temp); +} From dc78b15a03af315e63b818d4a8f2e695ccc3166e Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Fri, 30 Oct 2020 16:57:20 -0400 Subject: [PATCH 127/180] Add Windows JACK support (#5716) Add Windows JACK support Also adds JACK as a submodule --- .gitmodules | 3 +++ CMakeLists.txt | 37 +++++++++++++++++++--------------- plugins/vst_base/VstPlugin.cpp | 4 ---- src/3rdparty/jack2 | 1 + src/3rdparty/weakjack/weakjack | 2 +- src/core/main.cpp | 3 --- 6 files changed, 26 insertions(+), 24 deletions(-) create mode 160000 src/3rdparty/jack2 diff --git a/.gitmodules b/.gitmodules index 5065883f18f..521b77c5824 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "plugins/sid/resid"] path = plugins/Sid/resid url = https://github.com/simonowen/resid +[submodule "src/3rdparty/jack2"] + path = src/3rdparty/jack2 + url = https://github.com/jackaudio/jack2 diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c83c1a4b6..43d7dd1523e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,10 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0057 NEW) ENDIF(COMMAND CMAKE_POLICY) + +# Import of windows.h breaks min()/max() +ADD_DEFINITIONS(-DNOMINMAX) + INCLUDE(PluginList) INCLUDE(CheckSubmodules) INCLUDE(AddFileDependencies) @@ -97,7 +101,6 @@ ENDIF(LMMS_BUILD_APPLE) IF(LMMS_BUILD_WIN32) SET(WANT_ALSA OFF) - SET(WANT_JACK OFF) SET(WANT_PULSEAUDIO OFF) SET(WANT_SNDIO OFF) SET(WANT_SOUNDIO OFF) @@ -105,7 +108,6 @@ IF(LMMS_BUILD_WIN32) SET(BUNDLE_QT_TRANSLATIONS ON) SET(LMMS_HAVE_WINMM TRUE) SET(STATUS_ALSA "") - SET(STATUS_JACK "") SET(STATUS_PULSEAUDIO "") SET(STATUS_SOUNDIO "") SET(STATUS_WINMM "OK") @@ -433,23 +435,26 @@ ENDIF(NOT LMMS_HAVE_ALSA) # check for JACK IF(WANT_JACK) - PKG_CHECK_MODULES(JACK jack>=0.77) - IF(JACK_FOUND) - IF(WANT_WEAKJACK) - SET(LMMS_HAVE_WEAKJACK TRUE) - SET(WEAKJACK_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/3rdparty/weakjack/weakjack) - SET(STATUS_JACK "OK (weak linking enabled)") - # use dlsym instead - SET(JACK_LIBRARIES ${CMAKE_DL_LIBS}) - ELSE() + IF(WANT_WEAKJACK) + SET(LMMS_HAVE_WEAKJACK TRUE) + SET(WEAKJACK_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/3rdparty/weakjack/weakjack) + SET(JACK_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/3rdparty/jack2/common) + SET(STATUS_JACK "OK (weak linking enabled)") + # use dlsym instead + SET(JACK_LIBRARIES ${CMAKE_DL_LIBS}) + SET(LMMS_HAVE_JACK TRUE) + SET(JACK_FOUND TRUE) + ELSE() + PKG_CHECK_MODULES(JACK jack>=0.77) + IF(JACK_FOUND) SET(STATUS_JACK "OK") ENDIF() - SET(LMMS_HAVE_JACK TRUE) - ELSE(JACK_FOUND) + ENDIF() + + IF(NOT JACK_FOUND) SET(JACK_INCLUDE_DIRS "") - SET(STATUS_JACK "not found, please install libjack0.100.0-dev (or similar) " - "if you require JACK support") - ENDIF(JACK_FOUND) + SET(STATUS_JACK "not found") + ENDIF() ENDIF(WANT_JACK) # check for FFTW3F-library diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 278ab8b2d71..a08ef072d5e 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -46,10 +46,6 @@ #include #ifdef LMMS_BUILD_WIN32 -# ifndef NOMINMAX -# define NOMINMAX -# endif - # include # include #endif diff --git a/src/3rdparty/jack2 b/src/3rdparty/jack2 new file mode 160000 index 00000000000..db76dd6bb87 --- /dev/null +++ b/src/3rdparty/jack2 @@ -0,0 +1 @@ +Subproject commit db76dd6bb879a0a24d73ec41cc2e6a21bca8ee08 diff --git a/src/3rdparty/weakjack/weakjack b/src/3rdparty/weakjack/weakjack index cbb05c52561..fd11655be3b 160000 --- a/src/3rdparty/weakjack/weakjack +++ b/src/3rdparty/weakjack/weakjack @@ -1 +1 @@ -Subproject commit cbb05c52561d921885ad6651af6c8dd9f514dc9a +Subproject commit fd11655be3b2efd6082968ecfe53f9cfe88bda2b diff --git a/src/core/main.cpp b/src/core/main.cpp index 9db0555222f..29be5ff6745 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -40,9 +40,6 @@ #include #ifdef LMMS_BUILD_WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif #include #endif From 6a78d8d53543b70eda94f07f8f6f6a2d1efab4a0 Mon Sep 17 00:00:00 2001 From: firewall1110 <57725851+firewall1110@users.noreply.github.com> Date: Sat, 31 Oct 2020 02:41:13 +0200 Subject: [PATCH 128/180] An extra set of parenthese to fix warnings/errors (#5754) --- src/core/audio/AudioSoundIo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index b2be4884f93..34deec1d0b8 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -250,7 +250,7 @@ void AudioSoundIo::stopProcessing() m_stopped = true; if (m_outstream) { - if (err = soundio_outstream_pause(m_outstream, true)) + if ((err = soundio_outstream_pause(m_outstream, true))) { fprintf(stderr, "AudioSoundIo::stopProcessing() :: pausing result error: %s\n", From 615d36b08baf9826530fd19251222aa6ed82f83e Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sat, 31 Oct 2020 13:28:18 +0000 Subject: [PATCH 129/180] Support LV2 with MSVC (#5730) --- .appveyor.yml | 6 +++++- CMakeLists.txt | 17 +++++++++++------ include/Lv2ControlBase.h | 7 ++++++- include/Lv2SubPluginFeatures.h | 3 ++- include/Lv2ViewBase.h | 3 ++- plugins/Lv2Effect/Lv2Effect.cpp | 5 +++++ plugins/Lv2Instrument/Lv2Instrument.cpp | 7 ++++++- 7 files changed, 37 insertions(+), 11 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7ca7d58c4ce..9b25ff5e437 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -8,7 +8,11 @@ environment: matrix: - compiler: msvc install: - - vcpkg install --triplet %PLATFORM%-windows --recurse fftw3 libsamplerate libsndfile sdl2 + - cd C:\Tools\vcpkg + - git pull + - .\bootstrap-vcpkg.bat + - cd %APPVEYOR_BUILD_FOLDER% + - vcpkg install --triplet %PLATFORM%-windows --recurse fftw3 libsamplerate libsndfile lilv lv2 sdl2 - nuget install clcache -Version 4.1.0 build_script: - cd %APPVEYOR_BUILD_FOLDER% diff --git a/CMakeLists.txt b/CMakeLists.txt index 43d7dd1523e..84fd91ee46d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,14 +203,19 @@ IF(WANT_LV2) IF(PKG_CONFIG_FOUND) PKG_CHECK_MODULES(LV2 lv2) PKG_CHECK_MODULES(LILV lilv-0) - IF(LV2_FOUND AND LILV_FOUND) - SET(LMMS_HAVE_LV2 TRUE) - SET(STATUS_LV2 "OK") - ELSE() - SET(STATUS_LV2 "not found, install it or set PKG_CONFIG_PATH appropriately") + ENDIF() + IF(NOT LV2_FOUND AND NOT LILV_FOUND) + FIND_PACKAGE(LV2 CONFIG) + FIND_PACKAGE(LILV CONFIG) + IF(LILV_FOUND) + SET(LILV_LIBRARIES "lilv::lilv") ENDIF() + ENDIF() + IF(LV2_FOUND AND LILV_FOUND) + SET(LMMS_HAVE_LV2 TRUE) + SET(STATUS_LV2 "OK") ELSE() - SET(STATUS_LV2 "not found, requires pkg-config") + SET(STATUS_LV2 "not found, install it or set PKG_CONFIG_PATH appropriately") ENDIF() ELSE(WANT_LV2) SET(STATUS_LV2 "not built as requested") diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index d5d83f37800..acfbea25ee4 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -33,6 +33,7 @@ #include "DataFile.h" #include "LinkedModelGroups.h" +#include "lmms_export.h" #include "Plugin.h" class Lv2Proc; @@ -65,7 +66,7 @@ class PluginIssue; * this class can not override virtuals of Instrument or EffectControls, so it will offer functions that must be called by virtuals in its child class */ -class Lv2ControlBase : public LinkedModelGroups +class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups { public: static Plugin::PluginTypes check(const LilvPlugin* m_plugin, @@ -87,7 +88,11 @@ class Lv2ControlBase : public LinkedModelGroups //! this is the same pointer as this, but a different type //! @param uri the Lv2 URI telling this class what plugin to construct Lv2ControlBase(class Model *that, const QString& uri); + Lv2ControlBase(const Lv2ControlBase&) = delete; ~Lv2ControlBase() override; + + Lv2ControlBase& operator=(const Lv2ControlBase&) = delete; + //! Must be checked after ctor or reload bool isValid() const { return m_valid; } diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h index 33c29c3ef26..fee3c381216 100644 --- a/include/Lv2SubPluginFeatures.h +++ b/include/Lv2SubPluginFeatures.h @@ -33,9 +33,10 @@ #include +#include "lmms_export.h" #include "Plugin.h" -class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures +class LMMS_EXPORT Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures { private: static const LilvPlugin *getPlugin(const Key &k); diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index 980e1f7efcf..5ccbbb75be1 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -34,6 +34,7 @@ #include #include "LinkedModelGroupViews.h" +#include "lmms_export.h" #include "Lv2Basics.h" class Lv2Proc; @@ -54,7 +55,7 @@ class Lv2ViewProc : public LinkedModelGroupView //! Base class for view for one Lv2 plugin -class Lv2ViewBase : public LinkedModelGroupsView +class LMMS_EXPORT Lv2ViewBase : public LinkedModelGroupsView { protected: //! @param pluginWidget A child class which inherits QWidget diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 4f84104bbe9..dd0c4c44ff9 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -35,6 +35,9 @@ +extern "C" +{ + Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), @@ -49,6 +52,8 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = new Lv2SubPluginFeatures(Plugin::Effect) }; +} + diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index ca918d2ed04..d67e2549290 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -41,6 +41,9 @@ +extern "C" +{ + Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), @@ -55,6 +58,8 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = new Lv2SubPluginFeatures(Plugin::Instrument) }; +} + @@ -221,7 +226,7 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : setAutoFillBackground(true); if (m_reloadPluginButton) { connect(m_reloadPluginButton, &QPushButton::clicked, - this, [this](){ castModel()->reloadPlugin();} ); + this, [this](){ this->castModel()->reloadPlugin();} ); } if (m_toggleUIButton) { connect(m_toggleUIButton, &QPushButton::toggled, From afd037eaecc6c8c35a09844dd3130a4b9e466852 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sun, 1 Nov 2020 00:37:38 -0400 Subject: [PATCH 130/180] Add CMT Submodule (#5755) Switch ladspa CMT plugins to submodule --- .gitmodules | 3 + plugins/LadspaEffect/cmt/CMakeLists.txt | 12 +- plugins/LadspaEffect/cmt/README | 7 - plugins/LadspaEffect/cmt/cmt | 1 + plugins/LadspaEffect/cmt/doc/COPYING | 340 ----- .../LadspaEffect/cmt/doc/adding_plugins.html | 54 - plugins/LadspaEffect/cmt/doc/bugs.html | 20 - plugins/LadspaEffect/cmt/doc/changes.html | 149 --- plugins/LadspaEffect/cmt/doc/index.html | 26 - .../LadspaEffect/cmt/doc/installation.html | 19 - plugins/LadspaEffect/cmt/doc/license.html | 16 - plugins/LadspaEffect/cmt/doc/overview.html | 14 - plugins/LadspaEffect/cmt/doc/plugins.html | 477 ------- plugins/LadspaEffect/cmt/doc/tasks.html | 36 - plugins/LadspaEffect/cmt/src/am.cpp | 103 -- plugins/LadspaEffect/cmt/src/ambisonic.cpp | 1135 ----------------- plugins/LadspaEffect/cmt/src/amp.cpp | 186 --- plugins/LadspaEffect/cmt/src/analogue.cpp | 504 -------- plugins/LadspaEffect/cmt/src/canyondelay.cpp | 223 ---- plugins/LadspaEffect/cmt/src/cmt.cpp | 184 --- plugins/LadspaEffect/cmt/src/cmt.h | 180 --- plugins/LadspaEffect/cmt/src/delay.cpp | 351 ----- plugins/LadspaEffect/cmt/src/descriptor.cpp | 116 -- .../LadspaEffect/cmt/src/disintegrator.cpp | 151 --- plugins/LadspaEffect/cmt/src/dynamic.cpp | 800 ------------ plugins/LadspaEffect/cmt/src/filter.cpp | 250 ---- .../cmt/src/freeverb/Components/allpass.cpp | 36 - .../cmt/src/freeverb/Components/allpass.h | 48 - .../cmt/src/freeverb/Components/comb.cpp | 48 - .../cmt/src/freeverb/Components/comb.h | 55 - .../cmt/src/freeverb/Components/denormals.h | 20 - .../cmt/src/freeverb/Components/revmodel.cpp | 257 ---- .../cmt/src/freeverb/Components/revmodel.h | 91 -- .../cmt/src/freeverb/Components/tuning.h | 60 - .../cmt/src/freeverb/freeverb.cpp | 199 --- .../LadspaEffect/cmt/src/freeverb/readme.txt | 67 - plugins/LadspaEffect/cmt/src/grain.cpp | 401 ------ plugins/LadspaEffect/cmt/src/hardgate.cpp | 122 -- plugins/LadspaEffect/cmt/src/init.cpp | 133 -- plugins/LadspaEffect/cmt/src/ladspa_types.h | 80 -- plugins/LadspaEffect/cmt/src/lofi.cpp | 415 ------ plugins/LadspaEffect/cmt/src/logistic.cpp | 156 --- plugins/LadspaEffect/cmt/src/mixer.cpp | 106 -- plugins/LadspaEffect/cmt/src/noise.cpp | 141 -- plugins/LadspaEffect/cmt/src/null.cpp | 260 ---- plugins/LadspaEffect/cmt/src/organ.cpp | 375 ------ plugins/LadspaEffect/cmt/src/peak.cpp | 371 ------ plugins/LadspaEffect/cmt/src/phasemod.cpp | 465 ------- plugins/LadspaEffect/cmt/src/pink.cpp | 245 ---- plugins/LadspaEffect/cmt/src/pink_full.cpp | 114 -- plugins/LadspaEffect/cmt/src/pink_sh.cpp | 148 --- plugins/LadspaEffect/cmt/src/pinknoise.h | 111 -- plugins/LadspaEffect/cmt/src/run_adding.h | 159 --- plugins/LadspaEffect/cmt/src/sine.cpp | 296 ----- plugins/LadspaEffect/cmt/src/sledgehammer.cpp | 186 --- plugins/LadspaEffect/cmt/src/syndrum.cpp | 175 --- plugins/LadspaEffect/cmt/src/utils.h | 103 -- plugins/LadspaEffect/cmt/src/vcf303.cpp | 219 ---- plugins/LadspaEffect/cmt/src/wshape_sine.cpp | 110 -- 59 files changed, 10 insertions(+), 11119 deletions(-) delete mode 100644 plugins/LadspaEffect/cmt/README create mode 160000 plugins/LadspaEffect/cmt/cmt delete mode 100644 plugins/LadspaEffect/cmt/doc/COPYING delete mode 100644 plugins/LadspaEffect/cmt/doc/adding_plugins.html delete mode 100644 plugins/LadspaEffect/cmt/doc/bugs.html delete mode 100644 plugins/LadspaEffect/cmt/doc/changes.html delete mode 100644 plugins/LadspaEffect/cmt/doc/index.html delete mode 100644 plugins/LadspaEffect/cmt/doc/installation.html delete mode 100644 plugins/LadspaEffect/cmt/doc/license.html delete mode 100644 plugins/LadspaEffect/cmt/doc/overview.html delete mode 100644 plugins/LadspaEffect/cmt/doc/plugins.html delete mode 100644 plugins/LadspaEffect/cmt/doc/tasks.html delete mode 100644 plugins/LadspaEffect/cmt/src/am.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/ambisonic.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/amp.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/analogue.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/canyondelay.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/cmt.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/cmt.h delete mode 100644 plugins/LadspaEffect/cmt/src/delay.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/descriptor.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/disintegrator.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/dynamic.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/filter.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/allpass.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/allpass.h delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/comb.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/comb.h delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/denormals.h delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/Components/tuning.h delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/freeverb/readme.txt delete mode 100644 plugins/LadspaEffect/cmt/src/grain.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/hardgate.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/init.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/ladspa_types.h delete mode 100644 plugins/LadspaEffect/cmt/src/lofi.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/logistic.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/mixer.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/noise.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/null.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/organ.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/peak.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/phasemod.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/pink.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/pink_full.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/pink_sh.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/pinknoise.h delete mode 100644 plugins/LadspaEffect/cmt/src/run_adding.h delete mode 100644 plugins/LadspaEffect/cmt/src/sine.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/sledgehammer.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/syndrum.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/utils.h delete mode 100644 plugins/LadspaEffect/cmt/src/vcf303.cpp delete mode 100644 plugins/LadspaEffect/cmt/src/wshape_sine.cpp diff --git a/.gitmodules b/.gitmodules index 521b77c5824..efee7e4cba2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,6 @@ [submodule "src/3rdparty/jack2"] path = src/3rdparty/jack2 url = https://github.com/jackaudio/jack2 +[submodule "plugins/LadspaEffect/cmt/cmt"] + path = plugins/LadspaEffect/cmt/cmt + url = https://github.com/lmms/cmt diff --git a/plugins/LadspaEffect/cmt/CMakeLists.txt b/plugins/LadspaEffect/cmt/CMakeLists.txt index f9fcd89cbb4..7aae5a68324 100644 --- a/plugins/LadspaEffect/cmt/CMakeLists.txt +++ b/plugins/LadspaEffect/cmt/CMakeLists.txt @@ -1,5 +1,5 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") -FILE(GLOB_RECURSE SOURCES src/*.cpp) +FILE(GLOB_RECURSE SOURCES cmt/src/*.cpp) LIST(SORT SOURCES) ADD_LIBRARY(cmt MODULE ${SOURCES}) INSTALL(TARGETS cmt LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa") @@ -7,13 +7,13 @@ INSTALL(TARGETS cmt LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa") SET_TARGET_PROPERTIES(cmt PROPERTIES PREFIX "") SET_TARGET_PROPERTIES(cmt PROPERTIES COMPILE_FLAGS "-Wall -O3 -fno-strict-aliasing") -IF(LMMS_BUILD_WIN32) - ADD_CUSTOM_COMMAND(TARGET cmt POST_BUILD COMMAND "${STRIP}" \"$\") -ELSE(LMMS_BUILD_WIN32) +IF(LMMS_BUILD_WIN32 AND NOT CMAKE_BUILD_TYPE MATCHES "Deb") + ADD_CUSTOM_COMMAND(TARGET cmt POST_BUILD COMMAND "${STRIP}" "$") +ELSE() SET_TARGET_PROPERTIES(cmt PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -fPIC") -ENDIF(LMMS_BUILD_WIN32) +ENDIF() IF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD) SET_TARGET_PROPERTIES(cmt PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined") -ENDIF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD) +ENDIF() diff --git a/plugins/LadspaEffect/cmt/README b/plugins/LadspaEffect/cmt/README deleted file mode 100644 index 3653569fcbf..00000000000 --- a/plugins/LadspaEffect/cmt/README +++ /dev/null @@ -1,7 +0,0 @@ -Computer Music Toolkit (CMT) ----------------------------- - -This toolkit is a set of musical sound processing and synthesis tools -presented as a LADSPA plugin library. See the doc/ directory for -documentation and installation instructions. See http://www.ladspa.org -for LADSPA information. See http://www.ladspa.org/cmt for CMT updates. diff --git a/plugins/LadspaEffect/cmt/cmt b/plugins/LadspaEffect/cmt/cmt new file mode 160000 index 00000000000..f7c25ed4ef7 --- /dev/null +++ b/plugins/LadspaEffect/cmt/cmt @@ -0,0 +1 @@ +Subproject commit f7c25ed4ef7f4d7efb1bcd4229d25595d4f1ce55 diff --git a/plugins/LadspaEffect/cmt/doc/COPYING b/plugins/LadspaEffect/cmt/doc/COPYING deleted file mode 100644 index eeb586b392a..00000000000 --- a/plugins/LadspaEffect/cmt/doc/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - 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; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/plugins/LadspaEffect/cmt/doc/adding_plugins.html b/plugins/LadspaEffect/cmt/doc/adding_plugins.html deleted file mode 100644 index 785bdfdea8d..00000000000 --- a/plugins/LadspaEffect/cmt/doc/adding_plugins.html +++ /dev/null @@ -1,54 +0,0 @@ -

Adding Plugins to the CMT Library

- -

The CMT -LADSPA plugin collection is written in C++ and uses a little -additional sophistication to make plugin writing easier. This document -describes how to add a new plugin to the toolkit.

- -

At the moment CMT is not under public version control, so please -send changes to Richard -Furse.

- -

CMT plugins interpret LADSPA_Handle entities as -pointers to objects derived from the CMT_PluginInstance -class. Plugin instance structures are defined by subclassing this, so -writing a descendent of CMT_PluginInstance is the first -thing to do. The CMT library provides its own implementation of -connect_port(), cleanup() and a templated -form of instantiate() (see -CMT_Instantiate<>()). These calls assume that any -instantiation or cleanup mechanisms required will be written in the -constructor or destructor of the class.

- -

When writing a plugin module, an initialisation function should be -included. To ensure this is called, add a call to the -initialise_modules() function in -descriptor.cpp. The module should also be added to the -makefile.

- -

Your initialisation function should construct new -CMT_Desctiptor plugin descriptor structures and pass them -to registerNewPluginDescriptor(). The -CMT_Descriptor is directly descended from -LADSPA_Descriptor but provides constructor, destructor -and addPort() methods.

- -

All plugins need unique IDs. During development, use values between -1 and 1000. When the plugin is ready, please request an ID from ladspa@muse.demon.co.uk. Please -also add a brief description of your module to plugins.html.

- -

In practice, CMT plugin writing is probably best learned by -example. For a simple case, see the mixer.cpp -module. This defines a SimpleMixer class to handle -instance data, a runSimpleMixer() function for use with -it and a mixer_descriptor() function to provide a -description of the plugin to the CMT core. The -mixer_descriptor() function is declared and referenced in -the descriptor.cpp module. Additional information is -available in cmt.h and ladspa.h.

- -

CMT plugins are licenced under GPL -version 2. Please read and understand this license before submitting -plugins to the library.

diff --git a/plugins/LadspaEffect/cmt/doc/bugs.html b/plugins/LadspaEffect/cmt/doc/bugs.html deleted file mode 100644 index e1c89b44801..00000000000 --- a/plugins/LadspaEffect/cmt/doc/bugs.html +++ /dev/null @@ -1,20 +0,0 @@ -

CMT Bugs

- -

Please report bugs to -richard@muse.demon.co.uk.

- -
    - -
  • I'm not sure I've got attack & decay the right way around in the -expander plugins.
  • - -
  • Need to have a look at dynamic.cpp for handling of unusual -arithmetic situation such as isnan(), -isinf() etc.
  • - -
  • Memory management is a little haphazard at present. What happens -when new() fails? The host can use -set_new_handler(), but I suspect this needs further -thought anyway.
  • - -
diff --git a/plugins/LadspaEffect/cmt/doc/changes.html b/plugins/LadspaEffect/cmt/doc/changes.html deleted file mode 100644 index 38bd7071755..00000000000 --- a/plugins/LadspaEffect/cmt/doc/changes.html +++ /dev/null @@ -1,149 +0,0 @@ -

CMT Changes

- -

Version 1.01 - 4 May 2000

-
    - -
  • Initial Release.
  • - -
- -

Version 1.02 - 11 May 2000

-
    - -
  • Use _init() and _fini(). To handle -memory management automatically.
  • - -
  • Change from *_descriptor() approach simpler -initialise_*() approach. Use _init() and -_fini() to handle memory management. Supply -CMT_Descriptor::~CMT_Descriptor().
  • - -
  • Make comments compatible with Doxygen.
  • - -
  • Addition of Ambisonic encoder, decoder, converter and rotation -plugins.
  • - -
  • Addition of Sine Waveshaper and Granular Scatter Processor -plugin.
  • - -
- -

Version 1.03 - 14 May 2000

-
    - -
  • Updated to correspond to http://www.ladspa.org/.
  • - -
- -

Version 1.04 - 18 May 2000

-
    - -
  • Bugfixes: Ambisonic encoder inputs, white noise amplitude/DC, -Ambisonic rotation inplace support, sine oscillator frequency input -inplace support.
  • - -
- -

Version 1.05 - 18 May 2000

-
    - -
  • Bugfix: use explicit pointer type when deleting -ImplementationData in ~CMT_Descriptor.
  • - -
- -

Version 1.06 - 24 Sep 2000

-
    - -
  • Introduction of Identity plugins.
  • - -
- -

Version 1.07 - 30 Sep 2000

-
    - -
  • Use constructor/destructor rather than _fini() and _init(). Use -C++ for linkage.
  • - -
- -

Version 1.08 - 30 Sep 2000

-
    - -
  • Fix to Ambisonic decode equations.
  • - -
- -

Version 1.09 - 4 Nov 2000

-
    - -
  • Addition of a port of Freeverb (version 3) and a collection of -plugins by David Bartold (analogue, canyon_delay, organ, syndrum, -vcf303).
  • - -
- -

Version 1.10 - 17 Feb 2001

-
    - -
  • Small compile fixes to some modules. Apologies to David who sent -me a patch ages ago for the analogue module.
  • - -
- -

Version 1.11 - 8 May 2001

-
    - -
  • Addition of newline character to end of allpass.h.
  • - -
- -

Version 1.12 - 17 Sept 2001

-
    - -
  • Addition of new plugins by David: "Lo Fi" and "Phase Modulated -Voice."
  • - -
- -

Version 1.13 - 7 May 2002

-
    - -
  • Fix to B-Format rotation algorithm.
  • - -
- -

Version 1.14 - 7 Aug 2002

-
    - -
  • Fix to B-Format rotation algorithm.
  • - -
  • Update for LADSPA 1.1 (include default values).
  • - -
- -

Version 1.15 - 19 Dec 2002

-
    - -
  • Addition of a number of utility routines and namespaces by -Nathaniel Virgo.
  • - -
  • Addition of a number of plugins by Nathaniel Virgo.
  • - -
  • Small change to trigger mechanism in syndrum plugin.
  • - -
- -

Version 1.16 - 6 Nov 2007

-
    - -
  • Remove -Werror from compile options in makefile.
  • - -
  • Remove "local" part from install directories.
  • - -
  • Small additional changes to makefile for robustness.
  • - -
  • Replace strdup() with localStrdup() to avoid malloc/new -mismatch.
  • - -
diff --git a/plugins/LadspaEffect/cmt/doc/index.html b/plugins/LadspaEffect/cmt/doc/index.html deleted file mode 100644 index abab9a5a90b..00000000000 --- a/plugins/LadspaEffect/cmt/doc/index.html +++ /dev/null @@ -1,26 +0,0 @@ -

CMT Index

- - - -

Other Links

- - - -

Richard Furse can be emailed as richard@muse.demon.co.uk. - -

- diff --git a/plugins/LadspaEffect/cmt/doc/installation.html b/plugins/LadspaEffect/cmt/doc/installation.html deleted file mode 100644 index e66e29a03d1..00000000000 --- a/plugins/LadspaEffect/cmt/doc/installation.html +++ /dev/null @@ -1,19 +0,0 @@ -

CMT Installation

- -

To build the plugin library, enter the src/ directory -and run make. The makefile expects to find the -ladspa.h header file in your include path or -/usr/local/include/. If you do not have this file it can -be downloaded as part of the LADSPA SDK from -http://www.ladspa.org/download/.

- -

Running make will generate the CMT LADSPA plugin -library (cmt.so) in the plugins/ -directory. This can be moved to an appropriate location depending on -the application you are using. Running make install from -the src/ directory as root will install to -/usr/local/lib/ladspa/ which is on the search path -recommended for hosts looking for plugin libraries. Some applications -may not search this directory automatically.

- diff --git a/plugins/LadspaEffect/cmt/doc/license.html b/plugins/LadspaEffect/cmt/doc/license.html deleted file mode 100644 index a7e00db169d..00000000000 --- a/plugins/LadspaEffect/cmt/doc/license.html +++ /dev/null @@ -1,16 +0,0 @@ -

CMT License

- -

The CMT toolkit is licensed under GPL version -2.

- -

As I understand it (I'm not a lawyer) this means that, once built, -the CMT library may be used with non-GPL'd applications as -long as it is built and loaded using the standard LADSPA -dynamic-linking approach without modification. In my opinion this is a -good thing for the toolkit, if not for the GPL.

- -

The above may not be correct when built against the LGPL version of -the ladpsa.h header file, but it is certainly the way we would like -things to be. See the LADPSA -license for further details.

diff --git a/plugins/LadspaEffect/cmt/doc/overview.html b/plugins/LadspaEffect/cmt/doc/overview.html deleted file mode 100644 index e34b7a9b7b3..00000000000 --- a/plugins/LadspaEffect/cmt/doc/overview.html +++ /dev/null @@ -1,14 +0,0 @@ -

Computer Music Toolkit (CMT) v1.16 Overview

- -

The Computer Music Toolkit (CMT) is a collection of LADSPA plugins for use with software -synthesis and recording packages on Linux. See the license before use.

- -

The CMT was initially designed and developed by Richard W.E. Furse -(who was also the principal designer of the LADSPA standard) and -further plugins have been provided by by Jezar, David Bartold and -Nathaniel Virgo. If you are a programmer or can write documentation -and would like to help out, please feel free to contact Richard.

- diff --git a/plugins/LadspaEffect/cmt/doc/plugins.html b/plugins/LadspaEffect/cmt/doc/plugins.html deleted file mode 100644 index 9eddd04886d..00000000000 --- a/plugins/LadspaEffect/cmt/doc/plugins.html +++ /dev/null @@ -1,477 +0,0 @@ -

CMT Library Plugins

- -

The following plugins are provided in the CMT library:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Plugin IDPlugin LabelDescription
1051lpfLow Pass Filter (One Pole).
1052hpfHigh Pass Filter (One Pole).
1053delay_0.01sEcho Delay Line. The delay time may be varied up to 0.01 -seconds. No feedback is provided.
1054delay_0.1sEcho Delay Line. The delay time may be varied up to 0.1 -seconds. No feedback is provided.
1055delay_1sEcho Delay Line. The delay time may be varied up to 1 -second. No feedback is provided.
1056delay_5sEcho Delay Line. The delay time may be varied up to 5 -seconds. No feedback is provided.
1057delay_60sEcho Delay Line. The delay time may be varied up to 60 -seconds. No feedback is provided.
1058fbdelay_0.01sFeedback Delay Line. The delay time may be varied up to 0.01 -seconds.
1059fbdelay_0.1sFeedback Delay Line. The delay time may be varied up to 0.1 -seconds.
1060fbdelay_1sFeedback Delay Line. The delay time may be varied up to 1 -second.
1061fbdelay_5sFeedback Delay Line. The delay time may be varied up to 5 -seconds.
1062fbdelay_60sFeedback Delay Line. The delay time may be varied up to 60 -seconds.
1063sine_faaaSine Oscillator. Frequency input is audio, Amplitude input is -audio.
1064sine_faacSine Oscillator. Frequency input is audio, Amplitude input is -control.
1065sine_fcaaSine Oscillator. Frequency input is control, Amplitude input is -audio.
1066sine_fcacSine Oscillator. Frequency input is control, Amplitude input is -control.
1067amp_monoAmplifier (Mono).
1068amp_stereoAmplifier (Stereo).
1069noise_source_whiteNoise Source (White).
1070amAmplitude Modulator.
1071mixerMixer (Stereo to Mono).
1072compress_peakSimple Compressor (Peak Envelope Tracking).
1073compress_rmsSimple Compressor (RMS Envelope Tracking).
1074expand_peakSimple Expander (Peak Envelope Tracking).
1075expand_rmsSimple Expander (RMS Envelope Tracking).
1076limit_peakSimple Limiter (Peak Envelope Tracking).
1077limit_rmsSimple Limiter (RMS Envelope Tracking).
1078track_peakEnvelope Tracker (Peak).
1079track_rmsEnvelope Tracker (RMS).
1080track_max_peakEnvelope Tracker (Maximum Peak).
1081track_max_rmsEnvelope Tracker (Maximum RMS).
1082peakPeak Monitor.
1083null_ciNull Plugin (Control Input).
1084null_aiNull Plugin (Audio Input).
1085null_coNull Plugin (Control Output).
1086null_aoNull Plugin (Audio Output).
1087encode_bformatB-Format Encoder. This plugin encodes ambisonic B-Format audio -using the inverse square law but no filtering, reverb or delay.
1088encode_fmhFMH-Format Encoder. This plugin encodes ambisonic FMH-Format audio -using the inverse square law but no filtering, reverb or delay.
1089fmh2bfFMH-Format to B-Format. This plugin simply discards the R, S, T, U -and V channels but is included for clarity.
1090bf2stereoB-Format to Stereo Ambisonic Decoder. This plugin only actually -uses its W and Y input signals and does not use UHJ.
1091bf2quadB-Format to Quad Ambisonic Decoder. This plugin only actually uses -its W, Y and Z input signals.
1092bf2cubeB-Format to Cube Ambisonic Decoder.
1093bf2octFMH-Format to Octagon Ambisonic Decoder. This plugin only actually -uses its W, X, Y, U and V inputs.
1094bf_rotate_zB-Format Rotation (Horizontal). This plugin rotates an B-Format -encoded soundfield around the Z-axis.
1095fmh_rotate_zFMH-Format Rotation (Horizontal). This plugin rotates an -FMH-Format encoded soundfield around the Z-axis.
1096grain_scatterGranular Scattering Processor. This plugin generates an output -audio stream by scattering short `grains' of sound from an input -stream. It is possible to control the length and envelope of these -grains, how far away from their source time grains may be scattered -and the density (grains/sec) of the texture produced.
1097wsshape_sineWave Shaper (Sine-Based).
1098identity_audioIdentity (Audio).
1099identity_controlIdentity (Control).
1123freeverb3Freeverb (Version 3). This reverb unit is a direct port of the -free public domain source code available from Jezar at Dreampoint.
1221analogueAnalogue Synthesizer Voice. Contains two audio oscillators, one LFO, -and three ADSRs. There are five waveforms available for the DCOs: -Sine, Triangle, Square, Sawtooth, and Fullwave rectified sine. The DCOs -may be frequency modulated and/or pulse width modulated by the LFO.
1222organOrgan Voice with Configurable Harmonics. The user may control the -loudness of the harmonics. There are three additional tones that may -be enabled and combined: brass, flute, and reed. Two ADSRs control -the envelope for the upper and lower harmonics.
1223syndrumDrum Synthesizer.
1224vcf303VCF 303. A TB-303 resonant filter clone.
1225canyon_delayCanyon Delay. A deep stereo crossdelay with built-in low pass -filters.
1226phasemodPhase Modulated Synthesizer Voice. Contains six audio oscillators, each -oscillator phase modulates the next. If a modulation coefficient is zero, -then the former oscillator's output is summed with the module's output. -DCO1's phase modulation parameter specifies an offset not a coefficient. -Example modulation parameters {1.0, 0.5, 0.0, 0.5, 0.2, 0.0} for all -six oscillators results in the output function: DCO2 (phase = DCO1 (phase = -1.0) * 0.5) + DCO5 (phase = DCO4 (phase = DCO3 (phase = 0.0) * 0.5) * 0.2) + -DCO6 (phase = 0.0). Each oscillator's output is bounded by -1.0 and 1.0, -or -360o and 360o.
1227lofiLo Fi. Simulates old audio equipment. Adds distortion, -bandwidth limiting, compression, and crackling to audio.
1841pink_interpolated_audioInterpolated pink noise. Pink noise is a kind of random -one-dimensional fractal. This plugin approximates the effect of an -extreme low pass filter on a pink noise signal. It is useful as a -natural-sounding continuously varying control signal with long-term -trends as well as short-term variation. If you use it to control the -pitch of a sine wave it can sound a bit like wind blowing. Notice that -the average value tends to gradually drift over long periods of -time. This is in the nature of pink noise, and so can't be -helped.
1843pink_shSample and hold pink noise. Similar to pink, but with stepped -instead of interpolated output.
1844pink_full_frequencyPink noise simulation with a full frequency range. You can low -pass filter this to get a similar effect to the interpolated pink -noise generator.
1845hard_gateHard noise gate. If the absolute value of the signal falls below -"threshold", it is set to zero. There is no antialiasing.
1846disintegratorAmplifies random half-cycles of it's input by multiplier. Set -multiplier to 0 and vary probability for a weird fade effect, or set -multiplier to -1 and probability to 0.5 to turn pitched sounds into -noise.
1848sledgehammerA Dynamic Sledgehammer, which you can use to bash your signals -into shape. It's basically a very simple and heavy compressor with a -sidechain. Try it with a pad sound as the carrier and a drum loop as -the modulator. Also, you can get some nice "Satan Maximiser"-style -distortion by not connecting the modulator (set it's influence to 0) -and putting the rate up to around 0.1.
1849logisticLogistic Map chaotic stepped control generator. The logistic map -is a classic example from chaos theory. It can be summarised by the -formula
x := r*x*(1-x).
With r<3, x just converges to a -constant value. With r just greater than 3 it oscillates with period -2. at about 3.45 the period doubles again to 4. These period doublings -occur at smaller and smaller increments of r, until at about 3.5699 -there have been an infinite number and the signal never -repeates. Between this value and 4 the system exhibits chaotic -behaviour (although there are regions of periodicity). Papers are -still being published today on the subject of this system's -behaviour. This plugin iterates this map at a given frequency to -produce a stepped signal, which is scaled to lie in the range -(-1,1). When this signal is used as a frequency control it can -sometimes sound quite musical.
- -

"Ambisonics" is a registered trademark of Nimbus Communications -International.

diff --git a/plugins/LadspaEffect/cmt/doc/tasks.html b/plugins/LadspaEffect/cmt/doc/tasks.html deleted file mode 100644 index 72a9ff6d35d..00000000000 --- a/plugins/LadspaEffect/cmt/doc/tasks.html +++ /dev/null @@ -1,36 +0,0 @@ -

CMT Library Task List

- -

Basic Plugins Needed

- -
    - -
  • Noise Gate
  • -
  • Flanger
  • -
  • Phaser
  • -
  • Chorus
  • -
  • Unbounded Delay (echo & feedback)
  • -
  • Distortion
  • -
  • Overdrive
  • -
  • Exciter
  • -
  • Resonant Filter
  • -
  • Graphic EQ
  • -
  • Envelope Generator
  • - -
- -

Other Plugins Planned

- -
    - -
  • Vocoder
  • - -
- -

Other Tasks

- -
    - -
  • Think up a better name than CMT.
  • - -
- diff --git a/plugins/LadspaEffect/cmt/src/am.cpp b/plugins/LadspaEffect/cmt/src/am.cpp deleted file mode 100644 index 2e07ae5538d..00000000000 --- a/plugins/LadspaEffect/cmt/src/am.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* am.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -#define AM_INPUT1 0 -#define AM_INPUT2 1 -#define AM_OUTPUT 2 - -/** This plugin multiplies two signals together to produce a third. */ -class AmplitudeModulator : public CMT_PluginInstance { -public: - - AmplitudeModulator(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(3) { - } - - friend void runAmplitudeModulator(LADSPA_Handle Instance, - unsigned long SAmplitudeModulatorpleCount); - -}; - -/*****************************************************************************/ - -void -runAmplitudeModulator(LADSPA_Handle Instance, - unsigned long SAmplitudeModulatorpleCount) { - - AmplitudeModulator * poAmplitudeModulator = (AmplitudeModulator *)Instance; - - LADSPA_Data * pfInput1 = poAmplitudeModulator->m_ppfPorts[AM_INPUT1]; - LADSPA_Data * pfInput2 = poAmplitudeModulator->m_ppfPorts[AM_INPUT2]; - LADSPA_Data * pfOutput = poAmplitudeModulator->m_ppfPorts[AM_OUTPUT]; - - for (unsigned long lSAmplitudeModulatorpleIndex = 0; - lSAmplitudeModulatorpleIndex < SAmplitudeModulatorpleCount; - lSAmplitudeModulatorpleIndex++) - *(pfOutput++) = *(pfInput1++) * *(pfInput2++); -} - -/*****************************************************************************/ - -void -initialise_am() { - - CMT_Descriptor * psDescriptor = new CMT_Descriptor - (1070, - "am", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Amplitude Modulator", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runAmplitudeModulator, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input 1"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input 2"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/ambisonic.cpp b/plugins/LadspaEffect/cmt/src/ambisonic.cpp deleted file mode 100644 index 60e42809ca1..00000000000 --- a/plugins/LadspaEffect/cmt/src/ambisonic.cpp +++ /dev/null @@ -1,1135 +0,0 @@ -/* ambisonic.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -/* This module provides simple plugins handling B-Format and - FMH-Format audio. Ambisonics is a mathematical technique designed - to capture the sound field around point. See - http://www.muse.demon.co.uk/3daudio.html. "Ambisonics" is a - registered trademark of Nimbus Communications International - although. An exteremly `vanilla' approach is taken to encoding - distance, using inverse square, but no filtering or delay. */ - -/*****************************************************************************/ - -#include -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -#define ENC_INPUT 0 -#define ENC_IN_X 1 -#define ENC_IN_Y 2 -#define ENC_IN_Z 3 -#define ENC_OUT_W 4 -#define ENC_OUT_X 5 -#define ENC_OUT_Y 6 -#define ENC_OUT_Z 7 - -#define ENC_OUT_R 8 -#define ENC_OUT_S 9 -#define ENC_OUT_T 10 -#define ENC_OUT_U 11 -#define ENC_OUT_V 12 - -/*****************************************************************************/ - -/** This plugin encodes a signal to B-Format depending on where it is - located in a virtual space. */ -class BFormatEncoder : public CMT_PluginInstance { -public: - BFormatEncoder(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(8) { - } - friend void runBFormatEncoder(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/** This plugin encodes a signal to FMH-Format depending on where it - is located in a virtual space. */ -class FMHFormatEncoder : public CMT_PluginInstance { -public: - FMHFormatEncoder(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(13) { - } - friend void runFMHFormatEncoder(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define F2B_IN_W 0 -#define F2B_IN_X 1 -#define F2B_IN_Y 2 -#define F2B_IN_Z 3 -#define F2B_IN_R 4 -#define F2B_IN_S 5 -#define F2B_IN_T 6 -#define F2B_IN_U 7 -#define F2B_IN_V 8 -#define F2B_OUT_W 9 -#define F2B_OUT_X 10 -#define F2B_OUT_Y 11 -#define F2B_OUT_Z 12 - -/** This plugin coverts FMH-Format to B-Format. This is a trivial - operation that can also be achieved simply by discarding RSTUV - channels. */ -class FMHToB : public CMT_PluginInstance { -public: - FMHToB(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(13) { - } - friend void runFMHToB(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define DECST_IN_W 0 -#define DECST_IN_X 1 -#define DECST_IN_Y 2 -#define DECST_IN_Z 3 -#define DECST_OUT_L 4 -#define DECST_OUT_R 5 - -/** This plugin decodes B-Format to produce a stereo speaker feed. */ -class BFormatToStereo : public CMT_PluginInstance { -public: - BFormatToStereo(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(6) { - } - friend void runBFormatToStereo(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define DECQ_IN_W 0 -#define DECQ_IN_X 1 -#define DECQ_IN_Y 2 -#define DECQ_IN_Z 3 -#define DECQ_OUT_FL 4 -#define DECQ_OUT_FR 5 -#define DECQ_OUT_BL 6 -#define DECQ_OUT_BR 7 - -/** This plugin decodes B-Format to produce a quad (square) speaker feed. */ -class BFormatToQuad : public CMT_PluginInstance { -public: - BFormatToQuad(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(8) { - } - friend void runBFormatToQuad(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define DECC_IN_W 0 -#define DECC_IN_X 1 -#define DECC_IN_Y 2 -#define DECC_IN_Z 3 -#define DECC_OUT_BFL 4 -#define DECC_OUT_BFR 5 -#define DECC_OUT_BBL 6 -#define DECC_OUT_BBR 7 -#define DECC_OUT_TFL 8 -#define DECC_OUT_TFR 9 -#define DECC_OUT_TBL 10 -#define DECC_OUT_TBR 11 - -/** This plugin decodes B-Format to produce a speaker feed for eight - speakers arranged at the corners of a cube. */ -class BFormatToCube : public CMT_PluginInstance { -public: - BFormatToCube(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(12) { - } - friend void runBFormatToCube(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define DECO_IN_W 0 -#define DECO_IN_X 1 -#define DECO_IN_Y 2 -#define DECO_IN_Z 3 -#define DECO_IN_R 4 -#define DECO_IN_S 5 -#define DECO_IN_T 6 -#define DECO_IN_U 7 -#define DECO_IN_V 8 -#define DECO_OUT_FFL 9 -#define DECO_OUT_FFR 10 -#define DECO_OUT_FRR 11 -#define DECO_OUT_BRR 12 -#define DECO_OUT_BBR 13 -#define DECO_OUT_BBL 14 -#define DECO_OUT_BLL 15 -#define DECO_OUT_FLL 16 - -/** This plugin decodes FMH-Format to produce a speaker feed for eight - speakers arranged at the corners of an octagon. */ -class FMHFormatToOct : public CMT_PluginInstance { -public: - FMHFormatToOct(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(17) { - } - friend void runFMHFormatToOct(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define BFROT_ANGLE 0 -#define BFROT_IN_W 1 -#define BFROT_IN_X 2 -#define BFROT_IN_Y 3 -#define BFROT_IN_Z 4 -#define BFROT_OUT_W 5 -#define BFROT_OUT_X 6 -#define BFROT_OUT_Y 7 -#define BFROT_OUT_Z 8 - -/** This plugin rotates an B-Format soundfield around the Z-axis. */ -class BFormatRotation : public CMT_PluginInstance { -public: - BFormatRotation(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(9) { - } - friend void runBFormatRotation(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -#define FMHROT_ANGLE 0 - -#define FMHROT_IN_W 1 -#define FMHROT_IN_X 2 -#define FMHROT_IN_Y 3 -#define FMHROT_IN_Z 4 -#define FMHROT_IN_R 5 -#define FMHROT_IN_S 6 -#define FMHROT_IN_T 7 -#define FMHROT_IN_U 8 -#define FMHROT_IN_V 9 - -#define FMHROT_OUT_W 10 -#define FMHROT_OUT_X 11 -#define FMHROT_OUT_Y 12 -#define FMHROT_OUT_Z 13 -#define FMHROT_OUT_R 14 -#define FMHROT_OUT_S 15 -#define FMHROT_OUT_T 16 -#define FMHROT_OUT_U 17 -#define FMHROT_OUT_V 18 - -/** This plugin rotates an FMH-Format soundfield around the Z-axis. */ -class FMHFormatRotation : public CMT_PluginInstance { -public: - FMHFormatRotation(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(19) { - } - friend void runFMHFormatRotation(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -void -runBFormatEncoder(LADSPA_Handle Instance, - unsigned long SampleCount) { - - BFormatEncoder * poProcessor = (BFormatEncoder *)Instance; - - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ENC_INPUT]; - LADSPA_Data * pfOutW = poProcessor->m_ppfPorts[ENC_OUT_W]; - LADSPA_Data * pfOutX = poProcessor->m_ppfPorts[ENC_OUT_X]; - LADSPA_Data * pfOutY = poProcessor->m_ppfPorts[ENC_OUT_Y]; - LADSPA_Data * pfOutZ = poProcessor->m_ppfPorts[ENC_OUT_Z]; - - LADSPA_Data fX = *(poProcessor->m_ppfPorts[ENC_IN_X]); - LADSPA_Data fY = *(poProcessor->m_ppfPorts[ENC_IN_Y]); - LADSPA_Data fZ = *(poProcessor->m_ppfPorts[ENC_IN_Z]); - LADSPA_Data fDistanceSquared = fX * fX + fY * fY + fZ * fZ; - const LADSPA_Data fWScalar = 0.707107; - LADSPA_Data fXScalar, fYScalar, fZScalar; - if (fDistanceSquared > 1e-10) { - LADSPA_Data fOneOverDistanceSquared = 1 / fDistanceSquared; - fXScalar = fX * fOneOverDistanceSquared; - fYScalar = fY * fOneOverDistanceSquared; - fZScalar = fZ * fOneOverDistanceSquared; - } - else { - /* Avoid division by zero issues. */ - fXScalar = fYScalar = fZScalar = 0; - } - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - *(pfOutW++) = fWScalar * fInput; - *(pfOutX++) = fXScalar * fInput; - *(pfOutY++) = fYScalar * fInput; - *(pfOutZ++) = fZScalar * fInput; - } -} - -/*****************************************************************************/ - -void -runFMHFormatEncoder(LADSPA_Handle Instance, - unsigned long SampleCount) { - - FMHFormatEncoder * poProcessor = (FMHFormatEncoder *)Instance; - - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ENC_INPUT]; - LADSPA_Data * pfOutW = poProcessor->m_ppfPorts[ENC_OUT_W]; - LADSPA_Data * pfOutX = poProcessor->m_ppfPorts[ENC_OUT_X]; - LADSPA_Data * pfOutY = poProcessor->m_ppfPorts[ENC_OUT_Y]; - LADSPA_Data * pfOutZ = poProcessor->m_ppfPorts[ENC_OUT_Z]; - LADSPA_Data * pfOutR = poProcessor->m_ppfPorts[ENC_OUT_R]; - LADSPA_Data * pfOutS = poProcessor->m_ppfPorts[ENC_OUT_S]; - LADSPA_Data * pfOutT = poProcessor->m_ppfPorts[ENC_OUT_T]; - LADSPA_Data * pfOutU = poProcessor->m_ppfPorts[ENC_OUT_U]; - LADSPA_Data * pfOutV = poProcessor->m_ppfPorts[ENC_OUT_V]; - - LADSPA_Data fX = *(poProcessor->m_ppfPorts[ENC_IN_X]); - LADSPA_Data fY = *(poProcessor->m_ppfPorts[ENC_IN_Y]); - LADSPA_Data fZ = *(poProcessor->m_ppfPorts[ENC_IN_Z]); - LADSPA_Data fDistanceSquared = fX * fX + fY * fY + fZ * fZ; - const LADSPA_Data fWScalar = 0.707107; - LADSPA_Data fXScalar, fYScalar, fZScalar; - LADSPA_Data fRScalar, fSScalar, fTScalar; - LADSPA_Data fUScalar, fVScalar; - if (fDistanceSquared > 1e-10) { - LADSPA_Data fOneOverDistanceSquared - = 1 / fDistanceSquared; - LADSPA_Data fOneOverDistanceCubed - = LADSPA_Data(pow(fDistanceSquared, -1.5)); - fXScalar = fX * fOneOverDistanceSquared; - fYScalar = fY * fOneOverDistanceSquared; - fZScalar = fZ * fOneOverDistanceSquared; - fRScalar = ((fZ * fZ) * fOneOverDistanceSquared - 0.5) - * sqrt(fOneOverDistanceSquared); - fSScalar = 2 * (fZ * fX) * fOneOverDistanceCubed; - fTScalar = 2 * (fY * fX) * fOneOverDistanceCubed; - fUScalar = (fX * fX - fY * fY) * fOneOverDistanceCubed; - fVScalar = 2 * (fX * fY) * fOneOverDistanceCubed; - } - else { - /* Avoid division by zero issues. */ - fXScalar = fYScalar = fZScalar - = fRScalar = fSScalar = fTScalar - = fUScalar = fVScalar = 0; - } - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - *(pfOutW++) = fWScalar * fInput; - *(pfOutX++) = fXScalar * fInput; - *(pfOutY++) = fYScalar * fInput; - *(pfOutZ++) = fZScalar * fInput; - *(pfOutR++) = fRScalar * fInput; - *(pfOutS++) = fSScalar * fInput; - *(pfOutT++) = fTScalar * fInput; - *(pfOutU++) = fUScalar * fInput; - *(pfOutV++) = fVScalar * fInput; - } -} - -/*****************************************************************************/ - -void -runFMHToB(LADSPA_Handle Instance, - unsigned long SampleCount) { - - FMHToB * poProcessor = (FMHToB *)Instance; - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[F2B_IN_W]; - LADSPA_Data * pfInX = poProcessor->m_ppfPorts[F2B_IN_X]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[F2B_IN_Y]; - LADSPA_Data * pfInZ = poProcessor->m_ppfPorts[F2B_IN_Z]; - LADSPA_Data * pfOutW = poProcessor->m_ppfPorts[F2B_OUT_W]; - LADSPA_Data * pfOutX = poProcessor->m_ppfPorts[F2B_OUT_X]; - LADSPA_Data * pfOutY = poProcessor->m_ppfPorts[F2B_OUT_Y]; - LADSPA_Data * pfOutZ = poProcessor->m_ppfPorts[F2B_OUT_Z]; - - int iSize = sizeof(LADSPA_Data) * SampleCount; - memcpy(pfOutW, pfInW, iSize); - memcpy(pfOutX, pfInX, iSize); - memcpy(pfOutY, pfInY, iSize); - memcpy(pfOutZ, pfInZ, iSize); -} - -/*****************************************************************************/ - -void -runBFormatToStereo(LADSPA_Handle Instance, - unsigned long SampleCount) { - - BFormatToStereo * poProcessor = (BFormatToStereo *)Instance; - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[DECST_IN_W]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[DECST_IN_Y]; - - LADSPA_Data * pfOutL = poProcessor->m_ppfPorts[DECST_OUT_L]; - LADSPA_Data * pfOutR = poProcessor->m_ppfPorts[DECST_OUT_R]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fA = 0.707107 * *(pfInW++); - LADSPA_Data fB = 0.5 * *(pfInY++); - *(pfOutL++) = fA + fB; - *(pfOutR++) = fA - fB; - } - -} - -/*****************************************************************************/ - -void -runBFormatToQuad(LADSPA_Handle Instance, - unsigned long SampleCount) { - - BFormatToQuad * poProcessor = (BFormatToQuad *)Instance; - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[DECQ_IN_W]; - LADSPA_Data * pfInX = poProcessor->m_ppfPorts[DECQ_IN_X]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[DECQ_IN_Y]; - - LADSPA_Data * pfOutFL = poProcessor->m_ppfPorts[DECQ_OUT_FL]; - LADSPA_Data * pfOutFR = poProcessor->m_ppfPorts[DECQ_OUT_FR]; - LADSPA_Data * pfOutBL = poProcessor->m_ppfPorts[DECQ_OUT_BL]; - LADSPA_Data * pfOutBR = poProcessor->m_ppfPorts[DECQ_OUT_BR]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fW = 0.353553 * *(pfInW++); - LADSPA_Data fX = 0.243361 * *(pfInX++); - LADSPA_Data fY = 0.243361 * *(pfInY++); - LADSPA_Data fV = 0.096383 * *(pfInY++); - *(pfOutFL++) = fW + fX + fY + fV; - *(pfOutFR++) = fW + fX - fY - fV; - *(pfOutBL++) = fW - fX + fY + fV; - *(pfOutBR++) = fW - fX - fY - fV; - } - -} - -/*****************************************************************************/ - -void -runBFormatToCube(LADSPA_Handle Instance, - unsigned long SampleCount) { - - BFormatToCube * poProcessor = (BFormatToCube *)Instance; - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[DECC_IN_W]; - LADSPA_Data * pfInX = poProcessor->m_ppfPorts[DECC_IN_X]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[DECC_IN_Y]; - LADSPA_Data * pfInZ = poProcessor->m_ppfPorts[DECC_IN_Z]; - - LADSPA_Data * pfOutBFL = poProcessor->m_ppfPorts[DECC_OUT_BFL]; - LADSPA_Data * pfOutBFR = poProcessor->m_ppfPorts[DECC_OUT_BFR]; - LADSPA_Data * pfOutBBL = poProcessor->m_ppfPorts[DECC_OUT_BBL]; - LADSPA_Data * pfOutBBR = poProcessor->m_ppfPorts[DECC_OUT_BBR]; - LADSPA_Data * pfOutTFL = poProcessor->m_ppfPorts[DECC_OUT_BFL]; - LADSPA_Data * pfOutTFR = poProcessor->m_ppfPorts[DECC_OUT_BFR]; - LADSPA_Data * pfOutTBL = poProcessor->m_ppfPorts[DECC_OUT_BBL]; - LADSPA_Data * pfOutTBR = poProcessor->m_ppfPorts[DECC_OUT_BBR]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fW = 0.176777 * *(pfInW++); - LADSPA_Data fX = 0.113996 * *(pfInX++); - LADSPA_Data fY = 0.113996 * *(pfInY++); - LADSPA_Data fZ = 0.113996 * *(pfInZ++); - LADSPA_Data fS = 0.036859 * *(pfInX++); - LADSPA_Data fT = 0.036859 * *(pfInY++); - LADSPA_Data fV = 0.036859 * *(pfInZ++); - *(pfOutBFL++) = fW + fX + fY - fZ + fV - fT - fS; - *(pfOutBFR++) = fW + fX - fY - fZ - fV + fT - fS; - *(pfOutBBL++) = fW - fX + fY - fZ + fV + fT + fS; - *(pfOutBBR++) = fW - fX - fY - fZ - fV - fT + fS; - *(pfOutTFL++) = fW + fX + fY + fZ + fV + fT + fS; - *(pfOutTFR++) = fW + fX - fY + fZ - fV - fT + fS; - *(pfOutTBL++) = fW - fX + fY + fZ + fV - fT - fS; - *(pfOutTBR++) = fW - fX - fY + fZ - fV + fT - fS; - } - -} - -/*****************************************************************************/ - -void -runFMHFormatToOct(LADSPA_Handle Instance, - unsigned long SampleCount) { - - FMHFormatToOct * poProcessor = (FMHFormatToOct *)Instance; - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[DECO_IN_W]; - LADSPA_Data * pfInX = poProcessor->m_ppfPorts[DECO_IN_X]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[DECO_IN_Y]; - LADSPA_Data * pfInU = poProcessor->m_ppfPorts[DECO_IN_U]; - LADSPA_Data * pfInV = poProcessor->m_ppfPorts[DECO_IN_V]; - - LADSPA_Data * pfOutFFL = poProcessor->m_ppfPorts[DECO_OUT_FFL]; - LADSPA_Data * pfOutFFR = poProcessor->m_ppfPorts[DECO_OUT_FFR]; - LADSPA_Data * pfOutFRR = poProcessor->m_ppfPorts[DECO_OUT_FRR]; - LADSPA_Data * pfOutBRR = poProcessor->m_ppfPorts[DECO_OUT_BRR]; - LADSPA_Data * pfOutBBR = poProcessor->m_ppfPorts[DECO_OUT_BBR]; - LADSPA_Data * pfOutBBL = poProcessor->m_ppfPorts[DECO_OUT_BBL]; - LADSPA_Data * pfOutBLL = poProcessor->m_ppfPorts[DECO_OUT_BLL]; - LADSPA_Data * pfOutFLL = poProcessor->m_ppfPorts[DECO_OUT_FLL]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fW = 0.176777 * *(pfInW++); - LADSPA_Data fX1 = 0.065888 * *pfInX; - LADSPA_Data fX2 = 0.159068 * *(pfInX++); - LADSPA_Data fY1 = 0.065888 * *pfInY; - LADSPA_Data fY2 = 0.159068 * *(pfInY++); - LADSPA_Data fU = 0.034175 * *(pfInU++); - LADSPA_Data fV = 0.034175 * *(pfInV++); - *(pfOutFFL++) = fW + fX2 + fY1 + fU + fV; - *(pfOutFFR++) = fW + fX2 - fY1 + fU - fV; - *(pfOutFRR++) = fW + fX1 - fY2 - fU - fV; - *(pfOutBRR++) = fW - fX1 + fY2 - fU + fV; - *(pfOutBBR++) = fW - fX2 + fY1 + fU + fV; - *(pfOutBBL++) = fW - fX2 - fY1 + fU - fV; - *(pfOutBLL++) = fW - fX1 - fY2 - fU - fV; - *(pfOutFLL++) = fW + fX1 + fY2 - fU + fV; - } - -} - -/*****************************************************************************/ - -void -runBFormatRotation(LADSPA_Handle Instance, - unsigned long SampleCount) { - - BFormatRotation * poProcessor = (BFormatRotation *)Instance; - - /* Work in radians. */ - LADSPA_Data fAngle - = LADSPA_Data(M_PI / 180.0) * *(poProcessor->m_ppfPorts[FMHROT_ANGLE]); - LADSPA_Data fSin = sin(fAngle); - LADSPA_Data fCos = cos(fAngle); - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[BFROT_IN_W]; - LADSPA_Data * pfInX = poProcessor->m_ppfPorts[BFROT_IN_X]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[BFROT_IN_Y]; - LADSPA_Data * pfInZ = poProcessor->m_ppfPorts[BFROT_IN_Z]; - - LADSPA_Data * pfOutW = poProcessor->m_ppfPorts[BFROT_OUT_W]; - LADSPA_Data * pfOutX = poProcessor->m_ppfPorts[BFROT_OUT_X]; - LADSPA_Data * pfOutY = poProcessor->m_ppfPorts[BFROT_OUT_Y]; - LADSPA_Data * pfOutZ = poProcessor->m_ppfPorts[BFROT_OUT_Z]; - - int iSize = sizeof(LADSPA_Data) * SampleCount; - memcpy(pfOutW, pfInW, iSize); - memcpy(pfOutZ, pfInZ, iSize); - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - float fInX = *(pfInX++); - float fInY = *(pfInY++); - *(pfOutX++) = fCos * fInX - fSin * fInY; - *(pfOutY++) = fSin * fInX + fCos * fInY; - } -} - -/*****************************************************************************/ - -void -runFMHFormatRotation(LADSPA_Handle Instance, - unsigned long SampleCount) { - - FMHFormatRotation * poProcessor = (FMHFormatRotation *)Instance; - - /* Work in radians. */ - LADSPA_Data fAngle - = LADSPA_Data(M_PI / 180.0) * *(poProcessor->m_ppfPorts[FMHROT_ANGLE]); - LADSPA_Data fSin = sin(fAngle); - LADSPA_Data fCos = cos(fAngle); - LADSPA_Data fSin2 = sin(fAngle * 2); - LADSPA_Data fCos2 = cos(fAngle * 2); - - LADSPA_Data * pfInW = poProcessor->m_ppfPorts[FMHROT_IN_W]; - LADSPA_Data * pfInX = poProcessor->m_ppfPorts[FMHROT_IN_X]; - LADSPA_Data * pfInY = poProcessor->m_ppfPorts[FMHROT_IN_Y]; - LADSPA_Data * pfInZ = poProcessor->m_ppfPorts[FMHROT_IN_Z]; - LADSPA_Data * pfInR = poProcessor->m_ppfPorts[FMHROT_IN_R]; - LADSPA_Data * pfInS = poProcessor->m_ppfPorts[FMHROT_IN_S]; - LADSPA_Data * pfInT = poProcessor->m_ppfPorts[FMHROT_IN_T]; - LADSPA_Data * pfInU = poProcessor->m_ppfPorts[FMHROT_IN_U]; - LADSPA_Data * pfInV = poProcessor->m_ppfPorts[FMHROT_IN_V]; - - LADSPA_Data * pfOutW = poProcessor->m_ppfPorts[FMHROT_OUT_W]; - LADSPA_Data * pfOutX = poProcessor->m_ppfPorts[FMHROT_OUT_X]; - LADSPA_Data * pfOutY = poProcessor->m_ppfPorts[FMHROT_OUT_Y]; - LADSPA_Data * pfOutZ = poProcessor->m_ppfPorts[FMHROT_OUT_Z]; - LADSPA_Data * pfOutR = poProcessor->m_ppfPorts[FMHROT_OUT_R]; - LADSPA_Data * pfOutS = poProcessor->m_ppfPorts[FMHROT_OUT_S]; - LADSPA_Data * pfOutT = poProcessor->m_ppfPorts[FMHROT_OUT_T]; - LADSPA_Data * pfOutU = poProcessor->m_ppfPorts[FMHROT_OUT_U]; - LADSPA_Data * pfOutV = poProcessor->m_ppfPorts[FMHROT_OUT_V]; - - int iSize = sizeof(LADSPA_Data) * SampleCount; - memcpy(pfOutW, pfInW, iSize); - memcpy(pfOutZ, pfInZ, iSize); - memcpy(pfOutR, pfInR, iSize); - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - float fInX = *(pfInX++); - float fInY = *(pfInY++); - float fInS = *(pfInS++); - float fInT = *(pfInT++); - float fInU = *(pfInU++); - float fInV = *(pfInV++); - - *(pfOutX++) = fCos * fInX - fSin * fInY; - *(pfOutY++) = fSin * fInX + fCos * fInY; - *(pfOutS++) = fCos * fInS - fSin * fInT; - *(pfOutT++) = fSin * fInS + fCos * fInT; - *(pfOutU++) = fCos2 * fInU - fSin2 * fInV; - *(pfOutV++) = fSin2 * fInU + fCos2 * fInV; - } -} - -/*****************************************************************************/ - -void -initialise_ambisonic() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1087, - "encode_bformat", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Encoder (B-Format)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runBFormatEncoder, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sound Source X Coordinate", - LADSPA_HINT_DEFAULT_1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sound Source Y Coordinate", - LADSPA_HINT_DEFAULT_0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sound Source Z Coordinate", - LADSPA_HINT_DEFAULT_0); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (W)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (X)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Y)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Z)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1088, - "encode_fmh", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Encoder (FMH-Format)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runFMHFormatEncoder, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sound Source X Coordinate", - LADSPA_HINT_DEFAULT_1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sound Source Y Coordinate", - LADSPA_HINT_DEFAULT_0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sound Source Z Coordinate", - LADSPA_HINT_DEFAULT_0); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (W)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (X)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Y)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Z)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (R)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (S)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (T)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (U)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (V)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1089, - "fmh2bf", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "FMH-Format to B-Format (Discards RSTUV Channels)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runFMHToB, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (R)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (S)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (T)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (U)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (V)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (W)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (X)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Y)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Z)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1090, - "bf2stereo", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Decoder (B-Format to Stereo)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runBFormatToStereo, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Right)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1091, - "bf2quad", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Decoder (B-Format to Quad)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runBFormatToQuad, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Front Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Front Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Back Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Back Right)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1092, - "bf2cube", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Decoder (B-Format to Cube)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runBFormatToCube, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Base Front Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Base Front Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Base Back Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Base Back Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Top Front Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Top Front Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Top Back Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Top Back Right)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1093, - "fmh2oct", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Decoder (FMH-Format to Octagon)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runFMHFormatToOct, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (R)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (S)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (T)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (U)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (V)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Front Front Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Front Front Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Front Right Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Back Right Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Back Back Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Back Back Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Back Left Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Front Left Left)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1094, - "bf_rotate_z", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Rotation (B-Format, Horizontal)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runBFormatRotation, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Angle of Rotation (Degrees Anticlockwise)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_HIGH), - -180, - 180); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (W)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (X)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Y)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Z)"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1095, - "fmh_rotate_z", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Ambisonic Rotation (FMH-Format, Horizontal)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runFMHFormatRotation, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Angle of Rotation (Degrees Anticlockwise)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_HIGH), - -180, - 180); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (W)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (X)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Y)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Z)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (R)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (S)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (T)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (U)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (V)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (W)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (X)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Y)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Z)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (R)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (S)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (T)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (U)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (V)"); - registerNewPluginDescriptor(psDescriptor); - -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/amp.cpp b/plugins/LadspaEffect/cmt/src/amp.cpp deleted file mode 100644 index 1d806f1b891..00000000000 --- a/plugins/LadspaEffect/cmt/src/amp.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* amp.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -#define AMP_CONTROL 0 -#define AMP_INPUT1 1 -#define AMP_OUTPUT1 2 - -/** This plugin applies a gain to a mono signal. */ -class MonoAmplifier : public CMT_PluginInstance { -public: - - MonoAmplifier(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(3) { - } - - friend void runMonoAmplifier(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -/* Ports as above, plus... */ -#define AMP_INPUT2 3 -#define AMP_OUTPUT2 4 - -/** This plugin applies a gain to a stereo signal. */ -class StereoAmplifier : public CMT_PluginInstance { -public: - - StereoAmplifier(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(5) { - } - - friend void runStereoAmplifier(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -void -runMonoAmplifier(LADSPA_Handle Instance, - unsigned long SampleCount) { - - MonoAmplifier * poAmplifier = (MonoAmplifier *)Instance; - - LADSPA_Data * pfInput = poAmplifier->m_ppfPorts[AMP_INPUT1]; - LADSPA_Data * pfOutput = poAmplifier->m_ppfPorts[AMP_OUTPUT1]; - LADSPA_Data fGain = *(poAmplifier->m_ppfPorts[AMP_CONTROL]); - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) - *(pfOutput++) = *(pfInput++) * fGain; -} - -/*****************************************************************************/ - -void -runStereoAmplifier(LADSPA_Handle Instance, - unsigned long SampleCount) { - - unsigned long lSampleIndex; - - StereoAmplifier * poAmplifier = (StereoAmplifier *)Instance; - - LADSPA_Data fGain = *(poAmplifier->m_ppfPorts[AMP_CONTROL]); - - LADSPA_Data * pfInput = poAmplifier->m_ppfPorts[AMP_INPUT1]; - LADSPA_Data * pfOutput = poAmplifier->m_ppfPorts[AMP_OUTPUT1]; - for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) - *(pfOutput++) = *(pfInput++) * fGain; - - pfInput = poAmplifier->m_ppfPorts[AMP_INPUT2]; - pfOutput = poAmplifier->m_ppfPorts[AMP_OUTPUT2]; - for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++) - *(pfOutput++) = *(pfInput++) * fGain; -} - -/*****************************************************************************/ - -void -initialise_amp() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1067, - "amp_mono", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Amplifier (Mono)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runMonoAmplifier, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Gain", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1068, - "amp_stereo", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Amplifier (Stereo)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runStereoAmplifier, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Gain", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Left)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Right)"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/analogue.cpp b/plugins/LadspaEffect/cmt/src/analogue.cpp deleted file mode 100644 index 5a8dc265cdb..00000000000 --- a/plugins/LadspaEffect/cmt/src/analogue.cpp +++ /dev/null @@ -1,504 +0,0 @@ -/* analogue.cpp - - Analogue Voice - Analog synthesizer voice - Copyright (c) 2000 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include "cmt.h" - -#define PORT_OUT 0 -#define PORT_GATE 1 -#define PORT_VELOCITY 2 -#define PORT_FREQ 3 -#define PORT_DCO1_OCTAVE 4 -#define PORT_DCO1_WAVEFORM 5 -#define PORT_DCO1_FM 6 -#define PORT_DCO1_PWM 7 -#define PORT_DCO1_ATTACK 8 -#define PORT_DCO1_DECAY 9 -#define PORT_DCO1_SUSTAIN 10 -#define PORT_DCO1_RELEASE 11 -#define PORT_DCO2_OCTAVE 12 -#define PORT_DCO2_WAVEFORM 13 -#define PORT_DCO2_FM 14 -#define PORT_DCO2_PWM 15 -#define PORT_DCO2_ATTACK 16 -#define PORT_DCO2_DECAY 17 -#define PORT_DCO2_SUSTAIN 18 -#define PORT_DCO2_RELEASE 19 -#define PORT_LFO_FREQ 20 -#define PORT_LFO_FADEIN 21 -#define PORT_FILT_ENV_MOD 22 -#define PORT_FILT_LFO_MOD 23 -#define PORT_FILT_RES 24 -#define PORT_FILT_ATTACK 25 -#define PORT_FILT_DECAY 26 -#define PORT_FILT_SUSTAIN 27 -#define PORT_FILT_RELEASE 28 - -#define NUM_PORTS 29 - -#ifndef PI -#define PI 3.14159265358979F -#endif - -typedef struct Envelope -{ - int envelope_decay; - LADSPA_Data envelope; - - Envelope () : envelope_decay (0), envelope (0.0) {} -} Envelope; - -class Analogue : public CMT_PluginInstance -{ - LADSPA_Data sample_rate; - - int trigger; - Envelope dco1_env; - Envelope dco2_env; - Envelope filt_env; - LADSPA_Data d1; - LADSPA_Data d2; - - LADSPA_Data dco1_accum; - LADSPA_Data dco2_accum; - LADSPA_Data lfo_accum; - - LADSPA_Data lfo_vol; - -public: - Analogue(const LADSPA_Descriptor * Descriptor, - unsigned long SampleRate) - : CMT_PluginInstance(NUM_PORTS), - sample_rate (SampleRate), - trigger (0), - d1 (0.0), d2 (0.0), - dco1_accum (0.0), dco2_accum (0.0), lfo_accum (0.0) { - } - - ~Analogue () { - } - - /* Third-order approximation of a sine wave. */ - static inline LADSPA_Data - fast_sin(LADSPA_Data x) { - if (x > PI) - x = (x < PI * 1.5F) ? (PI - x) : (x - 2.0F * PI); - else if (x > PI * 0.5F) - x = PI - x; - - return x * (1.05F - x * x * 0.175F); - } - - static inline LADSPA_Data - tri(LADSPA_Data x) { - if (x > 0.75F) - x = x - 1.0F; - else if (x > 0.25F) - x = 0.5F - x; - - return x * 4.0F; - } - - static inline LADSPA_Data - envelope(Envelope *env, - int gate, - LADSPA_Data attack, - LADSPA_Data decay, - LADSPA_Data sustain, - LADSPA_Data release) - { - if (gate) - if (env->envelope_decay == 0) - { - env->envelope += (1.0F - env->envelope) * attack; - if (env->envelope >= 0.95F) - env->envelope_decay = 1; - } - else - env->envelope += (sustain - env->envelope) * decay; - else - env->envelope += -env->envelope * release; - - return env->envelope; - } - - static void - activate(LADSPA_Handle Instance) { - Analogue *analogue = (Analogue*) Instance; - - analogue->trigger = 0; - analogue->dco1_env.envelope_decay = 0; - analogue->dco1_env.envelope = 0.0; - analogue->dco2_env.envelope_decay = 0; - analogue->dco2_env.envelope = 0.0; - analogue->filt_env.envelope_decay = 0; - analogue->filt_env.envelope = 0.0; - analogue->d1 = 0.0F; - analogue->d2 = 0.0F; - - analogue->dco1_accum = 0.0F; - analogue->dco2_accum = 0.0F; - analogue->lfo_accum = 0.0F; - analogue->lfo_vol = 0.0F; - } - - static inline LADSPA_Data - osc(int waveform, - LADSPA_Data inc, - LADSPA_Data width, - LADSPA_Data *accum) { - *accum += inc; - while (*accum >= 1.0F) - *accum -= 1.0F; - - /* 0 = Sine wave */ - if (waveform == 0) - if (*accum < width) - return fast_sin (*accum / width * PI); - else - return fast_sin (PI + (*accum - width) / (1.0F - width) * PI); - - /* 1 = Triangle wave */ - else if (waveform == 1) - if (*accum < width) - return tri (*accum / width * 0.5); - else - return tri (0.5 + (*accum - width) * 0.5 / (1.0F - width)); - - /* 2 = Square wave */ - else if (waveform == 2) - return (*accum > width) ? 1.0F : -1.0F; - - /* 3 = Sawtooth wave */ - else if (waveform == 3) - if (*accum < width) - return *accum / width * 2.0F - 1.0F; - else - return (*accum - width) / (1.0F - width) * 2.0F - 1.0F; - - /* 4 = Fullwave Rectified Sine wave */ - else if (waveform == 4) - if (*accum < width) - return fast_sin (*accum / width * PI); - else - return fast_sin ((*accum - width) / (1.0F - width) * PI); - - /* 5 = Static */ - else - return (rand () & 1) ? -1.0F : 1.0F; - } - - static LADSPA_Data - inc(LADSPA_Data oct, - LADSPA_Data freq, - LADSPA_Data sample_rate) { - return pow (2.0, oct) * freq / sample_rate; - } - - static void - calc_a_b_c(Analogue *analogue, - LADSPA_Data freq, - LADSPA_Data *a, - LADSPA_Data *b, - LADSPA_Data *c) { - LADSPA_Data top_freq, k, res; - - top_freq = freq; - top_freq *= PI / analogue->sample_rate; - res = exp (-1.20 + 3.455 * *analogue->m_ppfPorts[PORT_FILT_RES]); - - k = exp (-top_freq / res); - - *a = 2.0 * cos (2.0 * top_freq) * k; - *b = -k * k; - *c = (1.0 - *a - *b) * 0.2; - } - - static inline LADSPA_Data - multiplier(Analogue *analogue, - LADSPA_Data value) { - return 1.0 - pow (0.05, 1.0 / (analogue->sample_rate * value)); - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - Analogue *analogue = (Analogue*) Instance; - unsigned long i; - int waveform1, waveform2; - int gate; - LADSPA_Data lfo_inc, inc1, inc2; - LADSPA_Data attack1, decay1, release1; - LADSPA_Data attack2, decay2, release2; - LADSPA_Data filt_attack, filt_decay, filt_release; - LADSPA_Data lfo_fadein, a = 0, b = 0, c = 0; - LADSPA_Data dco1_pwm, dco2_pwm; - LADSPA_Data dco1_fm, dco2_fm; - LADSPA_Data filt_lfo_mod; - LADSPA_Data **ports; - - ports = analogue->m_ppfPorts; - gate = (*ports[PORT_GATE] > 0.0); - if (gate == 1 && analogue->trigger == 0) - { - analogue->lfo_vol = 0.0F; - analogue->dco1_env.envelope_decay = 0; - analogue->dco1_env.envelope = 0.0; - analogue->dco2_env.envelope_decay = 0; - analogue->dco2_env.envelope = 0.0; - analogue->filt_env.envelope_decay = 0; - analogue->filt_env.envelope = 0.0; - } - - analogue->trigger = gate; - - waveform1 = (int) *ports[PORT_DCO1_WAVEFORM]; - waveform2 = (int) *ports[PORT_DCO2_WAVEFORM]; - - inc1 = inc (*ports[PORT_DCO1_OCTAVE], - *ports[PORT_FREQ], - analogue->sample_rate); - inc2 = inc (*ports[PORT_DCO2_OCTAVE], - *ports[PORT_FREQ], - analogue->sample_rate); - lfo_inc = 2.0F * PI * *ports[PORT_LFO_FREQ] / analogue->sample_rate; - - attack1 = multiplier (analogue, *ports[PORT_DCO1_ATTACK]); - decay1 = multiplier (analogue, *ports[PORT_DCO1_DECAY]); - release1 = multiplier (analogue, *ports[PORT_DCO1_RELEASE]); - - attack2 = multiplier (analogue, *ports[PORT_DCO2_ATTACK]); - decay2 = multiplier (analogue, *ports[PORT_DCO2_DECAY]); - release2 = multiplier (analogue, *ports[PORT_DCO2_RELEASE]); - - filt_attack = multiplier (analogue, *ports[PORT_FILT_ATTACK]); - filt_decay = multiplier (analogue, *ports[PORT_FILT_DECAY]); - filt_release = multiplier (analogue, *ports[PORT_FILT_RELEASE]); - - lfo_fadein = 1.0 / (*ports[PORT_LFO_FADEIN] * analogue->sample_rate); - - dco1_pwm = *analogue->m_ppfPorts[PORT_DCO1_PWM] * 0.225F; - dco2_pwm = *analogue->m_ppfPorts[PORT_DCO2_PWM] * 0.225F; - dco1_fm = *analogue->m_ppfPorts[PORT_DCO1_FM] * inc1 * 0.45F; - dco2_fm = *analogue->m_ppfPorts[PORT_DCO2_FM] * inc2 * 0.45F; - filt_lfo_mod = *analogue->m_ppfPorts[PORT_FILT_LFO_MOD] * 0.45F; - - for (i = 0; i < SampleCount; i++) - { - LADSPA_Data lfo, sample; - - analogue->lfo_accum += lfo_inc; - while (analogue->lfo_accum >= 2.0F * PI) - analogue->lfo_accum -= 2.0F * PI; - - lfo = fast_sin (analogue->lfo_accum) * analogue->lfo_vol; - - analogue->lfo_vol += lfo_fadein; - if (analogue->lfo_vol >= 1.0F) - analogue->lfo_vol = 1.0F; - - envelope (&analogue->filt_env, - gate, filt_attack, filt_decay, - *ports[PORT_FILT_SUSTAIN], filt_release); - - if ((i & 0x000f) == 0) - calc_a_b_c (analogue, - *ports[PORT_FREQ] * 0.25F + (analogue->filt_env.envelope * - *ports[PORT_FILT_ENV_MOD] * *ports[PORT_VELOCITY] * - (1.5 + filt_lfo_mod * lfo)) * *ports[PORT_FREQ] * 10.0F, &a, &b, &c); - - sample = osc (waveform1, inc1 * (1.0 + lfo * dco1_fm), - 0.5F + lfo * dco1_pwm, - &analogue->dco1_accum) - * envelope (&analogue->dco1_env, - gate, attack1, decay1, - *ports[PORT_DCO1_SUSTAIN], release1) - + osc (waveform2, inc2 * (1.0 + lfo * dco2_fm), - 0.5F + lfo * dco2_pwm, - &analogue->dco2_accum) - * envelope (&analogue->dco2_env, - gate, attack2, decay2, - *ports[PORT_DCO2_SUSTAIN], release2); - - sample = a * analogue->d1 + - b * analogue->d2 + - c * *ports[PORT_VELOCITY] * sample; - - analogue->d2 = analogue->d1; - analogue->d1 = sample; - ports[PORT_OUT][i] = sample; - } - } -}; - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "Out", - "Gate", - "Velocity", - "Frequency (Hz)", - - "DCO1 Octave", - "DCO1 Waveform", - "DCO1 LFO Frequency Modulation", - "DCO1 LFO Pulse Width Modulation", - - "DCO1 Attack", - "DCO1 Decay", - "DCO1 Sustain", - "DCO1 Release", - - "DCO2 Octave", - "DCO2 Waveform", - "DCO2 LFO Frequency Modulation", - "DCO2 LFO Pulse Width Modulation", - - "DCO2 Attack", - "DCO2 Decay", - "DCO2 Sustain", - "DCO2 Release", - - "LFO Frequency (Hz)", - "LFO Fadein", - - "Filter Envelope Modulation", - "Filter LFO Modulation", - "Filter Resonance", - - "Filter Attack", - "Filter Decay", - "Filter Sustain", - "Filter Release" -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 20000.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.001, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 10.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.00, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.00, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 20.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.00, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 } -}; - -void -initialise_analogue() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1221, - "analogue", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Analogue Voice", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("2000", "David A. Bartold"), - NULL, - CMT_Instantiate, - Analogue::activate, - Analogue::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/canyondelay.cpp b/plugins/LadspaEffect/cmt/src/canyondelay.cpp deleted file mode 100644 index 23651bd50af..00000000000 --- a/plugins/LadspaEffect/cmt/src/canyondelay.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* canyondelay.cpp - - Canyon Delay - Deep Stereo Cross Delay - Copyright (c) 1999, 2000 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - - -#include -#include -#include "cmt.h" - -#define PORT_IN_LEFT 0 -#define PORT_IN_RIGHT 1 -#define PORT_OUT_LEFT 2 -#define PORT_OUT_RIGHT 3 -#define PORT_LTR_TIME 4 -#define PORT_LTR_FEEDBACK 5 -#define PORT_RTL_TIME 6 -#define PORT_RTL_FEEDBACK 7 -#define PORT_CUTOFF 8 - -#define NUM_PORTS 9 - -#ifndef PI -#define PI 3.14159265358979 -#endif - -class CanyonDelay : public CMT_PluginInstance { - LADSPA_Data sample_rate; - - long datasize; - LADSPA_Data *data_l; - LADSPA_Data *data_r; - LADSPA_Data accum_l; - LADSPA_Data accum_r; - - int pos; - -public: - CanyonDelay(const LADSPA_Descriptor *, - unsigned long s_rate) - : CMT_PluginInstance(NUM_PORTS), - sample_rate(s_rate), - datasize(s_rate), - data_l(new LADSPA_Data[datasize]), - data_r(new LADSPA_Data[datasize]), - accum_l(0.0), - accum_r(0.0), - pos(0) { - for (long i = 0; i < datasize; i++) - data_l[i] = data_r[i] = 0.0; - } - - ~CanyonDelay() { - delete[] data_l; - delete[] data_r; - } - - static void - activate(LADSPA_Handle Instance) { - CanyonDelay *delay = (CanyonDelay*) Instance; - - for (long i = 0; i < delay->datasize; i++) - delay->data_l[i] = delay->data_r[i] = 0.0; - - delay->accum_l = 0.0; - delay->accum_r = 0.0; - delay->pos = 0; - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - CanyonDelay *delay = (CanyonDelay*) Instance; - LADSPA_Data **ports; - unsigned long i; - int l_to_r_offset, r_to_l_offset; - LADSPA_Data ltr_invmag, rtl_invmag; - LADSPA_Data filter_mag, filter_invmag; - - ports = delay->m_ppfPorts; - - l_to_r_offset = (int) (*ports[PORT_LTR_TIME] * delay->sample_rate); - r_to_l_offset = (int) (*ports[PORT_RTL_TIME] * delay->sample_rate); - - ltr_invmag = 1.0 - fabs (*ports[PORT_LTR_FEEDBACK]); - rtl_invmag = 1.0 - fabs (*ports[PORT_RTL_FEEDBACK]); - - filter_invmag = pow (0.5, (4.0 * PI * *ports[PORT_CUTOFF]) / delay->sample_rate); - filter_mag = 1.0 - filter_invmag; - - for (i = 0; i < SampleCount; i++) - { - LADSPA_Data accum_l, accum_r; - int pos1, pos2; - - accum_l = ports[PORT_IN_LEFT][i]; - accum_r = ports[PORT_IN_RIGHT][i]; - - pos1 = delay->pos - r_to_l_offset + delay->datasize; - while (pos1 >= delay->datasize) - pos1 -= delay->datasize; - - pos2 = delay->pos - l_to_r_offset + delay->datasize; - while (pos2 >= delay->datasize) - pos2 -= delay->datasize; - - /* Mix channels with past samples. */ - accum_l = accum_l * rtl_invmag + delay->data_r[pos1] * *ports[PORT_RTL_FEEDBACK]; - accum_r = accum_r * ltr_invmag + delay->data_l[pos2] * *ports[PORT_LTR_FEEDBACK]; - - /* Low-pass filter output. */ - accum_l = delay->accum_l * filter_invmag + accum_l * filter_mag; - accum_r = delay->accum_r * filter_invmag + accum_r * filter_mag; - - /* Store IIR samples. */ - delay->accum_l = accum_l; - delay->accum_r = accum_r; - - /* Store samples in arrays. */ - delay->data_l[delay->pos] = accum_l; - delay->data_r[delay->pos] = accum_r; - - ports[PORT_OUT_LEFT][i] = accum_l; - ports[PORT_OUT_RIGHT][i] = accum_r; - - delay->pos++; - if (delay->pos >= delay->datasize) - delay->pos -= delay->datasize; - } - } -}; - - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "In (Left)", - "In (Right)", - "Out (Left)", - "Out (Right)", - "Left to Right Time (Seconds)", - "Left to Right Feedback (Percent)", - "Right to Left Time (Seconds)", - "Right to Left Feedback (Percent)", - "Low-Pass Cutoff (Hz)" -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 0.99 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -1.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 0.99 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -1.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 1.0, 5000.0 } -}; - -void -initialise_canyondelay() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1225, - "canyon_delay", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Canyon Delay", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("1999, 2000", "David A. Bartold"), - NULL, - CMT_Instantiate, - CanyonDelay::activate, - CanyonDelay::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/cmt.cpp b/plugins/LadspaEffect/cmt/src/cmt.cpp deleted file mode 100644 index d735c44b970..00000000000 --- a/plugins/LadspaEffect/cmt/src/cmt.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* cmt.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -inline char * -localStrdup(const char * input) { - char * output = new char[strlen(input) + 1]; - strcpy(output, input); - return output; -} - -/*****************************************************************************/ - -CMT_Descriptor:: -~CMT_Descriptor() { - if (Label) - delete [] Label; - if (Name) - delete [] Name; - if (Maker) - delete [] Maker; - if (Copyright) - delete [] Copyright; - if (ImplementationData) - delete (CMT_ImplementationData *)ImplementationData; - if (PortDescriptors) - delete [] PortDescriptors; - if (PortNames) { - for (unsigned long lIndex = 0; lIndex < PortCount; lIndex++) - if (PortNames[lIndex]) - delete [] PortNames[lIndex]; - delete [] PortNames; - } - if (PortRangeHints) - delete [] PortRangeHints; -} - -/*****************************************************************************/ - -void -CMT_ConnectPort(LADSPA_Handle Instance, - unsigned long Port, - LADSPA_Data * DataLocation) { - CMT_PluginInstance * poInstance = (CMT_PluginInstance *)Instance; - poInstance->m_ppfPorts[Port] = DataLocation; -} - -/*****************************************************************************/ - -void -CMT_Cleanup(LADSPA_Handle Instance) { - CMT_PluginInstance * poInstance = (CMT_PluginInstance *)Instance; - delete poInstance; -} - -/*****************************************************************************/ - -CMT_Descriptor:: -CMT_Descriptor(unsigned long lUniqueID, - const char * pcLabel, - LADSPA_Properties iProperties, - const char * pcName, - const char * pcMaker, - const char * pcCopyright, - CMT_ImplementationData * poImplementationData, - LADSPA_Instantiate_Function fInstantiate, - LADSPA_Activate_Function fActivate, - LADSPA_Run_Function fRun, - LADSPA_Run_Adding_Function fRunAdding, - LADSPA_Set_Run_Adding_Gain_Function fSetRunAddingGain, - LADSPA_Deactivate_Function fDeactivate) { - - UniqueID = lUniqueID; - Label = localStrdup(pcLabel); - Properties = iProperties; - Name = localStrdup(pcName); - Maker = localStrdup(pcMaker); - Copyright = localStrdup(pcCopyright); - PortCount = 0; - ImplementationData = poImplementationData; - - instantiate = fInstantiate; - connect_port = CMT_ConnectPort; - activate = fActivate; - run = fRun; - run_adding = fRunAdding; - set_run_adding_gain = fSetRunAddingGain; - deactivate = fDeactivate; - cleanup = CMT_Cleanup; - -}; - -/*****************************************************************************/ - -typedef char * char_ptr; - -void CMT_Descriptor:: -addPort(LADSPA_PortDescriptor iPortDescriptor, - const char * pcPortName, - LADSPA_PortRangeHintDescriptor iHintDescriptor, - LADSPA_Data fLowerBound, - LADSPA_Data fUpperBound) { - - unsigned long lOldPortCount = PortCount; - unsigned long lNewPortCount = PortCount + 1; - - LADSPA_PortDescriptor * piOldPortDescriptors - = (LADSPA_PortDescriptor *)PortDescriptors; - char ** ppcOldPortNames - = (char **)PortNames; - LADSPA_PortRangeHint * psOldPortRangeHints - = (LADSPA_PortRangeHint *)PortRangeHints; - - LADSPA_PortDescriptor * piNewPortDescriptors - = new LADSPA_PortDescriptor[lNewPortCount]; - char ** ppcNewPortNames - = new char_ptr[lNewPortCount]; - LADSPA_PortRangeHint * psNewPortRangeHints - = new LADSPA_PortRangeHint[lNewPortCount]; - - if (piNewPortDescriptors == NULL - || ppcNewPortNames == NULL - || psNewPortRangeHints == NULL) { - /* Memory allocation failure that we cannot handle. Best option is - probably just to get out while the going is reasonably good. */ - return; - } - - for (unsigned long lPortIndex = 0; - lPortIndex < lOldPortCount; - lPortIndex++) { - piNewPortDescriptors[lPortIndex] = piOldPortDescriptors[lPortIndex]; - ppcNewPortNames[lPortIndex] = ppcOldPortNames[lPortIndex]; - psNewPortRangeHints[lPortIndex] = psOldPortRangeHints[lPortIndex]; - } - if (lOldPortCount > 0) { - delete [] piOldPortDescriptors; - delete [] ppcOldPortNames; - delete [] psOldPortRangeHints; - } - - piNewPortDescriptors[lOldPortCount] = iPortDescriptor; - ppcNewPortNames[lOldPortCount] = localStrdup(pcPortName); - psNewPortRangeHints[lOldPortCount].HintDescriptor = iHintDescriptor; - psNewPortRangeHints[lOldPortCount].LowerBound = fLowerBound; - psNewPortRangeHints[lOldPortCount].UpperBound = fUpperBound; - - PortDescriptors = piNewPortDescriptors; - PortNames = ppcNewPortNames; - PortRangeHints = psNewPortRangeHints; - - PortCount++; -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/cmt.h b/plugins/LadspaEffect/cmt/src/cmt.h deleted file mode 100644 index f151f34dfa2..00000000000 --- a/plugins/LadspaEffect/cmt/src/cmt.h +++ /dev/null @@ -1,180 +0,0 @@ -/* cmt.h - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -#ifndef CMT_BASE_INCLUDED -#define CMT_BASE_INCLUDED - -/*****************************************************************************/ - -#include "ladspa_types.h" - -/*****************************************************************************/ - -/** This class is the baseclass of all CMT plugin implementation - data. Baseclassed so virtual destructors can be used. */ -class CMT_ImplementationData { -public: - virtual ~CMT_ImplementationData() { - } -}; - -/*****************************************************************************/ - -/** This structure describes a CMT LADSPA Plugin. It is a direct - ancestor of the _LADSPA_Descriptor structure which allows direct - casting. A rich constructor function is provided make it easier to - write LADSPA_Descriptor objects. (Less code is required and the - compiler will tell you when you have missed an entry.) An - addPort() function makes configuration of ports more - straightforward than using the _LADSPA_Descriptor structure - directly. */ - -struct CMT_Descriptor : public _LADSPA_Descriptor { -private: - - CMT_Descriptor &operator=(const CMT_Descriptor &) { - return *this; - } - CMT_Descriptor(const CMT_Descriptor &) { - } - -public: - - ~CMT_Descriptor(); - - /** The basic constructor for a CMT_Descriptor object. If you do not - know what the parameters mean, please see the fields in the - LADSPA_Descriptor structure, described in ladspa.h. Note that - some parameters may be NULL. Note also that a template is - provided to generate instantiate functions automatically (see - CMT_Instantiate<>() below). Implementation data must be NULL if - not allocated. */ - CMT_Descriptor(unsigned long lUniqueID, - const char * pcLabel, - LADSPA_Properties iProperties, - const char * pcName, - const char * pcMaker, - const char * pcCopyright, - CMT_ImplementationData * poImplementationData, - LADSPA_Instantiate_Function fInstantiate, - LADSPA_Activate_Function fActivate, - LADSPA_Run_Function fRun, - LADSPA_Run_Adding_Function fRunAdding, - LADSPA_Set_Run_Adding_Gain_Function fSetRunAddingGain, - LADSPA_Deactivate_Function fDeactivate); - - /** This method adds a new port to the descriptor. If you do not - know what the parameters mean, please see the fields in the - LADSPA_Descriptor structure, described in ladspa.h. */ - void addPort(LADSPA_PortDescriptor iPortDescriptor, - const char * pcPortName, - LADSPA_PortRangeHintDescriptor iHintDescriptor = 0, - LADSPA_Data fLowerBound = 0, - LADSPA_Data fUpperBound = 0); - -}; - -typedef CMT_Descriptor * CMT_Descriptor_ptr; - -/*****************************************************************************/ - -/** Each plugin type must register itself with the descriptor - registry. This is done by calling the following function, passing - a newly allocated structure (that will be cleaned up on library - unload automatically). - - Each module needs to be initialised in order to have a chance to - register new plugins. This can be achieved by modifying the list - of initialiser functions in descriptor.cpp. */ -void registerNewPluginDescriptor(CMT_Descriptor * psDescriptor); - -/*****************************************************************************/ - -/** This class is the baseclass of all CMT plugins. It provides - functionality to handle LADSPA connect_port() and cleanup() - requirements (as long as plugins have correctly written - destructors!) A CMT_Instantiate<>() template is provided also, - which makes LADSPA instantiate() methods easier to write. - - Derived classes access port data through the m_ppfPorts[] - array. This contains one entry for each port, in the order in - which ports were added to the corresponding CMT_Descriptor - object. */ -class CMT_PluginInstance { -private: - - CMT_PluginInstance &operator=(const CMT_PluginInstance &) { - return *this; - } - CMT_PluginInstance(const CMT_PluginInstance &) { - } - -protected: - - LADSPA_Data ** m_ppfPorts; - - CMT_PluginInstance(const unsigned long lPortCount) - : m_ppfPorts(new LADSPA_Data_ptr[lPortCount]) { - } - virtual ~CMT_PluginInstance() { - delete [] m_ppfPorts; - } - - friend void CMT_ConnectPort(LADSPA_Handle Instance, - unsigned long Port, - LADSPA_Data * DataLocation); - friend void CMT_Cleanup(LADSPA_Handle Instance); - -}; - -/*****************************************************************************/ - -/** This template can be used to generate functions to instantiate CMT - plugins. To be used with this function, the plugin must accept two - parameters (a LADSPA_Descriptor pointer and a sample rate). See - the SimpleMixer class and mixer_descriptor() in mixer.cpp for a - simple example of this: the instantiate function for the mixer - class is generated within the mixer_descriptor() function as - "CMT_Instantiate". */ -template LADSPA_Handle -CMT_Instantiate(const LADSPA_Descriptor * Descriptor, - unsigned long SampleRate) { - return new T(Descriptor, SampleRate); -} - -/*****************************************************************************/ - -/** This macro should be used to fill in the `Maker' field in the - CMT_Descriptor. */ -#define CMT_MAKER(AUTHORS) \ - "CMT (http://www.ladspa.org/cmt, plugin by " AUTHORS ")" - -/** This macro should be used to fill in the `Copyright' field in the - CMT_Descriptor. */ -#define CMT_COPYRIGHT(YEARS, AUTHORS) \ - "(C)" YEARS ", " AUTHORS ". " \ - "GNU General Public Licence Version 2 applies." - -/*****************************************************************************/ - -#endif - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/delay.cpp b/plugins/LadspaEffect/cmt/src/delay.cpp deleted file mode 100644 index 12f6fedaa4c..00000000000 --- a/plugins/LadspaEffect/cmt/src/delay.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/* delay.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -/* This module provides delays and delays with feedback. A variety of - maximum delay times are available. (The plugins reserve different - amounts of memory space on this basis.) */ - -/*****************************************************************************/ - -#include -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -#define DELAY_TYPE_COUNT 2 - -#define DELAY_LENGTH_COUNT 5 - -/*****************************************************************************/ - -#define LIMIT_BETWEEN(x, a, b) \ -(((x) < a) ? a : (((x) > b) ? b : (x))) - -/*****************************************************************************/ - -#define DL_DELAY_LENGTH 0 -#define DL_DRY_WET 1 -#define DL_INPUT 2 -#define DL_OUTPUT 3 -/* Present only on feedback delays: */ -#define DL_FEEDBACK 4 - -/** This class is used to implement delay line plugins. Different - maximum delay times are supported as are both echo and feedback - delays. */ -class DelayLine : public CMT_PluginInstance { -private: - - LADSPA_Data m_fSampleRate; - - LADSPA_Data m_fMaximumDelay; - - LADSPA_Data * m_pfBuffer; - - /** Buffer size, a power of two. */ - unsigned long m_lBufferSize; - - /** Write pointer in buffer. */ - unsigned long m_lWritePointer; - - friend void activateDelayLine(LADSPA_Handle Instance); - friend void runSimpleDelayLine(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runFeedbackDelayLine(LADSPA_Handle Instance, - unsigned long SampleCount); - -public: - - DelayLine(const unsigned long lSampleRate, - const LADSPA_Data fMaximumDelay) - : CMT_PluginInstance(5), - m_fSampleRate(LADSPA_Data(lSampleRate)), - m_fMaximumDelay(fMaximumDelay) { - /* Buffer size is a power of two bigger than max delay time. */ - unsigned long lMinimumBufferSize - = (unsigned long)((LADSPA_Data)lSampleRate * m_fMaximumDelay); - m_lBufferSize = 1; - while (m_lBufferSize < lMinimumBufferSize) - m_lBufferSize <<= 1; - m_pfBuffer = new LADSPA_Data[m_lBufferSize]; - } - - ~DelayLine() { - delete [] m_pfBuffer; - } -}; - -/*****************************************************************************/ - -/* Initialise and activate a plugin instance. */ -void -activateDelayLine(LADSPA_Handle Instance) { - - DelayLine * poDelayLine = (DelayLine *)Instance; - - /* Need to reset the delay history in this function rather than - instantiate() in case deactivate() followed by activate() have - been called to reinitialise a delay line. */ - memset(poDelayLine->m_pfBuffer, - 0, - sizeof(LADSPA_Data) * poDelayLine->m_lBufferSize); - - poDelayLine->m_lWritePointer = 0; -} - -/*****************************************************************************/ - -/* Run a delay line instance for a block of SampleCount samples. */ -void -runSimpleDelayLine(LADSPA_Handle Instance, - unsigned long SampleCount) { - - DelayLine * poDelayLine = (DelayLine *)Instance; - - unsigned long lBufferSizeMinusOne = poDelayLine->m_lBufferSize - 1; - unsigned long lDelay = (unsigned long) - (LIMIT_BETWEEN(*(poDelayLine->m_ppfPorts[DL_DELAY_LENGTH]), - 0, - poDelayLine->m_fMaximumDelay) - * poDelayLine->m_fSampleRate); - - LADSPA_Data * pfInput - = poDelayLine->m_ppfPorts[DL_INPUT]; - LADSPA_Data * pfOutput - = poDelayLine->m_ppfPorts[DL_OUTPUT]; - LADSPA_Data * pfBuffer - = poDelayLine->m_pfBuffer; - - unsigned long lBufferWriteOffset - = poDelayLine->m_lWritePointer; - unsigned long lBufferReadOffset - = lBufferWriteOffset + poDelayLine->m_lBufferSize - lDelay; - LADSPA_Data fWet - = LIMIT_BETWEEN(*(poDelayLine->m_ppfPorts[DL_DRY_WET]), - 0, - 1); - LADSPA_Data fDry - = 1 - fWet; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInputSample = *(pfInput++); - *(pfOutput++) = (fDry * fInputSample - + fWet * pfBuffer[((lSampleIndex + lBufferReadOffset) - & lBufferSizeMinusOne)]); - pfBuffer[((lSampleIndex + lBufferWriteOffset) - & lBufferSizeMinusOne)] = fInputSample; - } - - poDelayLine->m_lWritePointer - = ((poDelayLine->m_lWritePointer + SampleCount) - & lBufferSizeMinusOne); -} - -/*****************************************************************************/ - -/** Run a feedback delay line instance for a block of SampleCount samples. */ -void -runFeedbackDelayLine(LADSPA_Handle Instance, - unsigned long SampleCount) { - - DelayLine * poDelayLine = (DelayLine *)Instance; - - unsigned long lBufferSizeMinusOne = poDelayLine->m_lBufferSize - 1; - unsigned long lDelay = (unsigned long) - (LIMIT_BETWEEN(*(poDelayLine->m_ppfPorts[DL_DELAY_LENGTH]), - 0, - poDelayLine->m_fMaximumDelay) - * poDelayLine->m_fSampleRate); - - LADSPA_Data * pfInput - = poDelayLine->m_ppfPorts[DL_INPUT]; - LADSPA_Data * pfOutput - = poDelayLine->m_ppfPorts[DL_OUTPUT]; - LADSPA_Data * pfBuffer - = poDelayLine->m_pfBuffer; - - unsigned long lBufferWriteOffset - = poDelayLine->m_lWritePointer; - unsigned long lBufferReadOffset - = lBufferWriteOffset + poDelayLine->m_lBufferSize - lDelay; - LADSPA_Data fWet - = LIMIT_BETWEEN(*(poDelayLine->m_ppfPorts[DL_DRY_WET]), - 0, - 1); - LADSPA_Data fDry - = 1 - fWet; - LADSPA_Data fFeedback - = LIMIT_BETWEEN(*(poDelayLine->m_ppfPorts[DL_FEEDBACK]), - -1, - 1); - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInputSample = *(pfInput++); - LADSPA_Data &fDelayedSample = pfBuffer[((lSampleIndex + lBufferReadOffset) - & lBufferSizeMinusOne)]; - - *(pfOutput++) = (fDry * fInputSample + fWet * fDelayedSample); - - pfBuffer[((lSampleIndex + lBufferWriteOffset) - & lBufferSizeMinusOne)] - = fInputSample + fDelayedSample * fFeedback; - } - - poDelayLine->m_lWritePointer - = ((poDelayLine->m_lWritePointer + SampleCount) - & lBufferSizeMinusOne); -} - -/*****************************************************************************/ - -template LADSPA_Handle -CMT_Delay_Instantiate(const LADSPA_Descriptor * Descriptor, - unsigned long SampleRate) { - return new DelayLine(SampleRate, - LADSPA_Data(lMaximumDelayMilliseconds - * 0.001)); -} - -/*****************************************************************************/ - -void -initialise_delay() { - - CMT_Descriptor * psDescriptor; - - const char * apcDelayTypeNames[DELAY_TYPE_COUNT] = { - "Echo", - "Feedback" - }; - const char * apcDelayTypeLabels[DELAY_TYPE_COUNT] = { - "delay", - "fbdelay" - }; - LADSPA_Run_Function afRunFunctions[DELAY_TYPE_COUNT] = { - runSimpleDelayLine, - runFeedbackDelayLine - }; - - LADSPA_Data afMaximumDelays[DELAY_LENGTH_COUNT] = { - 0.01, - 0.1, - 1, - 5, - 60 - }; - LADSPA_Instantiate_Function afInstantiateFunctions[DELAY_LENGTH_COUNT] = { - CMT_Delay_Instantiate<10>, - CMT_Delay_Instantiate<100>, - CMT_Delay_Instantiate<1000>, - CMT_Delay_Instantiate<5000>, - CMT_Delay_Instantiate<60000> - }; - - for (long lDelayTypeIndex = 0; - lDelayTypeIndex < DELAY_TYPE_COUNT; - lDelayTypeIndex++) { - - for (long lDelayLengthIndex = 0; - lDelayLengthIndex < DELAY_LENGTH_COUNT; - lDelayLengthIndex++) { - - long lPluginIndex - = lDelayTypeIndex * DELAY_LENGTH_COUNT + lDelayLengthIndex; - - char acLabel[100]; - sprintf(acLabel, - "%s_%gs", - apcDelayTypeLabels[lDelayTypeIndex], - afMaximumDelays[lDelayLengthIndex]); - char acName[100]; - sprintf(acName, - "%s Delay Line (Maximum Delay %gs)", - apcDelayTypeNames[lDelayTypeIndex], - afMaximumDelays[lDelayLengthIndex]); - - psDescriptor = new CMT_Descriptor - (1053 + lPluginIndex, - acLabel, - LADSPA_PROPERTY_HARD_RT_CAPABLE, - acName, - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - afInstantiateFunctions[lDelayLengthIndex], - activateDelayLine, - afRunFunctions[lDelayTypeIndex], - NULL, - NULL, - NULL); - - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Delay (Seconds)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_1), - 0, - afMaximumDelays[lDelayLengthIndex]); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Dry/Wet Balance", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - - if (lDelayTypeIndex == 1) - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Feedback", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_HIGH), - -1, - 1); - - registerNewPluginDescriptor(psDescriptor); - } - } -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/descriptor.cpp b/plugins/LadspaEffect/cmt/src/descriptor.cpp deleted file mode 100644 index 0a20dc67a99..00000000000 --- a/plugins/LadspaEffect/cmt/src/descriptor.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* descriptor.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -/* This module contains code providing and supporting the - CMT_Descriptor() function that provides hosts with initial access - to LADSPA plugins. ALL PLUGINS MUST BE REGISTERED IN THIS FILE (see - below). */ - -/*****************************************************************************/ - -/* Module Initialisation: - ---------------------- */ - -void initialise_am(); -void initialise_ambisonic(); -void initialise_amp(); -void initialise_analogue(); -void initialise_canyondelay(); -void initialise_delay(); -void initialise_dynamic(); -void initialise_filter(); -void initialise_freeverb3(); -void initialise_grain(); -void initialise_lofi(); -void initialise_mixer(); -void initialise_noise(); -void initialise_null(); -void initialise_organ(); -void initialise_peak(); -void initialise_phasemod(); -void initialise_sine(); -void initialise_syndrum(); -void initialise_vcf303(); -void initialise_wshape_sine(); -namespace hardgate { void initialise(); } -namespace disintegrator { void initialise(); } -namespace pink { void initialise(); } -namespace pink_full { void initialise(); } -namespace pink_sh { void initialise(); } -namespace sledgehammer { void initialise(); } -namespace logistic { void initialise(); } - -/** This function should initialise all modules in the library. This - will lead to all plugin descriptors being registered. If you write - a new plugin you should initialise it here. If the module has - structures it wishes to remove also then these should be included - in finalise_modules(). */ -void -initialise_modules() { - initialise_am(); - initialise_ambisonic(); - initialise_amp(); - initialise_analogue(); - initialise_canyondelay(); - initialise_delay(); - initialise_dynamic(); - initialise_filter(); - initialise_freeverb3(); - initialise_grain(); - initialise_lofi(); - initialise_mixer(); - initialise_noise(); - initialise_null(); - initialise_organ(); - initialise_peak(); - initialise_phasemod(); - initialise_sine(); - initialise_syndrum(); - initialise_vcf303(); - initialise_wshape_sine(); - hardgate::initialise(); - disintegrator::initialise(); - pink::initialise(); - pink_full::initialise(); - pink_sh::initialise(); - sledgehammer::initialise(); - logistic::initialise(); -} - -/*****************************************************************************/ - -/* Module Finalisation: - -------------------- */ - -void finalise_sine(); - -/** Finalise any structures allocated by the modules. This does not - include descriptors passed to registerNewPluginDescriptor(). */ -void -finalise_modules() { - finalise_sine(); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/disintegrator.cpp b/plugins/LadspaEffect/cmt/src/disintegrator.cpp deleted file mode 100644 index 39edefc5bd2..00000000000 --- a/plugins/LadspaEffect/cmt/src/disintegrator.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* disintegrator.cpp - - (c) 2002 Nathaniel Virgo - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" -#include "run_adding.h" - -/*****************************************************************************/ - -namespace disintegrator { - - enum { - port_probability = 0, - port_multiplier = 1, - port_input = 2, - port_output = 3, - n_ports = 4 - }; - -/** This plugin multiplies random half-waveforms by port_multiplier, - with probability port_probability */ - class Plugin : public CMT_PluginInstance { - LADSPA_Data run_adding_gain; - bool active; - LADSPA_Data last_input; - public: - Plugin(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(n_ports) { - active = false; last_input = 0.0f; - } - - template - friend void run(LADSPA_Handle instance, - unsigned long sample_count); - - friend void set_run_adding_gain(LADSPA_Handle instance, - LADSPA_Data new_gain); - }; - - template - void run(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - LADSPA_Data prob = *pp->m_ppfPorts[port_probability]; - LADSPA_Data mult = *pp->m_ppfPorts[port_multiplier]; - LADSPA_Data * in = pp->m_ppfPorts[port_input]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - mult *= get_gain(p.run_adding_gain); - - for ( unsigned long i = 0; i < sample_count ; ++i ) { - LADSPA_Data insig = *(in++); - if ( ( p.last_input>0 && insig<0 ) || ( p.last_input<0 && insig>0 ) ) - p.active = rand() < prob*RAND_MAX; - p.last_input = insig; - if (p.active) - write_output(out, insig*mult, 1.0f); - else - write_output(out, insig, p.run_adding_gain); - } - } - - void set_run_adding_gain(LADSPA_Handle instance, - LADSPA_Data new_gain) { - ((Plugin *) instance)->run_adding_gain = new_gain; - } - - void - initialise() { - - CMT_Descriptor * d = new CMT_Descriptor - (1846, - "disintegrator", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Disintegrator", - CMT_MAKER("Nathaniel Virgo"), - CMT_COPYRIGHT("2002", "Nathaniel Virgo"), - NULL, - CMT_Instantiate, - NULL, - run, - run, - set_run_adding_gain, - NULL); - - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Probability", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_0), - 0, - 1); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Multiplier", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_0), - -1, - 1); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - - registerNewPluginDescriptor(d); - - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ - - - - - diff --git a/plugins/LadspaEffect/cmt/src/dynamic.cpp b/plugins/LadspaEffect/cmt/src/dynamic.cpp deleted file mode 100644 index 6314cdfeb6f..00000000000 --- a/plugins/LadspaEffect/cmt/src/dynamic.cpp +++ /dev/null @@ -1,800 +0,0 @@ -/* dynamic.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -/* This module provides unsophisticated implementations of compressor, - expander and limiter plugins. Note that attack and decay times are - applied at the LEVEL DETECTION stage rather than at gain processing - (the reason no noise gate is provided). No delay is applied to the - main signal. These are useful (and efficient) tools and extended - compressors should probably allocate new IDs rather than break - compatibility in parameter set and sound for this set. */ - -// Having said this, I'm not sure the attack/decay parameters are the -// right way around. - -/*****************************************************************************/ - -#include -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" -#include "utils.h" - -/*****************************************************************************/ - -class DynamicProcessor { -protected: - - /** This state variable is used to track the input envelope (peak or - rms). The state is stored here so that the run function can - perform low-pass filtering to produce a smoothed envelope. */ - LADSPA_Data m_fEnvelopeState; - - /** The sample rate in the world this instance exists in. */ - LADSPA_Data m_fSampleRate; - - DynamicProcessor(const LADSPA_Data fSampleRate) - : m_fSampleRate(fSampleRate) { - } - -}; - -/*****************************************************************************/ - -#define CE_THRESHOLD 0 -#define CE_RATIO 1 -#define CE_ATTACK 2 -#define CE_DECAY 3 -#define CE_INPUT 4 -#define CE_OUTPUT 5 - -/** This class is used to implement simple compressor and expander - plugins. Attack and decay times are applied at the level detection - stage rather than at gain processing. No delay is applied to the - main signal. Both peak and RMS support is included. */ -class CompressorExpander - : public CMT_PluginInstance, public DynamicProcessor { -public: - - CompressorExpander(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(6), - DynamicProcessor(lSampleRate) { - } - - friend void activateCompressorExpander(void * pvHandle); - friend void runCompressor_Peak(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runCompressor_RMS(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runExpander_Peak(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runExpander_RMS(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -#define LN_THRESHOLD 0 -#define LN_ATTACK 1 -#define LN_DECAY 2 -#define LN_INPUT 3 -#define LN_OUTPUT 4 - -/** This class is used to implement simple limiter plugins. Attack and - decay times are applied at the level detection stage rather than - at gain processing. No delay is applied to the main signal. Both - peak and RMS support is included. */ -class Limiter - : public CMT_PluginInstance, public DynamicProcessor { -public: - - Limiter(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(5), - DynamicProcessor(lSampleRate) { - } - - friend void activateLimiter(void * pvHandle); - friend void runLimiter_Peak(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runLimiter_RMS(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -activateCompressorExpander(void * pvHandle) { - CompressorExpander * poProcessor = (CompressorExpander *)pvHandle; - poProcessor->m_fEnvelopeState = 0; -} - -/*****************************************************************************/ - -void -activateLimiter(void * pvHandle) { - Limiter * poProcessor = (Limiter *)pvHandle; - poProcessor->m_fEnvelopeState = 0; -} - -/*****************************************************************************/ - -void -runCompressor_Peak(LADSPA_Handle Instance, - unsigned long SampleCount) { - - CompressorExpander * poProcessor = (CompressorExpander *)Instance; - - LADSPA_Data fThreshold - = BOUNDED_BELOW(*(poProcessor->m_ppfPorts[CE_THRESHOLD]), - 0); - LADSPA_Data fOneOverThreshold - = 1 / fThreshold; - LADSPA_Data fRatioMinusOne - = *(poProcessor->m_ppfPorts[CE_RATIO]) - 1; - LADSPA_Data * pfInput - = poProcessor->m_ppfPorts[CE_INPUT]; - LADSPA_Data * pfOutput - = poProcessor->m_ppfPorts[CE_OUTPUT]; - - LADSPA_Data fEnvelopeDrag_Attack - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_ATTACK]), - poProcessor->m_fSampleRate); - LADSPA_Data fEnvelopeDrag_Decay - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_DECAY]), - poProcessor->m_fSampleRate); - - LADSPA_Data &rfEnvelopeState - = poProcessor->m_fEnvelopeState; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fabs(fInput); - if (fEnvelopeTarget > rfEnvelopeState) - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Attack - + fEnvelopeTarget * (1 - fEnvelopeDrag_Attack)); - else - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Decay - + fEnvelopeTarget * (1 - fEnvelopeDrag_Decay)); - - /* Perform the mapping. This questions this plugin's claim of - being `hard-realtime.' */ - LADSPA_Data fGain; - if (rfEnvelopeState < fThreshold) - fGain = 1; - else { - fGain = pow(rfEnvelopeState * fOneOverThreshold, fRatioMinusOne); - if (isnan(fGain)) - fGain = 0; - } - - /* Perform output. */ - *(pfOutput++) = fInput * fGain; - } -} - -/*****************************************************************************/ - -void -runCompressor_RMS(LADSPA_Handle Instance, - unsigned long SampleCount) { - - CompressorExpander * poProcessor = (CompressorExpander *)Instance; - - LADSPA_Data fThreshold - = BOUNDED_BELOW(*(poProcessor->m_ppfPorts[CE_THRESHOLD]), - 0); - LADSPA_Data fOneOverThreshold - = 1 / fThreshold; - LADSPA_Data fRatioMinusOne - = *(poProcessor->m_ppfPorts[CE_RATIO]) - 1; - LADSPA_Data * pfInput - = poProcessor->m_ppfPorts[CE_INPUT]; - LADSPA_Data * pfOutput - = poProcessor->m_ppfPorts[CE_OUTPUT]; - - LADSPA_Data fEnvelopeDrag_Attack - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_ATTACK]), - poProcessor->m_fSampleRate); - LADSPA_Data fEnvelopeDrag_Decay - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_DECAY]), - poProcessor->m_fSampleRate); - - LADSPA_Data &rfEnvelopeState - = poProcessor->m_fEnvelopeState; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fInput * fInput; - if (fEnvelopeTarget > rfEnvelopeState) - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Attack - + fEnvelopeTarget * (1 - fEnvelopeDrag_Attack)); - else - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Decay - + fEnvelopeTarget * (1 - fEnvelopeDrag_Decay)); - - LADSPA_Data fEnvelopeAmplitude = sqrt(rfEnvelopeState); - - /* Perform the mapping. This questions this plugin's claim of - being `hard-realtime.' */ - LADSPA_Data fGain; - if (fEnvelopeAmplitude < fThreshold) - fGain = 1; - else { - fGain = pow(fEnvelopeAmplitude * fOneOverThreshold, fRatioMinusOne); - if (isnan(fGain)) - fGain = 0; - } - - /* Perform output. */ - *(pfOutput++) = fInput * fGain; - } -} - -/*****************************************************************************/ - -void -runExpander_Peak(LADSPA_Handle Instance, - unsigned long SampleCount) { - - CompressorExpander * poProcessor = (CompressorExpander *)Instance; - - LADSPA_Data fThreshold - = BOUNDED_BELOW(*(poProcessor->m_ppfPorts[CE_THRESHOLD]), - 0); - LADSPA_Data fOneOverThreshold - = 1 / fThreshold; - LADSPA_Data fOneMinusRatio - = 1 - *(poProcessor->m_ppfPorts[CE_RATIO]); - LADSPA_Data * pfInput - = poProcessor->m_ppfPorts[CE_INPUT]; - LADSPA_Data * pfOutput - = poProcessor->m_ppfPorts[CE_OUTPUT]; - - LADSPA_Data fEnvelopeDrag_Attack - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_ATTACK]), - poProcessor->m_fSampleRate); - LADSPA_Data fEnvelopeDrag_Decay - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_DECAY]), - poProcessor->m_fSampleRate); - - LADSPA_Data &rfEnvelopeState - = poProcessor->m_fEnvelopeState; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fabs(fInput); - if (fEnvelopeTarget > rfEnvelopeState) - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Attack - + fEnvelopeTarget * (1 - fEnvelopeDrag_Attack)); - else - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Decay - + fEnvelopeTarget * (1 - fEnvelopeDrag_Decay)); - - /* Perform the mapping. This questions this plugin's claim of - being `hard-realtime.' */ - LADSPA_Data fGain; - if (rfEnvelopeState > fThreshold) - fGain = 1; - else { - fGain = pow(rfEnvelopeState * fOneOverThreshold, fOneMinusRatio); - if (isnan(fGain)) - fGain = 0; - } - - /* Perform output. */ - *(pfOutput++) = fInput * fGain; - } -} - -/*****************************************************************************/ - -void -runExpander_RMS(LADSPA_Handle Instance, - unsigned long SampleCount) { - - CompressorExpander * poProcessor = (CompressorExpander *)Instance; - - LADSPA_Data fThreshold - = BOUNDED_BELOW(*(poProcessor->m_ppfPorts[CE_THRESHOLD]), - 0); - LADSPA_Data fOneOverThreshold - = 1 / fThreshold; - LADSPA_Data fOneMinusRatio - = 1 - *(poProcessor->m_ppfPorts[CE_RATIO]); - LADSPA_Data * pfInput - = poProcessor->m_ppfPorts[CE_INPUT]; - LADSPA_Data * pfOutput - = poProcessor->m_ppfPorts[CE_OUTPUT]; - - LADSPA_Data fEnvelopeDrag_Attack - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_ATTACK]), - poProcessor->m_fSampleRate); - LADSPA_Data fEnvelopeDrag_Decay - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_DECAY]), - poProcessor->m_fSampleRate); - - LADSPA_Data &rfEnvelopeState - = poProcessor->m_fEnvelopeState; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fInput * fInput; - if (fEnvelopeTarget > rfEnvelopeState) - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Attack - + fEnvelopeTarget * (1 - fEnvelopeDrag_Attack)); - else - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Decay - + fEnvelopeTarget * (1 - fEnvelopeDrag_Decay)); - - LADSPA_Data fEnvelopeAmplitude = sqrt(rfEnvelopeState); - - /* Perform the mapping. This questions this plugin's claim of - being `hard-realtime.' */ - LADSPA_Data fGain; - if (fEnvelopeAmplitude > fThreshold) - fGain = 1; - else { - fGain = pow(fEnvelopeAmplitude * fOneOverThreshold, fOneMinusRatio); - if (isnan(fGain)) - fGain = 0; - } - - /* Perform output. */ - *(pfOutput++) = fInput * fGain; - } -} - -/*****************************************************************************/ - -void -runLimiter_Peak(LADSPA_Handle Instance, - unsigned long SampleCount) { - - Limiter * poProcessor = (Limiter *)Instance; - - LADSPA_Data fThreshold - = BOUNDED_BELOW(*(poProcessor->m_ppfPorts[LN_THRESHOLD]), - 0); - LADSPA_Data * pfInput - = poProcessor->m_ppfPorts[LN_INPUT]; - LADSPA_Data * pfOutput - = poProcessor->m_ppfPorts[LN_OUTPUT]; - - LADSPA_Data fEnvelopeDrag_Attack - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_ATTACK]), - poProcessor->m_fSampleRate); - LADSPA_Data fEnvelopeDrag_Decay - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_DECAY]), - poProcessor->m_fSampleRate); - - LADSPA_Data &rfEnvelopeState - = poProcessor->m_fEnvelopeState; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fabs(fInput); - if (fEnvelopeTarget > rfEnvelopeState) - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Attack - + fEnvelopeTarget * (1 - fEnvelopeDrag_Attack)); - else - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Decay - + fEnvelopeTarget * (1 - fEnvelopeDrag_Decay)); - - /* Perform the mapping. This questions this plugin's claim of - being `hard-realtime.' */ - LADSPA_Data fGain; - if (rfEnvelopeState < fThreshold) - fGain = 1; - else { - fGain = fThreshold / rfEnvelopeState; - if (isnan(fGain)) - fGain = 0; - } - - /* Perform output. */ - *(pfOutput++) = fInput * fGain; - } -} - -/*****************************************************************************/ - -void -runLimiter_RMS(LADSPA_Handle Instance, - unsigned long SampleCount) { - - Limiter * poProcessor = (Limiter *)Instance; - - LADSPA_Data fThreshold - = BOUNDED_BELOW(*(poProcessor->m_ppfPorts[LN_THRESHOLD]), - 0); - LADSPA_Data * pfInput - = poProcessor->m_ppfPorts[LN_INPUT]; - LADSPA_Data * pfOutput - = poProcessor->m_ppfPorts[LN_OUTPUT]; - - LADSPA_Data fEnvelopeDrag_Attack - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_ATTACK]), - poProcessor->m_fSampleRate); - LADSPA_Data fEnvelopeDrag_Decay - = calculate60dBDrag(*(poProcessor->m_ppfPorts[CE_DECAY]), - poProcessor->m_fSampleRate); - - LADSPA_Data &rfEnvelopeState - = poProcessor->m_fEnvelopeState; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fInput * fInput; - if (fEnvelopeTarget > rfEnvelopeState) - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Attack - + fEnvelopeTarget * (1 - fEnvelopeDrag_Attack)); - else - rfEnvelopeState = (rfEnvelopeState * fEnvelopeDrag_Decay - + fEnvelopeTarget * (1 - fEnvelopeDrag_Decay)); - - LADSPA_Data fEnvelopeAmplitude = sqrt(rfEnvelopeState); - - /* Perform the mapping. This questions this plugin's claim of - being `hard-realtime.' */ - LADSPA_Data fGain; - if (fEnvelopeAmplitude < fThreshold) - fGain = 1; - else { - fGain = fThreshold / fEnvelopeAmplitude; - if (isnan(fGain)) - fGain = 0; - } - - /* Perform output. */ - *(pfOutput++) = fInput * fGain; - } -} - -/*****************************************************************************/ - -void -initialise_dynamic() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1072, - "compress_peak", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Simple Compressor (Peak Envelope Tracking)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateCompressorExpander, - runCompressor_Peak, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Compression Ratio", - (LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Decay (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1073, - "compress_rms", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Simple Compressor (RMS Envelope Tracking)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateCompressorExpander, - runCompressor_RMS, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Compression Ratio", - (LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Decay (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1074, - "expand_peak", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Simple Expander (Peak Envelope Tracking)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateCompressorExpander, - runExpander_Peak, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Expansion Ratio", - (LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Decay (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1075, - "expand_rms", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Simple Expander (RMS Envelope Tracking)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateCompressorExpander, - runExpander_RMS, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Expansion Ratio", - (LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Decay (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1076, - "limit_peak", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Simple Limiter (Peak Envelope Tracking)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateLimiter, - runLimiter_Peak, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Decay (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1077, - "limit_rms", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Simple Limiter (RMS Envelope Tracking)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateLimiter, - runLimiter_RMS, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Output Envelope Decay (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.1f); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/filter.cpp b/plugins/LadspaEffect/cmt/src/filter.cpp deleted file mode 100644 index a9006fbb7cf..00000000000 --- a/plugins/LadspaEffect/cmt/src/filter.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* filter.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -#define SF_CUTOFF 0 -#define SF_INPUT 1 -#define SF_OUTPUT 2 - -/** Instance data for the OnePoll filter (one-poll, low or high - pass). We can get away with using this structure for both low- and - high-pass filters because the data stored is the same. Note that - the actual run() calls differ however. */ -class OnePollFilter : public CMT_PluginInstance { -private: - - LADSPA_Data m_fSampleRate; - LADSPA_Data m_fTwoPiOverSampleRate; - - LADSPA_Data m_fLastOutput; - LADSPA_Data m_fLastCutoff; - LADSPA_Data m_fAmountOfCurrent; - LADSPA_Data m_fAmountOfLast; - -public: - - OnePollFilter(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(3), - m_fSampleRate(LADSPA_Data(lSampleRate)), - m_fTwoPiOverSampleRate(LADSPA_Data((2 * M_PI) / lSampleRate)), - m_fLastCutoff(0), - m_fAmountOfCurrent(0), - m_fAmountOfLast(0) { - } - - friend void activateOnePollFilter(LADSPA_Handle Instance); - friend void runOnePollLowPassFilter(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runOnePollHighPassFilter(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -activateOnePollFilter(LADSPA_Handle Instance) { - ((OnePollFilter *)Instance)->m_fLastOutput = 0; -} - -/*****************************************************************************/ - -/** Run the LPF algorithm for a block of SampleCount samples. */ -void -runOnePollLowPassFilter(LADSPA_Handle Instance, - unsigned long SampleCount) { - - OnePollFilter * poFilter = (OnePollFilter *)Instance; - - LADSPA_Data * pfInput = poFilter->m_ppfPorts[SF_INPUT]; - LADSPA_Data * pfOutput = poFilter->m_ppfPorts[SF_OUTPUT]; - - if (poFilter->m_fLastCutoff != *(poFilter->m_ppfPorts[SF_CUTOFF])) { - poFilter->m_fLastCutoff = *(poFilter->m_ppfPorts[SF_CUTOFF]); - if (poFilter->m_fLastCutoff <= 0) { - /* Reject everything. */ - poFilter->m_fAmountOfCurrent = poFilter->m_fAmountOfLast = 0; - } - else if (poFilter->m_fLastCutoff > poFilter->m_fSampleRate * 0.5) { - /* Above Nyquist frequency. Let everything through. */ - poFilter->m_fAmountOfCurrent = 1; - poFilter->m_fAmountOfLast = 0; - } - else { - poFilter->m_fAmountOfLast = 0; - LADSPA_Data fComp = 2 - cos(poFilter->m_fTwoPiOverSampleRate - * poFilter->m_fLastCutoff); - poFilter->m_fAmountOfLast = fComp - (LADSPA_Data)sqrt(fComp * fComp - 1); - poFilter->m_fAmountOfCurrent = 1 - poFilter->m_fAmountOfLast; - } - } - - LADSPA_Data fAmountOfCurrent = poFilter->m_fAmountOfCurrent; - LADSPA_Data fAmountOfLast = poFilter->m_fAmountOfLast; - LADSPA_Data fLastOutput = poFilter->m_fLastOutput; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - *(pfOutput++) - = fLastOutput - = (fAmountOfCurrent * *(pfInput++) - + fAmountOfLast * fLastOutput); - } - - poFilter->m_fLastOutput = fLastOutput; -} - -/*****************************************************************************/ - -/** Run the HPF algorithm for a block of SampleCount samples. */ -void -runOnePollHighPassFilter(LADSPA_Handle Instance, - unsigned long SampleCount) { - - OnePollFilter * poFilter = (OnePollFilter *)Instance; - - LADSPA_Data * pfInput = poFilter->m_ppfPorts[SF_INPUT]; - LADSPA_Data * pfOutput = poFilter->m_ppfPorts[SF_OUTPUT]; - - if (poFilter->m_fLastCutoff != *(poFilter->m_ppfPorts[SF_CUTOFF])) { - poFilter->m_fLastCutoff = *(poFilter->m_ppfPorts[SF_CUTOFF]); - if (poFilter->m_fLastCutoff <= 0) { - /* Let everything through. */ - poFilter->m_fAmountOfCurrent = 1; - poFilter->m_fAmountOfLast = 0; - } - else if (poFilter->m_fLastCutoff > poFilter->m_fSampleRate * 0.5) { - /* Above Nyquist frequency. Reject everything. */ - poFilter->m_fAmountOfCurrent = poFilter->m_fAmountOfLast = 0; - } - else { - poFilter->m_fAmountOfLast = 0; - LADSPA_Data fComp = 2 - cos(poFilter->m_fTwoPiOverSampleRate - * poFilter->m_fLastCutoff); - poFilter->m_fAmountOfLast = fComp - (LADSPA_Data)sqrt(fComp * fComp - 1); - poFilter->m_fAmountOfCurrent = 1 - poFilter->m_fAmountOfLast; - } - - } - - LADSPA_Data fAmountOfCurrent = poFilter->m_fAmountOfCurrent; - LADSPA_Data fAmountOfLast = poFilter->m_fAmountOfLast; - LADSPA_Data fLastOutput = poFilter->m_fLastOutput; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - fLastOutput - = (fAmountOfCurrent * *pfInput - + fAmountOfLast * fLastOutput); - *(pfOutput++) = *(pfInput++) - fLastOutput; - } - - poFilter->m_fLastOutput = fLastOutput; -} - -/*****************************************************************************/ - -void -initialise_filter() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1051, - "lpf", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Low Pass Filter (One Pole)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateOnePollFilter, - runOnePollLowPassFilter, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Cutoff Frequency (Hz)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_440), - 0, - 0.5f); /* Nyquist frequency (half the sample rate) */ - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1052, - "hpf", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "High Pass Filter (One Pole)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateOnePollFilter, - runOnePollHighPassFilter, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Cutoff Frequency (Hz)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_440), - 0, - 0.5f); /* Nyquist frequency (half the sample rate) */ - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/allpass.cpp b/plugins/LadspaEffect/cmt/src/freeverb/Components/allpass.cpp deleted file mode 100644 index 850337e3a33..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/allpass.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Allpass filter implementation -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -#include "allpass.h" - -allpass::allpass() -{ - bufidx = 0; -} - -void allpass::setbuffer(float *buf, int size) -{ - buffer = buf; - bufsize = size; -} - -void allpass::mute() -{ - for (int i=0; i=bufsize) bufidx = 0; - - return output; -} - -#endif//_allpass - -//ends diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/comb.cpp b/plugins/LadspaEffect/cmt/src/freeverb/Components/comb.cpp deleted file mode 100644 index 62be706d705..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/comb.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Comb filter implementation -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -#include "comb.h" - -comb::comb() -{ - filterstore = 0; - bufidx = 0; -} - -void comb::setbuffer(float *buf, int size) -{ - buffer = buf; - bufsize = size; -} - -void comb::mute() -{ - for (int i=0; i=bufsize) bufidx = 0; - - return output; -} - -#endif //_comb_ - -//ends diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/denormals.h b/plugins/LadspaEffect/cmt/src/freeverb/Components/denormals.h deleted file mode 100644 index 01990916a53..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/denormals.h +++ /dev/null @@ -1,20 +0,0 @@ -// Macro for killing denormalled numbers -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// Based on IS_DENORMAL macro by Jon Watte -// This code is public domain - -#ifndef _denormals_ -#define _denormals_ - -/*#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f*/ -static void inline undenormalise(float *sample) -{ - if (((*(unsigned int*)sample) & 0x7f800000) == 0) - *sample = 0.0f; -} - -#endif//_denormals_ - -//ends diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp b/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp deleted file mode 100644 index 4f7fcd4d1e7..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Reverb model implementation -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -#include "revmodel.h" - -revmodel::revmodel( float sampleRatio ) : - m_sampleRatio( sampleRatio ) -{ - // Tie the components to their buffers - combL[0].setbuffer(bufcombL1,static_cast( combtuningL1 * m_sampleRatio )); - combR[0].setbuffer(bufcombR1,static_cast( combtuningR1 * m_sampleRatio )); - combL[1].setbuffer(bufcombL2,static_cast( combtuningL2 * m_sampleRatio )); - combR[1].setbuffer(bufcombR2,static_cast( combtuningR2 * m_sampleRatio )); - combL[2].setbuffer(bufcombL3,static_cast( combtuningL3 * m_sampleRatio )); - combR[2].setbuffer(bufcombR3,static_cast( combtuningR3 * m_sampleRatio )); - combL[3].setbuffer(bufcombL4,static_cast( combtuningL4 * m_sampleRatio )); - combR[3].setbuffer(bufcombR4,static_cast( combtuningR4 * m_sampleRatio )); - combL[4].setbuffer(bufcombL5,static_cast( combtuningL5 * m_sampleRatio )); - combR[4].setbuffer(bufcombR5,static_cast( combtuningR5 * m_sampleRatio )); - combL[5].setbuffer(bufcombL6,static_cast( combtuningL6 * m_sampleRatio )); - combR[5].setbuffer(bufcombR6,static_cast( combtuningR6 * m_sampleRatio )); - combL[6].setbuffer(bufcombL7,static_cast( combtuningL7 * m_sampleRatio )); - combR[6].setbuffer(bufcombR7,static_cast( combtuningR7 * m_sampleRatio )); - combL[7].setbuffer(bufcombL8,static_cast( combtuningL8 * m_sampleRatio )); - combR[7].setbuffer(bufcombR8,static_cast( combtuningR8 * m_sampleRatio )); - allpassL[0].setbuffer(bufallpassL1,static_cast( allpasstuningL1 * m_sampleRatio )); - allpassR[0].setbuffer(bufallpassR1,static_cast( allpasstuningR1 * m_sampleRatio )); - allpassL[1].setbuffer(bufallpassL2,static_cast( allpasstuningL2 * m_sampleRatio )); - allpassR[1].setbuffer(bufallpassR2,static_cast( allpasstuningR2 * m_sampleRatio )); - allpassL[2].setbuffer(bufallpassL3,static_cast( allpasstuningL3 * m_sampleRatio )); - allpassR[2].setbuffer(bufallpassR3,static_cast( allpasstuningR3 * m_sampleRatio )); - allpassL[3].setbuffer(bufallpassL4,static_cast( allpasstuningL4 * m_sampleRatio )); - allpassR[3].setbuffer(bufallpassR4,static_cast( allpasstuningR4 * m_sampleRatio )); - - // Set default values - allpassL[0].setfeedback(0.5f); - allpassR[0].setfeedback(0.5f); - allpassL[1].setfeedback(0.5f); - allpassR[1].setfeedback(0.5f); - allpassL[2].setfeedback(0.5f); - allpassR[2].setfeedback(0.5f); - allpassL[3].setfeedback(0.5f); - allpassR[3].setfeedback(0.5f); - setwet(initialwet); - setroomsize(initialroom); - setdry(initialdry); - setdamp(initialdamp); - setwidth(initialwidth); - setmode(initialmode); - - // Buffer will be full of rubbish - so we MUST mute them - mute(); -} - -void revmodel::mute() -{ - int i; - - if (getmode() >= freezemode) - return; - - for (i=0;i 0) - { - outL = outR = 0; - input = (*inputL + *inputR) * gain; - - // Accumulate comb filters in parallel - for(i=0; i 0) - { - outL = outR = 0; - input = (*inputL + *inputR) * gain; - - // Accumulate comb filters in parallel - for(i=0; i= freezemode) - { - roomsize1 = 1; - damp1 = 0; - gain = muted; - } - else - { - roomsize1 = roomsize; - damp1 = damp; - gain = fixedgain; - } - - for(i=0; i= freezemode) - return 1; - else - return 0; -} - -//ends diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h b/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h deleted file mode 100644 index 9846eb40de3..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h +++ /dev/null @@ -1,91 +0,0 @@ -// Reverb model declaration -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -#ifndef _revmodel_ -#define _revmodel_ - -#include "comb.h" -#include "allpass.h" -#include "tuning.h" - -const int maxSampleRatio = 18; // enough for largest possible samplerate, 8 * 96000 - -class revmodel -{ -public: - revmodel( float sampleRatio ); - void mute(); - void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); - void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); - void setroomsize(float value); - float getroomsize(); - void setdamp(float value); - float getdamp(); - void setwet(float value); - float getwet(); - void setdry(float value); - float getdry(); - void setwidth(float value); - float getwidth(); - void setmode(float value); - float getmode(); -private: - void update(); -private: - float gain; - float roomsize,roomsize1; - float damp,damp1; - float wet,wet1,wet2; - float dry; - float width; - float mode; - - float m_sampleRatio; - - // The following are all declared inline - // to remove the need for dynamic allocation - // with its subsequent error-checking messiness - - // Comb filters - comb combL[numcombs]; - comb combR[numcombs]; - - // Allpass filters - allpass allpassL[numallpasses]; - allpass allpassR[numallpasses]; - - // Buffers for the combs - float bufcombL1[combtuningL1 * maxSampleRatio]; - float bufcombR1[combtuningR1 * maxSampleRatio]; - float bufcombL2[combtuningL2 * maxSampleRatio]; - float bufcombR2[combtuningR2 * maxSampleRatio]; - float bufcombL3[combtuningL3 * maxSampleRatio]; - float bufcombR3[combtuningR3 * maxSampleRatio]; - float bufcombL4[combtuningL4 * maxSampleRatio]; - float bufcombR4[ combtuningR4 * maxSampleRatio ]; - float bufcombL5[ combtuningL5 * maxSampleRatio ]; - float bufcombR5[ combtuningR5 * maxSampleRatio ]; - float bufcombL6[ combtuningL6 * maxSampleRatio ]; - float bufcombR6[ combtuningR6 * maxSampleRatio ]; - float bufcombL7[ combtuningL7 * maxSampleRatio ]; - float bufcombR7[ combtuningR7 * maxSampleRatio ]; - float bufcombL8[ combtuningL8 * maxSampleRatio ]; - float bufcombR8[ combtuningR8 * maxSampleRatio ]; - - // Buffers for the allpasses - float bufallpassL1[ allpasstuningL1 * maxSampleRatio ]; - float bufallpassR1[ allpasstuningR1 * maxSampleRatio ]; - float bufallpassL2[ allpasstuningL2 * maxSampleRatio ]; - float bufallpassR2[ allpasstuningR2 * maxSampleRatio ]; - float bufallpassL3[ allpasstuningL3 * maxSampleRatio ]; - float bufallpassR3[ allpasstuningR3 * maxSampleRatio ]; - float bufallpassL4[ allpasstuningL4 * maxSampleRatio ]; - float bufallpassR4[ allpasstuningR4 * maxSampleRatio ]; -}; - -#endif//_revmodel_ - -//ends diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/tuning.h b/plugins/LadspaEffect/cmt/src/freeverb/Components/tuning.h deleted file mode 100644 index ced89252813..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/tuning.h +++ /dev/null @@ -1,60 +0,0 @@ -// Reverb model tuning values -// -// Written by Jezar at Dreampoint, June 2000 -// http://www.dreampoint.co.uk -// This code is public domain - -#ifndef _tuning_ -#define _tuning_ - -const int numcombs = 8; -const int numallpasses = 4; -const float muted = 0; -const float fixedgain = 0.015f; -const float scalewet = 3; -const float scaledry = 2; -const float scaledamp = 0.4f; -const float scaleroom = 0.28f; -const float offsetroom = 0.7f; -const float initialroom = 0.5f; -const float initialdamp = 0.5f; -const float initialwet = 1/scalewet; -const float initialdry = 0; -const float initialwidth = 1; -const float initialmode = 0; -const float freezemode = 0.5f; -const int stereospread = 23; - -// These values assume 44.1KHz sample rate -// they will probably be OK for 48KHz sample rate -// but would need scaling for 96KHz (or other) sample rates. -// The values were obtained by listening tests. -const int combtuningL1 = 1116; -const int combtuningR1 = 1116+stereospread; -const int combtuningL2 = 1188; -const int combtuningR2 = 1188+stereospread; -const int combtuningL3 = 1277; -const int combtuningR3 = 1277+stereospread; -const int combtuningL4 = 1356; -const int combtuningR4 = 1356+stereospread; -const int combtuningL5 = 1422; -const int combtuningR5 = 1422+stereospread; -const int combtuningL6 = 1491; -const int combtuningR6 = 1491+stereospread; -const int combtuningL7 = 1557; -const int combtuningR7 = 1557+stereospread; -const int combtuningL8 = 1617; -const int combtuningR8 = 1617+stereospread; -const int allpasstuningL1 = 556; -const int allpasstuningR1 = 556+stereospread; -const int allpasstuningL2 = 441; -const int allpasstuningR2 = 441+stereospread; -const int allpasstuningL3 = 341; -const int allpasstuningR3 = 341+stereospread; -const int allpasstuningL4 = 225; -const int allpasstuningR4 = 225+stereospread; - -#endif//_tuning_ - -//ends - diff --git a/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp b/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp deleted file mode 100644 index 418e4d91c47..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* freeverb.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. Freeverb is also Copyright (C) 2000 - Jezar. Richard may be contacted at richard@muse.demon.co.uk. [V1 - Ported to LADSPA 15/7/2000 Richard W.E. Furse, V3 ported to CMT - 4/11/2000.] - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "../cmt.h" -#include "Components/revmodel.h" - -/*****************************************************************************/ - -enum { - - FV_Input1 = 0, - FV_Input2, - FV_Output1, - FV_Output2, - FV_Mode, - FV_RoomSize, - FV_Damping, - FV_Wet, - FV_Dry, - FV_Width, - - FV_NumPorts - -}; - -/*****************************************************************************/ - -/** This plugin wraps Jezar's Freeverb free reverberation module - (version 3). */ -class Freeverb3 : public CMT_PluginInstance, public revmodel { -public: - - Freeverb3(const LADSPA_Descriptor *, unsigned long lSampleRate) - : CMT_PluginInstance(FV_NumPorts), - revmodel( (float) lSampleRate / 44100.0f ) - {} - - friend void activateFreeverb3(LADSPA_Handle Instance); - friend void runFreeverb3(LADSPA_Handle Instance, - unsigned long SampleCount); -}; - -/*****************************************************************************/ - -void -activateFreeverb3(LADSPA_Handle Instance) { - Freeverb3 * poFreeverb = (Freeverb3 *)Instance; - poFreeverb->mute(); -} - -/*****************************************************************************/ - -void -runFreeverb3(LADSPA_Handle Instance, - const unsigned long SampleCount) { - - Freeverb3 * poFreeverb = ((Freeverb3 *)Instance); - - /* Handle control ports. Note that this isn't especially efficient - because of the way the update() code works in revmodel.cpp, but - at least this approach allows Freeverb to work with almost no - code changes. */ - - if (*(poFreeverb->m_ppfPorts[FV_Mode]) > 0) - poFreeverb->setmode(1); - else - poFreeverb->setmode(0); - poFreeverb->setdamp(*(poFreeverb->m_ppfPorts[FV_Damping])); - poFreeverb->setwet(*(poFreeverb->m_ppfPorts[FV_Wet])); - poFreeverb->setdry(*(poFreeverb->m_ppfPorts[FV_Dry])); - poFreeverb->setroomsize(*(poFreeverb->m_ppfPorts[FV_RoomSize])); - poFreeverb->setwidth(*(poFreeverb->m_ppfPorts[FV_Width])); - - /* Connect to audio ports and run. */ - - poFreeverb->processreplace(poFreeverb->m_ppfPorts[FV_Input1], - poFreeverb->m_ppfPorts[FV_Input2], - poFreeverb->m_ppfPorts[FV_Output1], - poFreeverb->m_ppfPorts[FV_Output2], - SampleCount, - 1); -} - -/*****************************************************************************/ - -void -initialise_freeverb3() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1123, - "freeverb3", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Freeverb (Version 3)", - CMT_MAKER("Jezar at Dreampoint, ported by Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Jezar at Dreampoint"), - NULL, - CMT_Instantiate, - activateFreeverb3, - runFreeverb3, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Left)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input (Right)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Left)"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output (Right)"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Freeze Mode", - (LADSPA_HINT_TOGGLED - | LADSPA_HINT_DEFAULT_0), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Room Size", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Damping", - (LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Wet Level", - (LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Dry Level", - (LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 1); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Width", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - 1); - - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/freeverb/readme.txt b/plugins/LadspaEffect/cmt/src/freeverb/readme.txt deleted file mode 100644 index 1cc5e8b0097..00000000000 --- a/plugins/LadspaEffect/cmt/src/freeverb/readme.txt +++ /dev/null @@ -1,67 +0,0 @@ -Freeverb - Free, studio-quality reverb SOURCE CODE in the public domain ------------------------------------------------------------------------ - -Written by Jezar at Dreampoint - http://www.dreampoint.co.uk - - -Introduction ------------- - -Hello. - -I'll try to keep this "readme" reasonably small. There are few things in the world that I hate more than long "readme" files. Except "coding conventions" - but more on that later... - -In this zip file you will find two folders of C++ source code: - -"Components" - Contains files that should clean-compile ON ANY TYPE OF COMPUTER OR SYSTEM WHATSOEVER. It should not be necessary to make ANY changes to these files to get them to compile, except to make up for inadequacies of certain compilers. These files create three classes - a comb filter, an allpass filter, and a reverb model made up of a number of instances of the filters, with some features to control the filters at a macro level. You will need to link these classes into another program that interfaces with them. The files in the components drawer are completely independant, and can be built without dependancies on anything else. Because of the simple interface, it should be possible to interface these files to any system - VST, DirectX, anything - without changing them AT ALL. - -"FreeverbVST" - Contains a Steinberg VST implementation of this version of Freeverb, using the components in (surprise) the components folder. It was built on a PC but may compile properly for the Macintosh with no problems. I don't know - I don't have a Macintosh. If you've figured out how to compile the examples in the Steinberg VST Development Kit, then you should easilly figure out how to bring the files into a project and get it working in a few minutes. It should be very simple. - -Note that this version of Freeverb doesn't contain predelay, or any EQ. I thought that might make it difficult to understand the "reverb" part of the code. Once you figure out how Freeverb works, you should find it trivial to add such features with little CPU overhead. - -Also, the code in this version of Freeverb has been optimised. This has changed the sound *slightly*, but not significantly compared to how much processing power it saves. - -Finally, note that there is also a built copy of this version of Freeverb called "Freeverb3.dll" - this is a VST plugin for the PC. If you want a version for the Mac or anything else, then you'll need to build it yourself from the code. - - -Technical Explanation ---------------------- - -Freeverb is a simple implementation of the standard Schroeder/Moorer reverb model. I guess the only reason why it sounds better than other reverbs, is simply because I spent a long while doing listening tests in order to create the values found in "tuning.h". It uses 8 comb filters on both the left and right channels), and you might possibly be able to get away with less if CPU power is a serious constraint for you. It then feeds the result of the reverb through 4 allpass filters on both the left and right channels. These "smooth" the sound. Adding more than four allpasses doesn't seem to add anything significant to the sound, and if you use less, the sound gets a bit "grainy". The filters on the right channel are slightly detuned compared to the left channel in order to create a stereo effect. - -Hopefully, you should find the code in the components drawer a model of brevity and clarity. Notice that I don't use any "coding conventions". Personally, I think that coding conventions suck. They are meant to make the code "clearer", but they inevitably do the complete opposite, making the code completely unfathomable. Anyone whose done Windows programming with its - frankly stupid - "Hungarian notation" will know exactly what I mean. Coding conventions typically promote issues that are irrelevant up to the status of appearing supremely important. It may have helped back people in the days when compilers where somewhat feeble in their type-safety, but not in the new millenium with advanced C++ compilers. - -Imagine if we rewrote the English language to conform to coding conventions. After all, The arguments should be just as valid for the English language as they are for a computer language. For example, we could put a lower-case "n" in front of every noun, a lower-case "p" in front of a persons name, a lower-case "v" in front of every verb, and a lower-case "a" in front of every adjective. Can you imagine what the English language would look like? All in the name of "clarity". It's just as stupid to do this for computer code as it would be to do it for the English language. I hope that the code for Freeverb in the components drawer demonstrates this, and helps start a movement back towards sanity in coding practices. - - -Background ----------- - -Why is the Freeverb code now public domain? Simple. I only intended to create Freeverb to provide me and my friends with studio-quality reverb for free. I never intended to make any money out of it. However, I simply do not have the time to develop it any further. I'm working on a "concept album" at the moment, and I'll never finish it if I spend any more time programming. - -In any case, I make more far money as a contract programmer - making Mobile Internet products - than I ever could writing plugins, so it simply doesn't make financial sense for me to spend any more time on it. - -Rather than give Freeverb to any particular individual or organisation to profit from it, I've decided to give it away to the internet community at large, so that quality, FREE (or at the very least, low-cost) reverbs can be developed for all platforms. - -Feel free to use the source code for Freeverb in any of your own products, whether they are also available for free, or even if they are commercial - I really don't mind. You may do with the code whatever you wish. If you use it in a product (whether commercial or not), it would be very nice of you, if you were to send me a copy of your product - although I appreciate that this isn't always possible in all circumstances. - -HOWEVER, please don't bug me with questions about how to use this code. I gave away Freeverb because I don't have time to maintain it. That means I *certainly* don't have time to answer questions about the source code, so please don't email questions to me. I *will* ignore them. If you can't figure the code for Freeverb out - then find somebody who can. I hope that either way, you enjoy experimenting with it. - - -Disclaimer ----------- - -This software and source code is given away for free, without any warranties of any kind. It has been given away to the internet community as a free gift, so please treat it in the same spirit. - - -I hope this code is useful and interesting to you all! -I hope you have lots of fun experimenting with it and make good products! - -Very best regards, -Jezar. -Technology Consultant -Dreampoint Design and Engineering -http://www.dreampoint.co.uk - - -//ends diff --git a/plugins/LadspaEffect/cmt/src/grain.cpp b/plugins/LadspaEffect/cmt/src/grain.cpp deleted file mode 100644 index 3ec25436e8d..00000000000 --- a/plugins/LadspaEffect/cmt/src/grain.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* grain.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" -#include "utils.h" - -/*****************************************************************************/ - -/** Period (in seconds) from which grains are selected. */ -#define GRAIN_MAXIMUM_HISTORY 6 -#define GRAIN_MAXIMUM_BLOCK 1 /* (seconds) */ - -#define GRAIN_MAXIMUM_SCATTER (GRAIN_MAXIMUM_HISTORY - GRAIN_MAXIMUM_BLOCK) -#define GRAIN_MAXIMUM_LENGTH (GRAIN_MAXIMUM_HISTORY - GRAIN_MAXIMUM_BLOCK) - -/** What quality should we require when sampling the normal - distribution to generate grain counts? */ -#define GRAIN_NORMAL_RV_QUALITY 16 - -/*****************************************************************************/ - -/** Pointers to this can be used as linked list of grains. */ -class Grain { -private: - - long m_lReadPointer; - long m_lGrainLength; - long m_lAttackTime; - - long m_lRunTime; - - bool m_bFinished; - - LADSPA_Data m_fAttackSlope; - LADSPA_Data m_fDecaySlope; - -public: - - Grain(const long lReadPointer, - const long lGrainLength, - const long lAttackTime) - : m_lReadPointer(lReadPointer), - m_lGrainLength(lGrainLength), - m_lAttackTime(lAttackTime), - m_lRunTime(0), - m_bFinished(false) { - if (lAttackTime <= 0) { - m_fAttackSlope = 0; - m_fDecaySlope = LADSPA_Data(1.0 / lGrainLength); - } - else { - m_fAttackSlope = LADSPA_Data(1.0 / lAttackTime); - if (lAttackTime >= lGrainLength) - m_fDecaySlope = 0; - else - m_fDecaySlope = LADSPA_Data(1.0 / (lGrainLength - lAttackTime)); - } - } - - bool isFinished() const { - return m_bFinished; - } - - /** NULL if end of grain list. */ - Grain * m_poNextGrain; - - void run(const unsigned long lSampleCount, - float * pfOutput, - const float * pfHistoryBuffer, - const unsigned long lHistoryBufferSize) { - - LADSPA_Data fAmp; - if (m_lRunTime < m_lAttackTime) - fAmp = m_fAttackSlope * m_lRunTime; - else - fAmp = m_fDecaySlope * (m_lGrainLength - m_lRunTime); - - for (unsigned long lSampleIndex = 0; - lSampleIndex < lSampleCount; - lSampleIndex++) { - - if (fAmp < 0) { - m_bFinished = true; - break; - } - - *(pfOutput++) += fAmp * pfHistoryBuffer[m_lReadPointer]; - - m_lReadPointer = (m_lReadPointer + 1) & (lHistoryBufferSize - 1); - - if (m_lRunTime < m_lAttackTime) - fAmp += m_fAttackSlope; - else - fAmp -= m_fDecaySlope; - - m_lRunTime++; - } - } -}; - -/*****************************************************************************/ - -#define GRN_INPUT 0 -#define GRN_OUTPUT 1 -#define GRN_DENSITY 2 -#define GRN_SCATTER 3 -#define GRN_GRAIN_LENGTH 4 -#define GRN_GRAIN_ATTACK 5 - -/** This plugin cuts an audio stream up and uses it to generate a - granular texture. */ -class GrainScatter : public CMT_PluginInstance { -private: - - Grain * m_poCurrentGrains; - - long m_lSampleRate; - - LADSPA_Data * m_pfBuffer; - - /** Buffer size, a power of two. */ - unsigned long m_lBufferSize; - - /** Write pointer in buffer. */ - unsigned long m_lWritePointer; - -public: - - GrainScatter(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(6), - m_poCurrentGrains(NULL), - m_lSampleRate(lSampleRate) { - /* Buffer size is a power of two bigger than max delay time. */ - unsigned long lMinimumBufferSize - = (unsigned long)((LADSPA_Data)lSampleRate * GRAIN_MAXIMUM_HISTORY); - m_lBufferSize = 1; - while (m_lBufferSize < lMinimumBufferSize) - m_lBufferSize <<= 1; - m_pfBuffer = new LADSPA_Data[m_lBufferSize]; - } - - ~GrainScatter() { - delete [] m_pfBuffer; - } - - friend void activateGrainScatter(LADSPA_Handle Instance); - friend void runGrainScatter(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -/** Initialise and activate a plugin instance. */ -void -activateGrainScatter(LADSPA_Handle Instance) { - - GrainScatter * poGrainScatter = (GrainScatter *)Instance; - - /* Need to reset the delay history in this function rather than - instantiate() in case deactivate() followed by activate() have - been called to reinitialise a delay line. */ - memset(poGrainScatter->m_pfBuffer, - 0, - sizeof(LADSPA_Data) * poGrainScatter->m_lBufferSize); - - poGrainScatter->m_lWritePointer = 0; -} - -/*****************************************************************************/ - -void -runGrainScatter(LADSPA_Handle Instance, - unsigned long SampleCount) { - - GrainScatter * poGrainScatter = (GrainScatter *)Instance; - - LADSPA_Data * pfInput = poGrainScatter->m_ppfPorts[GRN_INPUT]; - LADSPA_Data * pfOutput = poGrainScatter->m_ppfPorts[GRN_OUTPUT]; - - unsigned long lMaximumSampleCount - = (unsigned long)(poGrainScatter->m_lSampleRate - * GRAIN_MAXIMUM_BLOCK); - - if (SampleCount > lMaximumSampleCount) { - - /* We're beyond our capabilities. We're going to run out of delay - line for a large grain. Divide and conquer. */ - - runGrainScatter(Instance, lMaximumSampleCount); - - poGrainScatter->m_ppfPorts[GRN_INPUT] += lMaximumSampleCount; - poGrainScatter->m_ppfPorts[GRN_OUTPUT] += lMaximumSampleCount; - runGrainScatter(Instance, SampleCount - lMaximumSampleCount); - poGrainScatter->m_ppfPorts[GRN_INPUT] = pfInput; - poGrainScatter->m_ppfPorts[GRN_OUTPUT] = pfOutput; - - } - else { - - /* Move the delay line along. */ - if (poGrainScatter->m_lWritePointer - + SampleCount - > poGrainScatter->m_lBufferSize) { - memcpy(poGrainScatter->m_pfBuffer + poGrainScatter->m_lWritePointer, - pfInput, - sizeof(LADSPA_Data) * (poGrainScatter->m_lBufferSize - - poGrainScatter->m_lWritePointer)); - memcpy(poGrainScatter->m_pfBuffer, - pfInput + (poGrainScatter->m_lBufferSize - - poGrainScatter->m_lWritePointer), - sizeof(LADSPA_Data) * (SampleCount - - (poGrainScatter->m_lBufferSize - - poGrainScatter->m_lWritePointer))); - } - else { - memcpy(poGrainScatter->m_pfBuffer + poGrainScatter->m_lWritePointer, - pfInput, - sizeof(LADSPA_Data) * SampleCount); - } - poGrainScatter->m_lWritePointer - = ((poGrainScatter->m_lWritePointer + SampleCount) - & (poGrainScatter->m_lBufferSize - 1)); - - /* Empty the output buffer. */ - memset(pfOutput, 0, SampleCount * sizeof(LADSPA_Data)); - - /* Process current grains. */ - Grain ** ppoGrainReference = &(poGrainScatter->m_poCurrentGrains); - while (*ppoGrainReference != NULL) { - (*ppoGrainReference)->run(SampleCount, - pfOutput, - poGrainScatter->m_pfBuffer, - poGrainScatter->m_lBufferSize); - if ((*ppoGrainReference)->isFinished()) { - Grain *poNextGrain = (*ppoGrainReference)->m_poNextGrain; - delete *ppoGrainReference; - *ppoGrainReference = poNextGrain; - } - else { - ppoGrainReference = &((*ppoGrainReference)->m_poNextGrain); - } - } - - LADSPA_Data fSampleRate = LADSPA_Data(poGrainScatter->m_lSampleRate); - LADSPA_Data fDensity - = BOUNDED_BELOW(*(poGrainScatter->m_ppfPorts[GRN_DENSITY]), - 0); - - /* We want to average fDensity new grains per second. We need to - use a RNG to generate a new grain count from the fraction of a - second we are dealing with. Use a normal distribution and - choose standard deviation also to be fDensity. This could be - separately parameterised but any guarantees could be confusing - given that individual grains are uniformly distributed within - the block. Note that fDensity isn't quite grains/sec as we - discard negative samples from the RV. */ - double dGrainCountRV_Mean = fDensity * SampleCount / fSampleRate; - double dGrainCountRV_SD = dGrainCountRV_Mean; - double dGrainCountRV = sampleNormalDistribution(dGrainCountRV_Mean, - dGrainCountRV_SD, - GRAIN_NORMAL_RV_QUALITY); - unsigned long lNewGrainCount = 0; - if (dGrainCountRV > 0) - lNewGrainCount = (unsigned long)(0.5 + dGrainCountRV); - if (lNewGrainCount > 0) { - - LADSPA_Data fScatter - = BOUNDED(*(poGrainScatter->m_ppfPorts[GRN_SCATTER]), - 0, - GRAIN_MAXIMUM_SCATTER); - LADSPA_Data fGrainLength - = BOUNDED_BELOW(*(poGrainScatter->m_ppfPorts[GRN_GRAIN_LENGTH]), - 0); - LADSPA_Data fAttack - = BOUNDED_BELOW(*(poGrainScatter->m_ppfPorts[GRN_GRAIN_ATTACK]), - 0); - - long lScatterSampleWidth - = long(fSampleRate * fScatter) + 1; - long lGrainLength - = long(fSampleRate * fGrainLength); - long lAttackTime - = long(fSampleRate * fAttack); - - for (unsigned long lIndex = 0; lIndex < lNewGrainCount; lIndex++) { - - long lOffset = rand() % SampleCount; - - long lGrainReadPointer - = (poGrainScatter->m_lWritePointer - - SampleCount - + lOffset - - (rand() % lScatterSampleWidth)); - while (lGrainReadPointer < 0) - lGrainReadPointer += poGrainScatter->m_lBufferSize; - lGrainReadPointer &= (poGrainScatter->m_lBufferSize - 1); - - Grain * poNewGrain = new Grain(lGrainReadPointer, - lGrainLength, - lAttackTime); - - poNewGrain->m_poNextGrain = poGrainScatter->m_poCurrentGrains; - poGrainScatter->m_poCurrentGrains = poNewGrain; - - poNewGrain->run(SampleCount - lOffset, - pfOutput + lOffset, - poGrainScatter->m_pfBuffer, - poGrainScatter->m_lBufferSize); - } - } - } -} - -/*****************************************************************************/ - -void -initialise_grain() { - - CMT_Descriptor * psDescriptor = new CMT_Descriptor - (1096, - "grain_scatter", - 0, - "Granular Scatter Processor", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateGrainScatter, - runGrainScatter, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Density (Grains/s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 10); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Scatter (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, - GRAIN_MAXIMUM_SCATTER); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Grain Length (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.2); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Grain Attack (s)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 0.05); - - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/hardgate.cpp b/plugins/LadspaEffect/cmt/src/hardgate.cpp deleted file mode 100644 index a2f04b10e96..00000000000 --- a/plugins/LadspaEffect/cmt/src/hardgate.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* hardgate.cpp - - (c) 2002 Nathaniel Virgo - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -namespace hardgate { - - enum { - port_threshold = 0, - port_input = 1, - port_output = 2, - n_ports = 3 - }; - -/** This plugin sets its input signal to 0 if it falls below a threshold. */ - class Plugin : public CMT_PluginInstance { - public: - - Plugin(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(n_ports) { - } - - friend void run(LADSPA_Handle instance, - unsigned long sample_count); - - }; - - void run(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - - LADSPA_Data threshold = *pp->m_ppfPorts[port_threshold]; - LADSPA_Data * in = pp->m_ppfPorts[port_input]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - for ( unsigned long i = 0; i < sample_count ; ++i ) - { - LADSPA_Data insig = *(in++); - if ( insig < threshold && insig > -threshold ) - *(out++) = 0.0f; - else - *(out++) = insig; - } - } - - void - initialise() { - - CMT_Descriptor * d = new CMT_Descriptor - (1845, - "hard_gate", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Hard Gate", - CMT_MAKER("Nathaniel Virgo"), - CMT_COPYRIGHT("2002", "Nathaniel Virgo"), - NULL, - CMT_Instantiate, - NULL, - run, - NULL, - NULL, - NULL); - - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Threshold", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_0), - 0, - 1); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - - registerNewPluginDescriptor(d); - - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ - - - - - diff --git a/plugins/LadspaEffect/cmt/src/init.cpp b/plugins/LadspaEffect/cmt/src/init.cpp deleted file mode 100644 index f232840fa9c..00000000000 --- a/plugins/LadspaEffect/cmt/src/init.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* init.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -void initialise_modules(); -void finalise_modules(); - -/*****************************************************************************/ - -int -pluginNameComparator(const void * pvDescriptor1, const void * pvDescriptor2) { - - const CMT_Descriptor * psDescriptor1 - = *(const CMT_Descriptor **)pvDescriptor1; - const CMT_Descriptor * psDescriptor2 - = *(const CMT_Descriptor **)pvDescriptor2; - - int iResult = strcmp(psDescriptor1->Name, psDescriptor2->Name); - if (iResult < 0) - return -1; - else if (iResult > 0) - return 1; - else - return 0; -} - -/*****************************************************************************/ - -CMT_Descriptor ** g_ppsRegisteredDescriptors = NULL; -unsigned long g_lPluginCapacity = 0; -unsigned long g_lPluginCount = 0; - -/*****************************************************************************/ - -#define CAPACITY_STEP 20 - -void -registerNewPluginDescriptor(CMT_Descriptor * psDescriptor) { - if (g_lPluginCapacity == g_lPluginCount) { - /* Full. Enlarge capacity. */ - CMT_Descriptor ** ppsOldDescriptors - = g_ppsRegisteredDescriptors; - g_ppsRegisteredDescriptors - = new CMT_Descriptor_ptr[g_lPluginCapacity + CAPACITY_STEP]; - if (g_lPluginCapacity > 0) { - memcpy(g_ppsRegisteredDescriptors, - ppsOldDescriptors, - g_lPluginCapacity * sizeof(CMT_Descriptor_ptr)); - delete [] ppsOldDescriptors; - } - g_lPluginCapacity += CAPACITY_STEP; - } - g_ppsRegisteredDescriptors[g_lPluginCount++] = psDescriptor; -} - -/*****************************************************************************/ - -/** A global object of this class is used to perform initialisation - and shutdown services for the entire library. The constructor is - run when the library is loaded and the destructor when it is - unloaded. */ -class StartupShutdownHandler { -public: - - StartupShutdownHandler() { - initialise_modules(); - qsort(g_ppsRegisteredDescriptors, - g_lPluginCount, - sizeof(CMT_Descriptor_ptr), - pluginNameComparator); - } - - ~StartupShutdownHandler() { - if (g_ppsRegisteredDescriptors != NULL) { - for (unsigned long lIndex = 0; lIndex < g_lPluginCount; lIndex++) - delete g_ppsRegisteredDescriptors[lIndex]; - delete [] g_ppsRegisteredDescriptors; - } - finalise_modules(); - } - -} ; - -/*****************************************************************************/ - -extern "C" -{ - -const LADSPA_Descriptor * -ladspa_descriptor(unsigned long Index) { - - static StartupShutdownHandler handler; - - if (Index < g_lPluginCount) - return g_ppsRegisteredDescriptors[Index]; - else - return NULL; -} - -}; - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/ladspa_types.h b/plugins/LadspaEffect/cmt/src/ladspa_types.h deleted file mode 100644 index 31197b31bb2..00000000000 --- a/plugins/LadspaEffect/cmt/src/ladspa_types.h +++ /dev/null @@ -1,80 +0,0 @@ -/* ladspa_types.h - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -#ifndef CMT_LADSPA_TYPES_INCLUDED -#define CMT_LADSPA_TYPES_INCLUDED - -/*****************************************************************************/ - -#include - -/* Compatibility hack for version 1.0. */ -#ifndef LADSPA_VERSION_MAJOR -#define LADSPA_HINT_DEFAULT_MINIMUM 0x40 -#define LADSPA_HINT_DEFAULT_LOW 0x80 -#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0 -#define LADSPA_HINT_DEFAULT_HIGH 0x100 -#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 -#define LADSPA_HINT_DEFAULT_0 0x200 -#define LADSPA_HINT_DEFAULT_1 0x240 -#define LADSPA_HINT_DEFAULT_100 0x280 -#define LADSPA_HINT_DEFAULT_440 0x2C0 -#endif - -/*****************************************************************************/ - -typedef LADSPA_Handle (*LADSPA_Instantiate_Function) - (const struct _LADSPA_Descriptor * Descriptor, - unsigned long SampleRate); - -typedef void (*LADSPA_Connect_Port_Function) - (LADSPA_Handle Instance, - unsigned long Port, - LADSPA_Data * DataLocation); - -typedef void (*LADSPA_Activate_Function) - (LADSPA_Handle Instance); - -typedef void (*LADSPA_Run_Function) - (LADSPA_Handle Instance, - unsigned long SampleCount); - -typedef void (*LADSPA_Run_Adding_Function) - (LADSPA_Handle Instance, - unsigned long SampleCount); - -typedef void (*LADSPA_Set_Run_Adding_Gain_Function) - (LADSPA_Handle Instance, - LADSPA_Data Gain); - -typedef void (*LADSPA_Deactivate_Function) - (LADSPA_Handle Instance); - -typedef void (*LADSPA_Cleanup_Function) - (LADSPA_Handle Instance); - -typedef LADSPA_Data * LADSPA_Data_ptr; - -/*****************************************************************************/ - -#endif - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/lofi.cpp b/plugins/LadspaEffect/cmt/src/lofi.cpp deleted file mode 100644 index 67be8008d08..00000000000 --- a/plugins/LadspaEffect/cmt/src/lofi.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* lofi.cpp - - Lo Fi - Simulate low quality audio equipment - Copyright (c) 2001 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include "cmt.h" - -#define PORT_IN_LEFT 0 -#define PORT_IN_RIGHT 1 -#define PORT_OUT_LEFT 2 -#define PORT_OUT_RIGHT 3 -#define PORT_CRACKLING 4 -#define PORT_OVERLOADING 5 -#define PORT_BANDWIDTH 6 - -#define NUM_PORTS 7 - -#ifndef PI -#define PI 3.14159265358979 -#endif - -#ifndef MIN -#define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -#ifndef MAX -#define MAX(x,y) ((x)>(y)?(x):(y)) -#endif - -class Pop -{ -public: - float x; - float dx; - float amp; - float pwr; - Pop *next; - - Pop (float dx, float amp, float pwr, Pop *next); - ~Pop (); -}; - -Pop::Pop (float _dx, - float _amp, - float _pwr, - Pop *_next) - : x (0.0), dx (_dx), amp (_amp), pwr (_pwr), next (_next) -{ -} - -Pop::~Pop () -{ - delete next; -} - - -class Record -{ -public: - int rate; - int amount; /* 0 -> 100% */ - Pop *pops; - - LADSPA_Data process (LADSPA_Data sample); - void setAmount (int _amount); - - Record (int sample_rate); - ~Record (); -}; - -Record::Record (int sample_rate) - : rate (sample_rate), - amount (0), - pops (NULL) -{ -} - -Record::~Record () -{ - delete pops; -} - -static Pop * -record_pop_new (Record *record, - Pop *next) -{ - return new Pop ((rand () % 1500 + 500.0) / record->rate, - (rand () % 50) / 10000.0, - 1.0, - next); -} - -static Pop * -record_pop_loud_new (Record *record, - Pop *next) -{ - return new Pop ((rand () % 500 + 2500.0) / record->rate, - (rand () % 100) / 400.0 + 0.5, - (rand () % 50) / 20.0, - next); -} - -LADSPA_Data -Record::process (LADSPA_Data sample) -{ - Pop *pop; - Pop **pop_prev; - - /* Add some crackle */ - if (rand () % rate < rate * amount / 4000) - pops = record_pop_new (this, pops); - - /* Add some loud pops */ - if (rand () % (rate * 10) < rate * amount / 400000) - pops = record_pop_loud_new (this, pops); - - /* Compute pops */ - pop_prev = &pops; - pop = *pop_prev; - while (pop != NULL) - { - if (pop->x >= 0.5) - sample += (pow ((1.0 - pop->x) * 2.0, pop->pwr) - 0.5) * pop->amp; - else - sample += (pow (pop->x * 2.0, pop->pwr) - 0.5) * pop->amp; - - pop->x += pop->dx; - if (pop->x > 1.0) - { - *pop_prev = pop->next; - pop->next = NULL; - delete pop; - } - else - pop_prev = &pop->next; - - pop = *pop_prev; - } - - return sample; -} - -void -Record::setAmount (int _amount) -{ - amount = _amount; -} - - -class Compressor -{ -public: - int rate; - double amp; - double up; - double down; - float vol; - float clamp_hi; - float clamp_lo; - - LADSPA_Data process (LADSPA_Data sample); - void setClamp (float clamp); - - Compressor (int sample_rate, float clamp); -}; - -Compressor::Compressor (int sample_rate, float clamp) - : rate (sample_rate), amp (0.5), - up (1.0 / pow (0.5, 20.0 / sample_rate)), - down (pow (0.5, 50.0 / sample_rate)), - vol (0.5), clamp_hi (clamp), clamp_lo (1.0 / clamp) -{ -} - -LADSPA_Data -Compressor::process (LADSPA_Data sample) -{ - sample *= amp; - - if (fabs (sample) > vol) - { - amp *= down; - if (amp < clamp_lo) - amp = clamp_lo; - } - else - { - amp *= up; - if (amp > clamp_hi) - amp = clamp_hi; - } - - return sample; -} - -void -Compressor::setClamp (float clamp) -{ - clamp_hi = clamp; - clamp_lo = 1.0 / clamp; -} - - -static inline LADSPA_Data -distort (LADSPA_Data in) -{ - if (in > 0.0F) - return (in * 1.0F) / (in + 1.0F) * 2.0F; - else - return -(-in * 1.0F) / (-in + 1.0F) * 2.0F; -} - - -class BandwidthLimit -{ -public: - int rate; - float x; - float dx; - - void setFreq (float freq); - - LADSPA_Data process (LADSPA_Data sample); - BandwidthLimit (int _rate, float _freq); -}; - -BandwidthLimit::BandwidthLimit (int _rate, float _freq) - : rate (_rate), x (0.0), dx (_freq / _rate) -{ -} - -LADSPA_Data -BandwidthLimit::process (LADSPA_Data sample) -{ - if (sample >= x) - sample = MIN (x + dx, sample); - else - sample = MAX (x - dx, sample); - x = sample; - - return sample; -} - -void -BandwidthLimit::setFreq (float freq) -{ - dx = freq / rate; -} - - -class LoFi : public CMT_PluginInstance { - Record *record; - Compressor *compressor; - BandwidthLimit *bandwidth_l; - BandwidthLimit *bandwidth_r; - -public: - LoFi(const LADSPA_Descriptor *, - unsigned long s_rate) - : CMT_PluginInstance (NUM_PORTS), - record (new Record (s_rate * 2)), - compressor (new Compressor (s_rate * 2, 1.6)), - bandwidth_l (new BandwidthLimit (s_rate, 8000.0)), - bandwidth_r (new BandwidthLimit (s_rate, 8000.0)) { - } - - ~LoFi() { - delete bandwidth_l; - delete bandwidth_r; - delete compressor; - delete record; - } - - static void - activate (LADSPA_Handle Instance) { - LoFi *lofi = (LoFi*) Instance; - - lofi->bandwidth_l->setFreq (8000); - lofi->bandwidth_r->setFreq (8000); - lofi->compressor->setClamp (1.6); - lofi->record->setAmount (0); - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - LoFi *lofi = (LoFi*) Instance; - unsigned long i; - LADSPA_Data **ports = lofi->m_ppfPorts; - LADSPA_Data clamp; - - lofi->bandwidth_l->setFreq (ports[PORT_BANDWIDTH][0]); - lofi->bandwidth_r->setFreq (ports[PORT_BANDWIDTH][0]); - - if (ports[PORT_OVERLOADING][0] > 99.0) - clamp = 100.0; - else - clamp = 100.0 / (100.0 - ports[PORT_OVERLOADING][0]); - - lofi->compressor->setClamp (clamp); - - lofi->record->setAmount ((int) ports[PORT_CRACKLING][0]); - - for (i = 0; i < SampleCount; i++) - { - LADSPA_Data sample_l, sample_r; - - sample_l = ports[PORT_IN_LEFT][i]; - sample_r = ports[PORT_IN_RIGHT][i]; - - sample_l = lofi->compressor->process (sample_l); - sample_r = lofi->compressor->process (sample_r); - sample_l = lofi->bandwidth_l->process (sample_l); - sample_r = lofi->bandwidth_r->process (sample_r); - sample_l = distort (sample_l); - sample_r = distort (sample_r); - sample_l = lofi->record->process (sample_l); - sample_r = lofi->record->process (sample_r); - - ports[PORT_OUT_LEFT][i] = sample_l; - ports[PORT_OUT_RIGHT][i] = sample_r; - } - } -}; - - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "In (Left)", - "In (Right)", - - "Out (Left)", - "Out (Right)", - - "Crackling (%)", - "Powersupply Overloading (%)", - "Opamp Bandwidth Limiting (Hz)" -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 100.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 100.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 1.0, 10000.0 } -}; - -void -initialise_lofi() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1227, - "lofi", - 0 /* Sorry, this module is not RT capable, run() calls malloc() */, - "Lo Fi", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("2001", "David A. Bartold"), - NULL, - CMT_Instantiate, - LoFi::activate, - LoFi::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/logistic.cpp b/plugins/LadspaEffect/cmt/src/logistic.cpp deleted file mode 100644 index b2dfcb7fbf5..00000000000 --- a/plugins/LadspaEffect/cmt/src/logistic.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* logistic.cpp - - A sample-and-hold logistic map control generator - - (c) 2002 Nathaniel Virgo - - Part of the Computer Music Toolkit - a library of LADSPA plugins. - The Computer Music Toolkit is Copyright (C) 2000-2002 - Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -#include "pinknoise.h" -#include "utils.h" - -/*****************************************************************************/ - -namespace logistic { - - enum { - port_r = 0, - port_frequency = 1, - port_output = 2, - n_ports = 3 - }; - - /** This plugin uses the logistic map to generate periodic or - chaotic control signals. */ - class Plugin : public CMT_PluginInstance { - private: - LADSPA_Data sample_rate; - LADSPA_Data x; - unsigned counter; - public: - - Plugin(const LADSPA_Descriptor *, - unsigned long s_rate) : - CMT_PluginInstance(n_ports), - sample_rate(s_rate) { - } - - ~Plugin() { - } - - friend void activate(LADSPA_Handle instance); - - friend void run(LADSPA_Handle instance, - unsigned long sample_count); - }; - - void activate(LADSPA_Handle instance) { - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - p.x = 0.3; // arbitrary non-zero value. - } - - void run(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - LADSPA_Data r = *pp->m_ppfPorts[port_r]; - LADSPA_Data frequency = *pp->m_ppfPorts[port_frequency]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - frequency = BOUNDED_ABOVE(frequency,p.sample_rate); - r = BOUNDED_ABOVE(r,4); - unsigned remain = sample_count; - - if (frequency > 0) { - while (remain) { - unsigned jump_samples = (remain, - activate, - run, - NULL, - NULL, - NULL); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "\"r\" parameter", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MAXIMUM), - 2.9, 3.9999); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Step frequency", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0, 0.001); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(d); - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ - diff --git a/plugins/LadspaEffect/cmt/src/mixer.cpp b/plugins/LadspaEffect/cmt/src/mixer.cpp deleted file mode 100644 index 32d3ec4a74b..00000000000 --- a/plugins/LadspaEffect/cmt/src/mixer.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* mixer.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -/* The port numbers for the plugin: */ - -#define MIXER_INPUT1 0 -#define MIXER_INPUT2 1 -#define MIXER_OUTPUT 2 - -/** This plugin adds two signals together to produce a third. */ -class SimpleMixer : public CMT_PluginInstance { -public: - - SimpleMixer(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(3) { - } - - friend void runSimpleMixer(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -runSimpleMixer(LADSPA_Handle Instance, - unsigned long SampleCount) { - - SimpleMixer * poMixer = (SimpleMixer *)Instance; - - LADSPA_Data * pfInput1 = poMixer->m_ppfPorts[MIXER_INPUT1]; - LADSPA_Data * pfInput2 = poMixer->m_ppfPorts[MIXER_INPUT2]; - LADSPA_Data * pfOutput = poMixer->m_ppfPorts[MIXER_OUTPUT]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) - *(pfOutput++) = *(pfInput1++) + *(pfInput2++); -} - -/*****************************************************************************/ - -void -initialise_mixer() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1071, - "mixer", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Mixer (Stereo to Mono)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runSimpleMixer, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input 1"); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input 2"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/noise.cpp b/plugins/LadspaEffect/cmt/src/noise.cpp deleted file mode 100644 index bf6c5a89fc4..00000000000 --- a/plugins/LadspaEffect/cmt/src/noise.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* noise.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -/* The port numbers for the plugin: */ - -#define NOISE_AMPLITUDE 0 -#define NOISE_OUTPUT 1 - -/** Plugin that provides white noise output. This is provided by - calling rand() repeatedly. */ -class WhiteNoise : public CMT_PluginInstance { -private: - - LADSPA_Data m_fRunAddingGain; - -public: - - WhiteNoise(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(2) { - } - - friend void runWhiteNoise(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runWhiteNoiseAdding(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void setWhiteNoiseRunAddingGain(LADSPA_Handle Instance, - LADSPA_Data Gain); - -}; - -/*****************************************************************************/ - -void -runWhiteNoise(LADSPA_Handle Instance, - unsigned long SampleCount) { - - WhiteNoise * poNoise = (WhiteNoise *)Instance; - - LADSPA_Data fAmplitude = *(poNoise->m_ppfPorts[NOISE_AMPLITUDE]); - LADSPA_Data fScalar = fAmplitude * LADSPA_Data(2.0 / RAND_MAX); - - LADSPA_Data * pfOutput = poNoise->m_ppfPorts[NOISE_OUTPUT]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) - *(pfOutput++) = rand() * fScalar - fAmplitude; -} - -void -runWhiteNoiseAdding(LADSPA_Handle Instance, - unsigned long SampleCount) { - - WhiteNoise * poNoise = (WhiteNoise *)Instance; - - LADSPA_Data fAmplitude - = *(poNoise->m_ppfPorts[NOISE_AMPLITUDE]); - LADSPA_Data fScalar - = poNoise->m_fRunAddingGain * fAmplitude * LADSPA_Data(2.0 / RAND_MAX); - - LADSPA_Data * pfOutput = poNoise->m_ppfPorts[NOISE_OUTPUT]; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) - *(pfOutput++) += rand() * fScalar - fAmplitude; - -} - -void -setWhiteNoiseRunAddingGain(LADSPA_Handle Instance, - LADSPA_Data Gain) { -} - -/*****************************************************************************/ - -void -initialise_noise() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1069, - "noise_source_white", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Noise Source (White)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runWhiteNoise, - runWhiteNoiseAdding, - setWhiteNoiseRunAddingGain, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Amplitude", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/null.cpp b/plugins/LadspaEffect/cmt/src/null.cpp deleted file mode 100644 index afae4f48c67..00000000000 --- a/plugins/LadspaEffect/cmt/src/null.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* null.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -/* The port numbers for the plugin: */ - -#define NULL_PORT 0 - -/** This plugin can be used to take care of unwanted connections in a - host's plugin network by generating zero data and audio or - accepting (but ignoring) data and audio. */ -class NullPlugin : public CMT_PluginInstance { -public: - - NullPlugin(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(1) { - } - - friend void runNull_Nop(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runNull_OutputAudio(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runNull_OutputControl(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -#define IDENTITY_INPUT 0 -#define IDENTITY_OUTPUT 1 - -/* This plugin passes its input to its output. There are audio and - control varieties. */ -class IdentityPlugin : public CMT_PluginInstance { -public: - - IdentityPlugin(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(2) { - } - - friend void runIdentity_Audio(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runIdentity_Control(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -runNull_Nop(LADSPA_Handle Instance, - unsigned long SampleCount) { - /* Nothing to do. */ -} - -/*****************************************************************************/ - -void -runNull_OutputAudio(LADSPA_Handle Instance, - unsigned long SampleCount) { - NullPlugin * poPlugin = (NullPlugin *)Instance; - memset(poPlugin->m_ppfPorts[NULL_PORT], - 0, - sizeof(LADSPA_Data) * SampleCount); -} - -/*****************************************************************************/ - -void -runNull_OutputControl(LADSPA_Handle Instance, - unsigned long) { - NullPlugin * poPlugin = (NullPlugin *)Instance; - *(poPlugin->m_ppfPorts[NULL_PORT]) = 0; -} - -/*****************************************************************************/ - -void -runIdentity_Audio(LADSPA_Handle Instance, - unsigned long SampleCount) { - IdentityPlugin * poPlugin = (IdentityPlugin *)Instance; - if (poPlugin->m_ppfPorts[IDENTITY_OUTPUT] - != poPlugin->m_ppfPorts[IDENTITY_INPUT]) - memcpy(poPlugin->m_ppfPorts[IDENTITY_OUTPUT], - poPlugin->m_ppfPorts[IDENTITY_INPUT], - sizeof(LADSPA_Data) * SampleCount); -} - -/*****************************************************************************/ - -void -runIdentity_Control(LADSPA_Handle Instance, - unsigned long) { - IdentityPlugin * poPlugin = (IdentityPlugin *)Instance; - *(poPlugin->m_ppfPorts[IDENTITY_OUTPUT]) - = *(poPlugin->m_ppfPorts[IDENTITY_INPUT]); -} - -/*****************************************************************************/ - -void -initialise_null() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1083, - "null_ci", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Null (Control Input)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runNull_Nop, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Input"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1084, - "null_ai", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Null (Audio Input)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runNull_Nop, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1085, - "null_co", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Null (Control Output)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runNull_OutputControl, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1086, - "null_ao", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Null (Audio Output)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runNull_OutputAudio, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1098, - "identity_audio", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Identity (Audio)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runIdentity_Audio, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1099, - "identity_control", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Identity (Control)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runIdentity_Control, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/organ.cpp b/plugins/LadspaEffect/cmt/src/organ.cpp deleted file mode 100644 index f05d6b02ffb..00000000000 --- a/plugins/LadspaEffect/cmt/src/organ.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* organ.cpp - - Organ - Additive Organ Synthesizer Voice - Copyright (c) 1999, 2000 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include "cmt.h" - -#define PORT_OUT 0 -#define PORT_GATE 1 -#define PORT_VELOCITY 2 -#define PORT_FREQ 3 -#define PORT_BRASS 4 -#define PORT_FLUTE 5 -#define PORT_REED 6 -#define PORT_HARM0 7 -#define PORT_HARM1 8 -#define PORT_HARM2 9 -#define PORT_HARM3 10 -#define PORT_HARM4 11 -#define PORT_HARM5 12 -#define PORT_ATTACK_LO 13 -#define PORT_DECAY_LO 14 -#define PORT_SUSTAIN_LO 15 -#define PORT_RELEASE_LO 16 -#define PORT_ATTACK_HI 17 -#define PORT_DECAY_HI 18 -#define PORT_SUSTAIN_HI 19 -#define PORT_RELEASE_HI 20 - -#define NUM_PORTS 21 - -#define RESOLUTION 16384 - -#ifndef PI -#define PI 3.14159265358979 -#endif - -typedef struct Envelope -{ - int envelope_decay; - double envelope; - - Envelope () : envelope_decay (0), envelope (0.0) {} -} Envelope; - -static LADSPA_Data *g_sine_table; -static LADSPA_Data *g_triangle_table; -static LADSPA_Data *g_pulse_table; -static int ref_count; - -class Organ : CMT_PluginInstance -{ - LADSPA_Data sample_rate; - - Envelope env0; - Envelope env1; - - unsigned long harm0_accum; - unsigned long harm1_accum; - unsigned long harm2_accum; - unsigned long harm3_accum; - unsigned long harm4_accum; - unsigned long harm5_accum; - - public: - - Organ(const LADSPA_Descriptor * Descriptor, - unsigned long SampleRate) - : CMT_PluginInstance(NUM_PORTS), - sample_rate(SampleRate), - harm0_accum(0), harm1_accum(0), - harm2_accum(0), harm3_accum(0), - harm4_accum(0), harm5_accum(0) { - if (ref_count++ == 0) - { - int size = RESOLUTION; - int half = size / 2; - int slope = size / 10; - int i; - - /* Initialize sine table. */ - g_sine_table = new LADSPA_Data[size]; - for (i = 0; i < size; i++) - g_sine_table[i] = sin ((i * 2.0 * PI) / size) / 6.0; - - /* Initialize triangle table. */ - g_triangle_table = new LADSPA_Data[size]; - for (i = 0; i < half; i++) - g_triangle_table[i] = (4.0 / size * i - 1.0) / 6.0; - for (; i < size; i++) - g_triangle_table[i] = (4.0 / size * (size - i) - 1.0) / 6.0; - - /* Initialize pulse table. */ - g_pulse_table = new LADSPA_Data[size]; - for (i = 0; i < slope; i++) - g_pulse_table[i] = ((double) -i) / slope / 6.0; - for (; i < half - slope; i++) - g_pulse_table[i] = -1.0 / 6.0; - for (; i < half + slope; i++) - g_pulse_table[i] = ((double) i - half) / slope / 6.0; - for (; i < size - slope; i++) - g_pulse_table[i] = 1.0 / 6.0; - for (; i < size; i++) - g_pulse_table[i] = ((double) size - i) / slope / 6.0; - } - } - - ~Organ () { - if (--ref_count == 0) - { - delete[] g_pulse_table; - delete[] g_triangle_table; - delete[] g_sine_table; - } - } - - static inline LADSPA_Data - table_pos (LADSPA_Data *table, - unsigned long freq_256, - unsigned long *accum) { - *accum += freq_256; - while (*accum >= RESOLUTION * 256) - *accum -= RESOLUTION * 256; - - return table[*accum >> 8]; - } - - static inline LADSPA_Data - envelope(Envelope *env, - int gate, - LADSPA_Data attack, - LADSPA_Data decay, - LADSPA_Data sustain, - LADSPA_Data release) - { - if (gate) - if (env->envelope_decay == 0) - { - env->envelope += (1.0F - env->envelope) * attack; - if (env->envelope >= 0.95F) - env->envelope_decay = 1; - } - else - env->envelope += (sustain - env->envelope) * decay; - else - env->envelope += -env->envelope * release; - - return env->envelope; - } - - static inline LADSPA_Data - multiplier(Organ *organ, - LADSPA_Data value) { - return 1.0 - pow (0.05, 1.0 / (organ->sample_rate * value)); - } - - static void - activate(LADSPA_Handle Instance) { - Organ *organ = (Organ*) Instance; - - organ->env0.envelope_decay = 0; - organ->env0.envelope = 0.0; - organ->env1.envelope_decay = 0; - organ->env1.envelope = 0.0; - organ->harm0_accum = 0; - organ->harm1_accum = 0; - organ->harm2_accum = 0; - organ->harm3_accum = 0; - organ->harm4_accum = 0; - organ->harm5_accum = 0; - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - Organ *organ = (Organ*) Instance; - unsigned long i; - LADSPA_Data **ports; - LADSPA_Data *sine_table; - LADSPA_Data *reed_table; - LADSPA_Data *flute_table; - unsigned long freq_256; - unsigned long freq_256_harm0, freq_256_harm1; - unsigned long freq_256_harm2, freq_256_harm3; - unsigned long freq_256_harm4, freq_256_harm5; - double attack0, decay0, release0; - double attack1, decay1, release1; - int gate; - - ports = organ->m_ppfPorts; - - gate = (*ports[PORT_GATE] > 0.0); - if (gate == 0) - { - organ->env0.envelope_decay = 0; - organ->env1.envelope_decay = 0; - } - - sine_table = g_sine_table; - reed_table = (*ports[PORT_REED] > 0.0) ? g_pulse_table : sine_table; - flute_table = (*ports[PORT_FLUTE] > 0.0) ? g_triangle_table : sine_table; - freq_256 = (int) (*ports[PORT_FREQ] * - ((double) RESOLUTION) / - organ->sample_rate * 256.0); - - freq_256_harm0 = freq_256 / 2; - freq_256_harm1 = freq_256; - - attack0 = multiplier (organ, *ports[PORT_ATTACK_LO]); - decay0 = multiplier (organ, *ports[PORT_DECAY_LO]); - release0 = multiplier (organ, *ports[PORT_RELEASE_LO]); - - attack1 = multiplier (organ, *ports[PORT_ATTACK_HI]); - decay1 = multiplier (organ, *ports[PORT_DECAY_HI]); - release1 = multiplier (organ, *ports[PORT_RELEASE_HI]); - - if (*ports[PORT_BRASS] > 0.0) - { - freq_256_harm2 = freq_256 * 2; - freq_256_harm3 = freq_256_harm2 * 2; - freq_256_harm4 = freq_256_harm3 * 2; - freq_256_harm5 = freq_256_harm4 * 2; - - for (i = 0; i < SampleCount; i++) - ports[PORT_OUT][i] = - ((table_pos (sine_table, freq_256_harm0, &organ->harm0_accum) * *ports[PORT_HARM0] - + table_pos (sine_table, freq_256_harm1, &organ->harm1_accum) * *ports[PORT_HARM1] - + table_pos (reed_table, freq_256_harm2, &organ->harm2_accum) * *ports[PORT_HARM2]) - * envelope (&organ->env0, gate, attack0, decay0, *ports[PORT_SUSTAIN_LO], release0) - + (table_pos (sine_table, freq_256_harm3, &organ->harm3_accum) * *ports[PORT_HARM3] - + table_pos (flute_table, freq_256_harm4, &organ->harm4_accum) * *ports[PORT_HARM4] - + table_pos (flute_table, freq_256_harm5, &organ->harm5_accum) * *ports[PORT_HARM5]) - * envelope (&organ->env1, gate, attack1, decay1, *ports[PORT_SUSTAIN_HI], release1)) * *ports[PORT_VELOCITY]; - } - else - { - freq_256_harm2 = freq_256 * 3 / 2; - freq_256_harm3 = freq_256 * 2; - freq_256_harm4 = freq_256 * 3; - freq_256_harm5 = freq_256_harm3 * 2; - - for (i = 0; i < SampleCount; i++) - ports[PORT_OUT][i] = - ((table_pos (sine_table, freq_256_harm0, &organ->harm0_accum) * *ports[PORT_HARM0] - + table_pos (sine_table, freq_256_harm1, &organ->harm1_accum) * *ports[PORT_HARM1] - + table_pos (sine_table, freq_256_harm2, &organ->harm2_accum) * *ports[PORT_HARM2]) - * envelope (&organ->env0, gate, attack0, decay0, *ports[PORT_SUSTAIN_LO], release0) - - + (table_pos (reed_table, freq_256_harm3, &organ->harm3_accum) * *ports[PORT_HARM3] - + table_pos (sine_table, freq_256_harm4, &organ->harm4_accum) * *ports[PORT_HARM4] - + table_pos (flute_table, freq_256_harm5, &organ->harm5_accum) * *ports[PORT_HARM5]) - * envelope (&organ->env1, gate, attack1, decay1, *ports[PORT_SUSTAIN_HI], release1)) * *ports[PORT_VELOCITY]; - } -} - - -}; - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "Out", - "Gate", - "Velocity", - "Frequency (Hz)", - "Brass", "Reed", "Flute", - "16th Harmonic", "8th Harmonic", - "5 1/3rd Harmonic", "4th Harmonic", - "2 2/3rd Harmonic", "2nd Harmonic", - "Attack Lo (Secs)", "Decay Lo (Secs)", "Sustain Lo (Level)", "Release Lo (Secs)", - "Attack Hi (Secs)", "Decay Hi (Secs)", "Sustain Hi (Level)", "Release Hi (Secs)", -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 20000.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.00, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.00, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 1.0 } -}; - -void -initialise_organ() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1222, - "organ", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Organ", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("1999, 2000", "David A. Bartold"), - NULL, - CMT_Instantiate, - Organ::activate, - Organ::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/peak.cpp b/plugins/LadspaEffect/cmt/src/peak.cpp deleted file mode 100644 index d17fc2c7eb0..00000000000 --- a/plugins/LadspaEffect/cmt/src/peak.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/* peak.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" -#include "utils.h" - -/*****************************************************************************/ - -#define ET_INPUT 0 -#define ET_OUTPUT 1 - -#define ET_FILTER 2 - -/** This class is used to provide plugins that perform envelope - tracking. Peak and RMS are supported and smoothed or smoothed - maximum approaches are available. */ -class Tracker : public CMT_PluginInstance { -private: - - LADSPA_Data m_fState; - LADSPA_Data m_fSampleRate; - -public: - - Tracker(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(3), - m_fSampleRate(LADSPA_Data(lSampleRate)) { - } - - friend void activateTracker(void * pvHandle); - friend void runEnvelopeTracker_Peak(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runEnvelopeTracker_RMS(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runEnvelopeTracker_MaxPeak(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runEnvelopeTracker_MaxRMS(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/** This class provides a simple peak monitor that records the highest - signal peak present ever. It can be useful to identify clipping - cases. */ -class PeakMonitor : public CMT_PluginInstance { -private: - - LADSPA_Data m_fState; - -public: - - PeakMonitor(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(2) { - } - - friend void activatePeakMonitor(void * pvHandle); - friend void runPeakMonitor(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -activateTracker(void * pvHandle) { - ((Tracker *)pvHandle)->m_fState = 0; -} - -/*****************************************************************************/ - -void -activatePeakMonitor(void * pvHandle) { - ((PeakMonitor *)pvHandle)->m_fState = 0; -} - -/*****************************************************************************/ - -void -runEnvelopeTracker_Peak(LADSPA_Handle Instance, - unsigned long SampleCount) { - Tracker * poProcessor = (Tracker *)Instance; - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ET_INPUT]; - LADSPA_Data fDrag = *(poProcessor->m_ppfPorts[ET_FILTER]); - LADSPA_Data fOneMinusDrag = 1 - fDrag; - LADSPA_Data &rfState = poProcessor->m_fState; - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fabs(fInput); - rfState = rfState * fDrag + fEnvelopeTarget * fOneMinusDrag; - } - *(poProcessor->m_ppfPorts[ET_OUTPUT]) = rfState; -} - -/*****************************************************************************/ - -void -runEnvelopeTracker_RMS(LADSPA_Handle Instance, - unsigned long SampleCount) { - Tracker * poProcessor = (Tracker *)Instance; - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ET_INPUT]; - LADSPA_Data fDrag = *(poProcessor->m_ppfPorts[ET_FILTER]); - LADSPA_Data fOneMinusDrag = 1 - fDrag; - LADSPA_Data &rfState = poProcessor->m_fState; - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fInput * fInput; - rfState = rfState * fDrag + fEnvelopeTarget * fOneMinusDrag; - } - *(poProcessor->m_ppfPorts[ET_OUTPUT]) = sqrt(rfState); -} - -/*****************************************************************************/ - -void -runEnvelopeTracker_MaxPeak(LADSPA_Handle Instance, - unsigned long SampleCount) { - Tracker * poProcessor = (Tracker *)Instance; - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ET_INPUT]; - LADSPA_Data fDrag = calculate60dBDrag(*(poProcessor->m_ppfPorts[ET_FILTER]), - poProcessor->m_fSampleRate); - LADSPA_Data &rfState = poProcessor->m_fState; - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fabs(fInput); - if (fEnvelopeTarget > rfState) - rfState = fEnvelopeTarget; - else { - rfState *= fDrag; - if (fEnvelopeTarget > rfState) - rfState = fEnvelopeTarget; - } - } - *(poProcessor->m_ppfPorts[ET_OUTPUT]) = rfState; -} - -/*****************************************************************************/ - -void -runEnvelopeTracker_MaxRMS(LADSPA_Handle Instance, - unsigned long SampleCount) { - Tracker * poProcessor = (Tracker *)Instance; - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ET_INPUT]; - LADSPA_Data fDrag = calculate60dBDrag(*(poProcessor->m_ppfPorts[ET_FILTER]), - poProcessor->m_fSampleRate); - LADSPA_Data &rfState = poProcessor->m_fState; - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fInput * fInput; - if (fEnvelopeTarget > rfState) - rfState = fEnvelopeTarget; - else { - rfState *= fDrag; - if (fEnvelopeTarget > rfState) - rfState = fEnvelopeTarget; - } - } - *(poProcessor->m_ppfPorts[ET_OUTPUT]) = sqrt(rfState); -} - -/*****************************************************************************/ - -void -runPeakMonitor(LADSPA_Handle Instance, - unsigned long SampleCount) { - PeakMonitor * poProcessor = (PeakMonitor *)Instance; - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[ET_INPUT]; - LADSPA_Data &rfState = poProcessor->m_fState; - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) { - LADSPA_Data fInput = *(pfInput++); - LADSPA_Data fEnvelopeTarget = fabs(fInput); - if (rfState < fEnvelopeTarget) - rfState = fEnvelopeTarget; - } - *(poProcessor->m_ppfPorts[ET_OUTPUT]) = rfState; -} - -/*****************************************************************************/ - -void -initialise_peak() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1078, - "track_peak", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Envelope Tracker (Peak)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateTracker, - runEnvelopeTracker_Peak, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output", - LADSPA_HINT_BOUNDED_BELOW, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Smoothing Factor", - LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, - 0, - 1); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1079, - "track_rms", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Envelope Tracker (RMS)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateTracker, - runEnvelopeTracker_RMS, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output", - LADSPA_HINT_BOUNDED_BELOW, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Smoothing Factor", - LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, - 0, - 1); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1080, - "track_max_peak", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Envelope Tracker (Maximum Peak)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateTracker, - runEnvelopeTracker_MaxPeak, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output", - LADSPA_HINT_BOUNDED_BELOW, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Envelope Forgetting Factor (s/60dB)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 10); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1081, - "track_max_rms", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Envelope Tracker (Maximum RMS)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateTracker, - runEnvelopeTracker_MaxRMS, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output", - LADSPA_HINT_BOUNDED_BELOW, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Envelope Forgetting Factor (s/60dB)", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_DEFAULT_MAXIMUM), - 0, - 10); - registerNewPluginDescriptor(psDescriptor); - - psDescriptor = new CMT_Descriptor - (1082, - "peak", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Peak Monitor", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activatePeakMonitor, - runPeakMonitor, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Peak", - LADSPA_HINT_BOUNDED_BELOW, - 0); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/phasemod.cpp b/plugins/LadspaEffect/cmt/src/phasemod.cpp deleted file mode 100644 index 0a8c8e27c4e..00000000000 --- a/plugins/LadspaEffect/cmt/src/phasemod.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* phasemod.cpp - - Phase Modulated Voice - Phase Modulation synthesizer voice - Copyright (c) 2001 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include "cmt.h" - -#define PORT_OUT 0 -#define PORT_GATE 1 -#define PORT_VELOCITY 2 -#define PORT_FREQ 3 -#define PORT_DCO_MODULATION 4 -#define PORT_DCO_OCTAVE 5 -#define PORT_DCO_WAVEFORM 6 -#define PORT_DCO_ATTACK 7 -#define PORT_DCO_DECAY 8 -#define PORT_DCO_SUSTAIN 9 -#define PORT_DCO_RELEASE 10 - -#define DCO_MULTIPLIER 7 - -#define NUM_PORTS 46 - -#ifndef PI -#define PI 3.14159265358979F -#endif - -typedef struct Envelope -{ - int envelope_decay; - LADSPA_Data envelope; - - Envelope () : envelope_decay (0), envelope (0.0) {} -} Envelope; - -class PhaseMod : public CMT_PluginInstance -{ - LADSPA_Data sample_rate; - - int trigger; - - Envelope dco_env[6]; - LADSPA_Data dco_accum[6]; - -public: - PhaseMod(const LADSPA_Descriptor * Descriptor, - unsigned long SampleRate) - : CMT_PluginInstance(NUM_PORTS), - sample_rate (SampleRate), - trigger (0) { - int i; - - for (i = 0; i < 6; i++) - dco_accum[i] = 0.0; - } - - ~PhaseMod () { - } - - static inline LADSPA_Data - tri(LADSPA_Data x) { - if (x > 0.75F) - x = x - 1.0F; - else if (x > 0.25F) - x = 0.5F - x; - - return x * 4.0F; - } - - static inline LADSPA_Data - envelope(Envelope *env, - int gate, - LADSPA_Data attack, - LADSPA_Data decay, - LADSPA_Data sustain, - LADSPA_Data release) - { - if (gate) - if (env->envelope_decay == 0) - { - env->envelope += (1.0F - env->envelope) * attack; - if (env->envelope >= 0.95F) - env->envelope_decay = 1; - } - else - env->envelope += (sustain - env->envelope) * decay; - else - env->envelope += -env->envelope * release; - - return env->envelope; - } - - static void - activate(LADSPA_Handle Instance) { - PhaseMod *phasemod = (PhaseMod*) Instance; - int i; - - phasemod->trigger = 0; - - for (i = 0; i < 6; i++) - { - phasemod->dco_env[i].envelope_decay = 0; - phasemod->dco_env[i].envelope = 0.0; - phasemod->dco_accum[i] = 0.0; - } - } - - static inline LADSPA_Data - osc(int waveform, - LADSPA_Data inc, - LADSPA_Data phasemod, - LADSPA_Data *accum) { - LADSPA_Data pos; - - *accum += inc; - while (*accum >= 1.0F) - *accum -= 1.0F; - - pos = *accum + phasemod; - while (pos < 0.0F) pos += 1.0F; - while (pos > 1.0F) pos -= 1.0F; - - /* 0 = Sine wave */ - if (waveform == 0) - return sin (pos * 2.0 * PI); - - /* 1 = Triangle wave */ - else if (waveform == 1) - return tri (pos); - - /* 2 = Square wave */ - else if (waveform == 2) - return (pos > 0.5) ? 1.0F : -1.0F; - - /* 3 = Sawtooth wave */ - else if (waveform == 3) - return pos * 2.0F - 1.0F; - - /* 4 = Fullwave Rectified Sine wave */ - else if (waveform == 4) - return fabs (pos * PI); - - /* 5 = Static */ - else - return (rand () & 1) ? -1.0F : 1.0F; - } - - static LADSPA_Data - calc_inc(LADSPA_Data oct, - LADSPA_Data freq, - LADSPA_Data sample_rate) { - return pow (2.0, oct) * freq / sample_rate; - } - - static inline LADSPA_Data - multiplier(PhaseMod *phasemod, - LADSPA_Data value) { - return 1.0 - pow (0.05, 1.0 / (phasemod->sample_rate * value)); - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - PhaseMod *phasemod = (PhaseMod*) Instance; - - unsigned long i, j; - int gate; - int waveform[6]; - int store[6]; - LADSPA_Data inc[6]; - LADSPA_Data attack[6]; - LADSPA_Data decay[6]; - LADSPA_Data release[6]; - LADSPA_Data **ports; - LADSPA_Data vol; - - ports = phasemod->m_ppfPorts; - gate = (*ports[PORT_GATE] > 0.0); - - if (gate == 1 && phasemod->trigger == 0) - for (i = 0; i < 6; i++) - phasemod->dco_env[i].envelope_decay = 0; - - phasemod->trigger = gate; - - for (i = 0; i < 6; i++) - { - int offset = DCO_MULTIPLIER * i; - - waveform[i] = (int) *ports[PORT_DCO_WAVEFORM + offset]; - inc[i] = calc_inc (*ports[PORT_DCO_OCTAVE + offset], - *ports[PORT_FREQ], - phasemod->sample_rate); - attack[i] = multiplier (phasemod, *ports[PORT_DCO_ATTACK + offset]); - decay[i] = multiplier (phasemod, *ports[PORT_DCO_DECAY + offset]); - release[i] = multiplier (phasemod, *ports[PORT_DCO_RELEASE + offset]); - } - - j = 1; - for (i = 0; i < 5; i++) - if (*ports[PORT_DCO_MODULATION + (i + 1) * DCO_MULTIPLIER] < 0.0001) - store[i] = 1, j++; - else - store[i] = 0; - store[5] = 1; - vol = 1.0 / j; - - for (i = 0; i < SampleCount; i++) - { - LADSPA_Data sample; - LADSPA_Data prev; - - sample = 0.0; - prev = 1.0; - for (j = 0; j < 6; j++) - { - int offset = DCO_MULTIPLIER * j; - - prev = - envelope (&phasemod->dco_env[j], - gate, attack[j], decay[j], - *ports[PORT_DCO_SUSTAIN + offset], release[j]) * - osc (waveform[j], inc[j], - prev * *ports[PORT_DCO_MODULATION + offset], - &phasemod->dco_accum[j]) * - *ports[PORT_VELOCITY]; - - if (store[j]) - sample += prev; - } - ports[PORT_OUT][i] = sample * vol; - } - } -}; - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "Out", - - "Gate", - "Velocity", - "Frequency (Hz)", - - "DCO1 Modulation", - "DCO1 Octave", - "DCO1 Waveform", - "DCO1 Attack", - "DCO1 Decay", - "DCO1 Sustain", - "DCO1 Release", - - "DCO2 Modulation", - "DCO2 Octave", - "DCO2 Waveform", - "DCO2 Attack", - "DCO2 Decay", - "DCO2 Sustain", - "DCO2 Release", - - "DCO3 Modulation", - "DCO3 Octave", - "DCO3 Waveform", - "DCO3 Attack", - "DCO3 Decay", - "DCO3 Sustain", - "DCO3 Release", - - "DCO4 Modulation", - "DCO4 Octave", - "DCO4 Waveform", - "DCO4 Attack", - "DCO4 Decay", - "DCO4 Sustain", - "DCO4 Release", - - "DCO5 Modulation", - "DCO5 Octave", - "DCO5 Waveform", - "DCO5 Attack", - "DCO5 Decay", - "DCO5 Sustain", - "DCO5 Release", - - "DCO6 Modulation", - "DCO6 Octave", - "DCO6 Waveform", - "DCO6 Attack", - "DCO6 Decay", - "DCO6 Sustain", - "DCO6 Release" -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 20000.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, -2.0, 2.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | - LADSPA_HINT_INTEGER, -0.1, 5.1 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.01, 8.0 } -}; - -void -initialise_phasemod() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1226, - "phasemod", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Phase Modulated Voice", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("2001", "David A. Bartold"), - NULL, - CMT_Instantiate, - PhaseMod::activate, - PhaseMod::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/pink.cpp b/plugins/LadspaEffect/cmt/src/pink.cpp deleted file mode 100644 index bf3e1bb48e1..00000000000 --- a/plugins/LadspaEffect/cmt/src/pink.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* pink.cpp - - Interpolated pink noise plugins for use as control signals. - - (c) 2002 Nathaniel Virgo - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -#include "pinknoise.h" -#include "utils.h" - -/*****************************************************************************/ - -namespace pink { - - enum { - port_frequency = 0, - port_output = 1, - n_ports = 2 - }; - - /** This plugin generates a signal which approximates the effect of low-pass - filtered pink noise, which makes for an interesting randomly changing - control parameter. It should probably use sinc interpolation, but in fact - it uses third-order splines, which sound more-or-less okay to me. */ - class Plugin : public CMT_PluginInstance { - private: - - LADSPA_Data sample_rate; - - PinkNoise noise_source; - LADSPA_Data *data_points; - int first_point; - unsigned long counter; - float multiplier; // 1/(max counter value) - - public: - - Plugin(const LADSPA_Descriptor *, - unsigned long s_rate) : - CMT_PluginInstance(n_ports), - sample_rate(s_rate) { - data_points = new LADSPA_Data[4]; - } - - ~Plugin() { - delete [] data_points; - } - - friend void activate(LADSPA_Handle instance); - - friend void run_interpolated_audio(LADSPA_Handle instance, - unsigned long sample_count); - - friend void run_interpolated_control(LADSPA_Handle instance, - unsigned long sample_count); - - }; - - void activate(LADSPA_Handle instance) { - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - p.noise_source.reset(); - for (int i=0; i<4; ++i) - p.data_points[i] = p.noise_source.getValue(); - p.first_point = 0; - p.counter = 0; - p.multiplier = 1; - } - - inline float thirdInterp(const float &x, - const float &L1,const float &L0, - const float &H0,const float &H1) { - return - L0 + - .5f* - x*(H0-L1 + - x*(H0 + L0*(-2) + L1 + - x*( (H0 - L0)*9 + (L1 - H1)*3 + - x*((L0 - H0)*15 + (H1 - L1)*5 + - x*((H0 - L0)*6 + (L1 - H1)*2 ))))); - } - - void run_interpolated_audio(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - LADSPA_Data frequency = *pp->m_ppfPorts[port_frequency]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - if (frequency<=0) { - LADSPA_Data value = thirdInterp( 1 - p.counter*p.multiplier, - p.data_points[ p.first_point ], - p.data_points[ (p.first_point+1) % 4 ], - p.data_points[ (p.first_point+2) % 4 ], - p.data_points[ (p.first_point+3) % 4 ] ); - for (unsigned long i=0; im_ppfPorts[port_frequency]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - float value = thirdInterp( 1 - p.counter*p.multiplier, - p.data_points[ p.first_point ], - p.data_points[ (p.first_point+1) % 4 ], - p.data_points[ (p.first_point+2) % 4 ], - p.data_points[ (p.first_point+3) % 4 ] ); - if (frequency>0) { - frequency = BOUNDED_ABOVE(frequency, p.sample_rate/sample_count); - while (p.counter <= sample_count) { - p.data_points[ p.first_point ] = p.noise_source.getValue(); - p.first_point = (p.first_point + 1) % 4; - p.multiplier = frequency/p.sample_rate; - p.counter += (unsigned long)(p.sample_rate/frequency); - } - p.counter -= (p.counter < sample_count) ? p.counter : sample_count; - } - *(out)=value; - } - - void initialise() { - CMT_Descriptor * d = new CMT_Descriptor - (1841, - "pink_interpolated_audio", - 0, - "Pink Noise (Interpolated)", - CMT_MAKER("Nathaniel Virgo"), - CMT_COPYRIGHT("2002", "Nathaniel Virgo"), - NULL, - CMT_Instantiate, - activate, - run_interpolated_audio, - NULL, - NULL, - NULL); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Highest frequency", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_DEFAULT_1), - 0, - 1); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(d); - - // the following has been commented out because I'm pretty sure that - // control-rate outputs don't make sense for the vast majority of hosts. - // (SSM being the notable exception) - /* - d = new CMT_Descriptor - (1842, - "pink_interpolated_control", - 0, - "Pink Noise (Interpolated, control rate)", - CMT_MAKER("Nathaniel Virgo"), - CMT_COPYRIGHT("2002", "Nathaniel Virgo"), - NULL, - CMT_Instantiate, - activate, - run_interpolated_control, - NULL, - NULL, - NULL); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Highest frequency", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_DEFAULT_1), - 0, - 0.002); // arbitrary low value (sensible for sample_count around 500) - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, - "Output"); - registerNewPluginDescriptor(d); - */ - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/pink_full.cpp b/plugins/LadspaEffect/cmt/src/pink_full.cpp deleted file mode 100644 index 59c92906eca..00000000000 --- a/plugins/LadspaEffect/cmt/src/pink_full.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* pink_full.cpp - - A full-frequency pink noise generator. - - (c) 2002 Nathaniel Virgo - - Part of the Computer Music Toolkit - a library of LADSPA plugins. - The Computer Music Toolkit is Copyright (C) 2000-2002 - Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -#include "pinknoise.h" -#include "utils.h" - -/*****************************************************************************/ - -namespace pink_full { - - enum { - port_output = 0, - - n_ports = 1 - }; - - /** This plugin generates a signal which approximates pink noise, using the - Voss-McCartney algorithm described at www.firstpr.com.au/dsp/pink-noise/ */ - class Plugin : public CMT_PluginInstance { - private: - PinkNoise noise_source; - public: - - Plugin(const LADSPA_Descriptor *, - unsigned long s_rate) : - CMT_PluginInstance(n_ports) - { - } - - ~Plugin() { - } - - friend void activate(LADSPA_Handle instance); - - friend void run(LADSPA_Handle instance, - unsigned long sample_count); - }; - - void activate(LADSPA_Handle instance) { - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - p.noise_source.reset(); - } - - void run(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - for (unsigned long i=0; i, - activate, - run, - NULL, - NULL, - NULL); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(d); - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/pink_sh.cpp b/plugins/LadspaEffect/cmt/src/pink_sh.cpp deleted file mode 100644 index 13f2a69d5e5..00000000000 --- a/plugins/LadspaEffect/cmt/src/pink_sh.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* pink_sh.cpp - - A sample-and-hold pink noise generator. - - (c) 2002 Nathaniel Virgo - - Part of the Computer Music Toolkit - a library of LADSPA plugins. - The Computer Music Toolkit is Copyright (C) 2000-2002 - Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -#include "cmt.h" - -#include "pinknoise.h" -#include "utils.h" - -/*****************************************************************************/ - -namespace pink_sh { - - enum { - port_frequency = 0, - port_output = 1, - n_ports = 2 - }; - - /** This plugin generates a signal which approximates stepped - (sample-and-hold like) pink noise, using the - Voss-McCartney algorithm described at www.firstpr.com.au/dsp/pink-noise/ */ - class Plugin : public CMT_PluginInstance { - private: - LADSPA_Data sample_rate; - PinkNoise noise_source; - unsigned counter; - public: - - Plugin(const LADSPA_Descriptor *, - unsigned long s_rate) : - CMT_PluginInstance(n_ports), - sample_rate(s_rate) { - } - - ~Plugin() { - } - - friend void activate(LADSPA_Handle instance); - - friend void run(LADSPA_Handle instance, - unsigned long sample_count); - }; - - void activate(LADSPA_Handle instance) { - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - p.noise_source.reset(); - p.counter = 0; - } - - void run(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - LADSPA_Data frequency = *pp->m_ppfPorts[port_frequency]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - frequency = BOUNDED_ABOVE(frequency,p.sample_rate); - unsigned remain = sample_count; - - if (frequency > 0) { - while (remain) { - unsigned jump_samples = (remain, - activate, - run, - NULL, - NULL, - NULL); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Sample and hold frequency", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_DEFAULT_1), - 0, 0.02); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(d); - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ - diff --git a/plugins/LadspaEffect/cmt/src/pinknoise.h b/plugins/LadspaEffect/cmt/src/pinknoise.h deleted file mode 100644 index c6b350ff8f6..00000000000 --- a/plugins/LadspaEffect/cmt/src/pinknoise.h +++ /dev/null @@ -1,111 +0,0 @@ -/* pinknoise.h - - pink noise generating class using the Voss-McCartney algorithm, as - described at www.firstpr.com.au/dsp/pink-noise/ - - (c) 2002 Nathaniel Virgo - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -#ifndef _PINKNOISE_H -#define _PINKNOISE_H - -#include - -typedef unsigned int CounterType; -typedef float DataValue; - -const int n_generators = 8*sizeof(CounterType); - -class PinkNoise { - private: - - CounterType counter; - DataValue * generators; - DataValue last_value; - - public: - - PinkNoise() { - generators = new DataValue[n_generators]; - reset(); - } - - ~PinkNoise() {delete [] generators;}; - - void reset() { - counter = 0; - last_value = 0; - for (int i=0; i>= 1; - index++; - // this loop means that the plugins cannot be labelled as - // capable of hard real-time performance. - } - - last_value -= generators[index]; - generators[index] = 2*(rand()/DataValue(RAND_MAX))-1; - last_value += generators[index]; - } - - counter++; - - return last_value; - } - - inline DataValue getValue() { - return getUnscaledValue()/n_generators; - } - - inline DataValue getLastValue() { - return last_value/n_generators; - } - - inline DataValue getValue2() { - // adding some white noise gets rid of some nulls in the frequency spectrum - // but makes the signal spikier, so possibly not so good for control signals. - return (getUnscaledValue() + rand()/DataValue(RAND_MAX*0.5)-1)/(n_generators+1); - } - -}; - -#endif - - - - - - - - - diff --git a/plugins/LadspaEffect/cmt/src/run_adding.h b/plugins/LadspaEffect/cmt/src/run_adding.h deleted file mode 100644 index 2ab1892af91..00000000000 --- a/plugins/LadspaEffect/cmt/src/run_adding.h +++ /dev/null @@ -1,159 +0,0 @@ -/* run_adding.h - - (c) 2002 Nathaniel Virgo - - a few simple inline functions that can be used with templates - to get run_adding for free. - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -/* - How to use this - --------------- - - Templates can be used to automatically generate code for both the run and - run_adding LADSPA functions. Simply define the plugin's run function as - - template - void run_foo(LADSPA_Handle Instance, unsigned long SampleCount); - - and in the body of the function use - - write_output(pfOutput, fValue, poFoo->m_fRunAddingGain); - - instead of - - *(pfOutput++) = fValue. - - and be sure to include a set_run_adding_gain function. - then set the LADSPA run function as - - run_foo; - - and the run_adding function as - - run_foo; - - With -O1 or greater g++ will inline the write_output function, and - although the code ends up slightly bigger there is no overhead compared to - having two seperate functions. - - Sometimes the run_adding_gain function can be made more efficient than this - - for instance, if the output is multiplied by a gain already then you are - doing one more multiplication than necessary on every sample. It's a lot - less code to maintain, though, and it should still save some work for the - host compared to not having a run_adding function. -*/ - -/*****************************************************************************/ - -#include - -/*****************************************************************************/ - -typedef void OutputFunction(LADSPA_Data *&, const LADSPA_Data &, - const LADSPA_Data &); - -inline void write_output_normal(LADSPA_Data *&out, const LADSPA_Data &value, - const LADSPA_Data &run_adding_gain) -{ - *(out++) = value; -} - -inline void write_output_adding(LADSPA_Data *&out, const LADSPA_Data &value, - const LADSPA_Data &run_adding_gain) -{ - *(out++) += value*run_adding_gain; -} - -/*****************************************************************************/ - -/* - If the plugin has a control-rate ouput then you don't want the write_output - function to try to increment the pointer. To achieve this, use - - write_control(pfOutput, fValue, poFoo->m_fRunAddingGain); - - instead of just - - write_output(...); - - I realise this feels a bit hacky, but it works. -*/ - -template -inline void write_control(LADSPA_Data *const, - const LADSPA_Data &, const LADSPA_Data &); - -template <> -inline void write_control(LADSPA_Data *const out, - const LADSPA_Data &value, - const LADSPA_Data &run_adding_gain) -{ - *out = value; -} - -template <> -inline void write_control(LADSPA_Data *const out, - const LADSPA_Data &value, - const LADSPA_Data &run_adding_gain) -{ - *out += value*run_adding_gain; -} - - -/*****************************************************************************/ - -/* - This next bit is an attempt to facilitate the writing of slightly - more efficent run_adding functions without writing two seperate pieces of - code. You can say something like - - LADSPA_Data fOutputGain = ... ; - ... - fOutputGain *= get_gain(poFoo->m_fRunAddingGain); - ... - write_output(pfOutput, fValue*fOutputGain, 1.0f); - - in run_foo. With -O1 or greater g++ should inline the functions and - optimise away the multiplies by 1.0f, so in run_foo - fOutputGain will be multiplied by m_fRunAddingGain and in - run_foo it will be left alone. - - This does not make for very clear code, sorry about that. See disintegrator.cpp - for an example. -*/ - -template -inline float get_gain(const LADSPA_Data &); - -template <> -inline float get_gain(const LADSPA_Data &) -{ - return 1.0f; -} - -template <> -inline float get_gain(const LADSPA_Data &run_adding_gain) -{ - return run_adding_gain; -} diff --git a/plugins/LadspaEffect/cmt/src/sine.cpp b/plugins/LadspaEffect/cmt/src/sine.cpp deleted file mode 100644 index 7b27ff4cf2c..00000000000 --- a/plugins/LadspaEffect/cmt/src/sine.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* sine.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -/* Sine table size is given by (1 << SINE_TABLE_BITS). */ -#define SINE_TABLE_BITS 14 -#define SINE_TABLE_SHIFT (8 * sizeof(unsigned long) - SINE_TABLE_BITS) - -/*****************************************************************************/ - -LADSPA_Data * g_pfSineTable = NULL; -LADSPA_Data g_fPhaseStepBase = 0; - -/*****************************************************************************/ - -void -initialise_sine_wavetable() { - if (g_pfSineTable == NULL) { - long lTableSize = (1 << SINE_TABLE_BITS); - double dShift = (double(M_PI) * 2) / lTableSize; - g_pfSineTable = new float[lTableSize]; - if (g_pfSineTable != NULL) - for (long lIndex = 0; lIndex < lTableSize; lIndex++) - g_pfSineTable[lIndex] = LADSPA_Data(sin(dShift * lIndex)); - } - if (g_fPhaseStepBase == 0) { - g_fPhaseStepBase = (LADSPA_Data)pow(2, sizeof(unsigned long) * 8); - } -} - -/*****************************************************************************/ - -#define OSC_FREQUENCY 0 -#define OSC_AMPLITUDE 1 -#define OSC_OUTPUT 2 - -/* This class provides sine wavetable oscillator - plugins. Band-limiting to avoid aliasing is trivial because of the - waveform in use. Four versions of the oscillator are provided, - allowing the amplitude and frequency inputs of the oscillator to be - audio signals rather than controls (for use in AM and FM - synthesis). */ -class SineOscillator : public CMT_PluginInstance{ -private: - - /* Oscillator state: */ - - unsigned long m_lPhase; - unsigned long m_lPhaseStep; - LADSPA_Data m_fCachedFrequency; - const LADSPA_Data m_fLimitFrequency; - const LADSPA_Data m_fPhaseStepScalar; - - void setPhaseStepFromFrequency(const LADSPA_Data fFrequency) { - if (fFrequency != m_fCachedFrequency) { - if (fFrequency >= 0 && fFrequency < m_fLimitFrequency) - m_lPhaseStep = (unsigned long)(m_fPhaseStepScalar * fFrequency); - else - m_lPhaseStep = 0; - m_fCachedFrequency = fFrequency; - } - } - -public: - - SineOscillator(const LADSPA_Descriptor *, - unsigned long lSampleRate) - : CMT_PluginInstance(3), - m_lPhaseStep(0), - m_fCachedFrequency(0), - m_fLimitFrequency(LADSPA_Data(lSampleRate * 0.5)), - m_fPhaseStepScalar(LADSPA_Data(g_fPhaseStepBase / lSampleRate)) { - } - - friend void activateSineOscillator(void * pvHandle); - friend void runSineOscillator_FreqAudio_AmpAudio(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runSineOscillator_FreqAudio_AmpCtrl(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runSineOscillator_FreqCtrl_AmpAudio(LADSPA_Handle Instance, - unsigned long SampleCount); - friend void runSineOscillator_FreqCtrl_AmpCtrl(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -activateSineOscillator(void * pvHandle) { - ((SineOscillator *)pvHandle)->m_lPhase = 0; -} - -/*****************************************************************************/ - -void -runSineOscillator_FreqAudio_AmpAudio(LADSPA_Handle Instance, - unsigned long SampleCount) { - SineOscillator * poSineOscillator = (SineOscillator *)Instance; - LADSPA_Data * pfFrequency = poSineOscillator->m_ppfPorts[OSC_FREQUENCY]; - LADSPA_Data * pfAmplitude = poSineOscillator->m_ppfPorts[OSC_AMPLITUDE]; - LADSPA_Data * pfOutput = poSineOscillator->m_ppfPorts[OSC_OUTPUT]; - for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { - /* Extract frequency at this point to guarantee inplace - support. */ - LADSPA_Data fFrequency = *(pfFrequency++); - *(pfOutput++) - = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] - * *(pfAmplitude++)); - poSineOscillator->setPhaseStepFromFrequency(fFrequency); - poSineOscillator->m_lPhase - += poSineOscillator->m_lPhaseStep; - } -} - -/*****************************************************************************/ - -void -runSineOscillator_FreqAudio_AmpCtrl(LADSPA_Handle Instance, - unsigned long SampleCount) { - SineOscillator * poSineOscillator = (SineOscillator *)Instance; - LADSPA_Data fAmplitude = *(poSineOscillator->m_ppfPorts[OSC_AMPLITUDE]); - LADSPA_Data * pfFrequency = poSineOscillator->m_ppfPorts[OSC_FREQUENCY]; - LADSPA_Data * pfOutput = poSineOscillator->m_ppfPorts[OSC_OUTPUT]; - for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { - /* Extract frequency at this point to guarantee inplace - support. */ - LADSPA_Data fFrequency = *(pfFrequency++); - *(pfOutput++) - = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] - * fAmplitude); - poSineOscillator->setPhaseStepFromFrequency(fFrequency); - poSineOscillator->m_lPhase - += poSineOscillator->m_lPhaseStep; - } -} - -/*****************************************************************************/ - -void -runSineOscillator_FreqCtrl_AmpAudio(LADSPA_Handle Instance, - unsigned long SampleCount) { - SineOscillator * poSineOscillator = (SineOscillator *)Instance; - poSineOscillator->setPhaseStepFromFrequency - (*(poSineOscillator->m_ppfPorts[OSC_FREQUENCY])); - LADSPA_Data * pfAmplitude = poSineOscillator->m_ppfPorts[OSC_AMPLITUDE]; - LADSPA_Data * pfOutput = poSineOscillator->m_ppfPorts[OSC_OUTPUT]; - for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { - *(pfOutput++) - = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] - * *(pfAmplitude++)); - poSineOscillator->m_lPhase - += poSineOscillator->m_lPhaseStep; - } -} - -/*****************************************************************************/ - -void -runSineOscillator_FreqCtrl_AmpCtrl(LADSPA_Handle Instance, - unsigned long SampleCount) { - SineOscillator * poSineOscillator = (SineOscillator *)Instance; - LADSPA_Data fAmplitude = *(poSineOscillator->m_ppfPorts[OSC_AMPLITUDE]); - poSineOscillator->setPhaseStepFromFrequency - (*(poSineOscillator->m_ppfPorts[OSC_FREQUENCY])); - LADSPA_Data * pfOutput = poSineOscillator->m_ppfPorts[OSC_OUTPUT]; - for (unsigned long lIndex = 0; lIndex < SampleCount; lIndex++) { - *(pfOutput++) - = (g_pfSineTable[poSineOscillator->m_lPhase >> SINE_TABLE_SHIFT] - * fAmplitude); - poSineOscillator->m_lPhase - += poSineOscillator->m_lPhaseStep; - } -} - -/*****************************************************************************/ - -void -initialise_sine() { - - initialise_sine_wavetable(); - - const char * apcLabels[] = { - "sine_faaa", - "sine_faac", - "sine_fcaa", - "sine_fcac" - }; - const char * apcNames[] = { - "Sine Oscillator (Freq:audio, Amp:audio)", - "Sine Oscillator (Freq:audio, Amp:control)", - "Sine Oscillator (Freq:control, Amp:audio)", - "Sine Oscillator (Freq:control, Amp:control)" - }; - LADSPA_Run_Function afRunFunction[] = { - runSineOscillator_FreqAudio_AmpAudio, - runSineOscillator_FreqAudio_AmpCtrl, - runSineOscillator_FreqCtrl_AmpAudio, - runSineOscillator_FreqCtrl_AmpCtrl - }; - LADSPA_PortDescriptor piFrequencyPortProperties[] = { - LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL - }; - LADSPA_PortDescriptor piAmplitudePortProperties[] = { - LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL - }; - - for (long lPluginIndex = 0; lPluginIndex < 4; lPluginIndex++) { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1063 + lPluginIndex, - apcLabels[lPluginIndex], - LADSPA_PROPERTY_HARD_RT_CAPABLE, - apcNames[lPluginIndex], - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - activateSineOscillator, - afRunFunction[lPluginIndex], - NULL, - NULL, - NULL); - - psDescriptor->addPort - (piFrequencyPortProperties[lPluginIndex], - "Frequency", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_SAMPLE_RATE - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_440), - 0, - 0.5); - psDescriptor->addPort - (piAmplitudePortProperties[lPluginIndex], - "Amplitude", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - - registerNewPluginDescriptor(psDescriptor); - } -} - -/*****************************************************************************/ - -void -finalise_sine() { - delete [] g_pfSineTable; -} - -/*****************************************************************************/ - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/sledgehammer.cpp b/plugins/LadspaEffect/cmt/src/sledgehammer.cpp deleted file mode 100644 index 60eb79f70cf..00000000000 --- a/plugins/LadspaEffect/cmt/src/sledgehammer.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* sledgehammer.cpp - - "Dynamic Sledgehammer" - a thing to brutally mangle the dynamics of - a sound. - - (c) 2002 Nathaniel Virgo - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" -#include "run_adding.h" - -/*****************************************************************************/ - -namespace sledgehammer { - - enum { - port_rate = 0, - port_mod_infl = 1, // modulator influence - port_car_infl = 2, // carrier influence (0 to 1 for compression) - port_modulator = 3, - port_carrier = 4, - port_output = 5, - n_ports = 6 - }; - -/** This plugin imposes the dynamics of one sound onto another. - It can be seen as a brutal compressor with a sidechain, or - as a kind of one-band vocoder. */ - class Plugin : public CMT_PluginInstance { - LADSPA_Data run_adding_gain; - LADSPA_Data running_ms_mod; - LADSPA_Data running_ms_car; // current mean square average - public: - Plugin(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(n_ports) {} - - friend void activate(LADSPA_Handle instance); - - template - friend void run(LADSPA_Handle instance, - unsigned long sample_count); - - friend void set_run_adding_gain(LADSPA_Handle instance, - LADSPA_Data new_gain); - }; - - void activate(LADSPA_Handle instance) { - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - p.running_ms_mod = 0; - p.running_ms_car = 0; - } - - template - void run(LADSPA_Handle instance, - unsigned long sample_count) { - - Plugin *pp = (Plugin *) instance; - Plugin &p = *pp; - - LADSPA_Data rate = *pp->m_ppfPorts[port_rate]; - LADSPA_Data mod_infl = *pp->m_ppfPorts[port_mod_infl]; - LADSPA_Data car_infl = *pp->m_ppfPorts[port_car_infl]; - LADSPA_Data * modptr = pp->m_ppfPorts[port_modulator]; - LADSPA_Data * carptr = pp->m_ppfPorts[port_carrier]; - LADSPA_Data * out = pp->m_ppfPorts[port_output]; - - for ( unsigned long i = 0; i < sample_count ; ++i ) { - LADSPA_Data mod = *(modptr++); - LADSPA_Data car = *(carptr++); - - p.running_ms_mod = p.running_ms_mod*(1-rate) + (mod*mod)*rate; - p.running_ms_car = p.running_ms_car*(1-rate) + (car*car)*rate; - - LADSPA_Data rms_mod = sqrt(p.running_ms_mod); - LADSPA_Data rms_car = sqrt(p.running_ms_car); - - LADSPA_Data outsig = car; - - if (rms_car>0) - outsig *= ((rms_car-0.5)*car_infl+0.5)/rms_car; - - outsig *= ((rms_mod-0.5)*mod_infl+0.5); - - write_output(out, outsig ,p.run_adding_gain); - } - } - - void set_run_adding_gain(LADSPA_Handle instance, - LADSPA_Data new_gain) { - ((Plugin *) instance)->run_adding_gain = new_gain; - } - - void - initialise() { - - CMT_Descriptor * d = new CMT_Descriptor - (1848, - "sledgehammer", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Dynamic Sledgehammer", - CMT_MAKER("Nathaniel Virgo"), - CMT_COPYRIGHT("2002", "Nathaniel Virgo"), - NULL, - CMT_Instantiate, - activate, - run, - run, - set_run_adding_gain, - NULL); - - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Rate", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_MIDDLE), - 0.00001, - 0.001); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Modulator influence", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_0), - -1, - 1); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Carrier influence", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_BOUNDED_ABOVE - | LADSPA_HINT_DEFAULT_1), - -1, - 1); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Modulator"); - d->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Carrier"); - d->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - - registerNewPluginDescriptor(d); - - } - -} // end of namespace - -/*****************************************************************************/ - -/* EOF */ - - - - - diff --git a/plugins/LadspaEffect/cmt/src/syndrum.cpp b/plugins/LadspaEffect/cmt/src/syndrum.cpp deleted file mode 100644 index 0ff2e9b3941..00000000000 --- a/plugins/LadspaEffect/cmt/src/syndrum.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* syndrum.cpp - - SynDrum - Drum Synthesizer - Copyright (c) 1999, 2000 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include -#include "cmt.h" - -#define PORT_OUT 0 -#define PORT_TRIGGER 1 -#define PORT_VELOCITY 2 -#define PORT_FREQ 3 -#define PORT_RESONANCE 4 -#define PORT_RATIO 5 - -#define NUM_PORTS 6 - -#ifndef PI -#define PI 3.14159265358979 -#endif - -class SynDrum : public CMT_PluginInstance { - LADSPA_Data sample_rate; - - LADSPA_Data spring_vel; - LADSPA_Data spring_pos; - LADSPA_Data env; - - int last_trigger; - -public: - SynDrum(const LADSPA_Descriptor *, - unsigned long s_rate) - : CMT_PluginInstance(NUM_PORTS), - sample_rate(s_rate), - spring_vel(0.0F), - spring_pos(0.0F), - env(0.0F) { - } - - ~SynDrum() { - } - - static void - activate(LADSPA_Handle Instance) { - SynDrum *syndrum = (SynDrum*) Instance; - syndrum->spring_vel = 0.0F; - syndrum->spring_pos = 0.0F; - syndrum->env = 0.0F; - syndrum->last_trigger = 0; - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - SynDrum *syndrum = (SynDrum*) Instance; - unsigned long i; - int trigger; - LADSPA_Data freq_shift; - LADSPA_Data factor; - LADSPA_Data res; - - trigger = *syndrum->m_ppfPorts[PORT_TRIGGER] > 0.0; - if (trigger == 1 && syndrum->last_trigger == 0) - { - syndrum->spring_vel = *syndrum->m_ppfPorts[PORT_VELOCITY]; - syndrum->env = *syndrum->m_ppfPorts[PORT_VELOCITY]; - } - syndrum->last_trigger = trigger; - - factor = 2.0 * PI / syndrum->sample_rate; - freq_shift = *syndrum->m_ppfPorts[PORT_FREQ] * - *syndrum->m_ppfPorts[PORT_RATIO]; - res = pow (0.05, 1.0 / (syndrum->sample_rate * *syndrum->m_ppfPorts[PORT_RESONANCE])); - - for (i = 0; i < SampleCount; i++) - { - LADSPA_Data cur_freq; - - cur_freq = *syndrum->m_ppfPorts[PORT_FREQ] + - (syndrum->env * freq_shift); - cur_freq *= factor; - syndrum->spring_vel -= syndrum->spring_pos * cur_freq; - syndrum->spring_pos += syndrum->spring_vel * cur_freq; - syndrum->spring_vel *= res; - syndrum->env *= res; - - syndrum->m_ppfPorts[PORT_OUT][i] = syndrum->spring_pos; - } - } -}; - - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "Out", - "Trigger", - "Velocity", - "Frequency (Hz)", - "Resonance", - "Frequency Ratio" -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 10.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 20000.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.001, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 10.0 } -}; - -void -initialise_syndrum() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1223, - "syndrum", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Syn Drum", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("1999, 2000", "David A. Bartold"), - NULL, - CMT_Instantiate, - SynDrum::activate, - SynDrum::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/utils.h b/plugins/LadspaEffect/cmt/src/utils.h deleted file mode 100644 index 120567546e1..00000000000 --- a/plugins/LadspaEffect/cmt/src/utils.h +++ /dev/null @@ -1,103 +0,0 @@ -/* utils.h - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -#ifndef CMT_UTILS_INCLUDED -#define CMT_UTILS_INCLUDED - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "ladspa_types.h" - -/*****************************************************************************/ - -/** The drag setting is arranged so that the gain drops by a factor of - 1e3 (60dB) in the time specified. This is a bit of an arbitrary - value but ties in with what the user will probably expect from - his/her experience with reverb units. */ -inline LADSPA_Data -calculate60dBDrag(const LADSPA_Data fTime, - const LADSPA_Data fSampleRate) { - if (fTime <= 0) - return 0; - else - return pow(1e3, -1 / (fTime * fSampleRate)); -} - -/*****************************************************************************/ - -inline LADSPA_Data -BOUNDED_BELOW(const LADSPA_Data fData, - const LADSPA_Data fLowerBound) { - if (fData <= fLowerBound) - return fLowerBound; - else - return fData; -} - -inline LADSPA_Data BOUNDED_ABOVE(const LADSPA_Data fData, - const LADSPA_Data fUpperBound) { - if (fData >= fUpperBound) - return fUpperBound; - else - return fData; -} - -inline LADSPA_Data -BOUNDED(const LADSPA_Data fData, - const LADSPA_Data fLowerBound, - const LADSPA_Data fUpperBound) { - if (fData <= fLowerBound) - return fLowerBound; - else if (fData >= fUpperBound) - return fUpperBound; - else - return fData; -} - -/*****************************************************************************/ - -/* Take a reading from a normal RV. The algorithm works by repeated - sampling of the uniform distribution, the lQuality variable giving - the number of samples. */ -inline double -sampleNormalDistribution(const double dMean, - const double dStandardDeviation, - const long lQuality = 12) { - - double dValue = 0; - for (long lIter = 0; lIter < lQuality; lIter++) - dValue += rand(); - - double dSampleFromNormal01 = (dValue / RAND_MAX) - (lQuality * 0.5); - - return dMean + dStandardDeviation * dSampleFromNormal01; -} - -/*****************************************************************************/ - -#endif - -/* EOF */ diff --git a/plugins/LadspaEffect/cmt/src/vcf303.cpp b/plugins/LadspaEffect/cmt/src/vcf303.cpp deleted file mode 100644 index 734850ec691..00000000000 --- a/plugins/LadspaEffect/cmt/src/vcf303.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* vcf303.cpp - - VCF 303 - TB-303 Resonant Filter - Copyright (c) 1998 Andy Sloane - Copyright (c) 1999, 2000 David A. Bartold - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - - -#include -#include -#include "cmt.h" - -#define PORT_IN 0 -#define PORT_OUT 1 -#define PORT_TRIGGER 2 -#define PORT_CUTOFF 3 -#define PORT_RESONANCE 4 -#define PORT_ENV_MOD 5 -#define PORT_DECAY 6 - -#define NUM_PORTS 7 - -#ifndef PI -#define PI 3.14159265358979 -#endif - -class Vcf303 : public CMT_PluginInstance { - LADSPA_Data sample_rate; - - LADSPA_Data d1, d2, c0; - int last_trigger; - int envpos; - -public: - Vcf303(const LADSPA_Descriptor *, - unsigned long s_rate) - : CMT_PluginInstance(NUM_PORTS), - sample_rate(s_rate), - d1(0.0), d2(0.0), c0(0.0), - last_trigger(0), - envpos(0) { - } - - ~Vcf303() { - } - - static void - activate(LADSPA_Handle Instance) { - Vcf303 *vcf303 = (Vcf303*) Instance; - - vcf303->d1 = 0.0; - vcf303->d2 = 0.0; - vcf303->c0 = 0.0; - vcf303->last_trigger = 0; - vcf303->envpos = 0; - } - - static inline void - recalc_a_b_c (Vcf303 *filter, - LADSPA_Data e0, - LADSPA_Data c0, - LADSPA_Data resonance, - LADSPA_Data *a, - LADSPA_Data *b, - LADSPA_Data *c) { - LADSPA_Data whopping, k; - - whopping = e0 + c0; - k = exp (-whopping / resonance); - - *a = 2.0 * cos (2.0 * whopping) * k; - *b = -k * k; - *c = (1.0 - *a - *b) * 0.2; - } - - static void - run(LADSPA_Handle Instance, - unsigned long SampleCount) { - Vcf303 *vcf303 = (Vcf303*) Instance; - unsigned long i; - LADSPA_Data e0, d, a, b, c; - LADSPA_Data decay, resonance; - LADSPA_Data **ports; - int trigger; - - /* Update vars given envmod, cutoff, and reso. */ - ports = vcf303->m_ppfPorts; - e0 = exp (5.613 - 0.8 * *ports[PORT_ENV_MOD] + 2.1553 * - *ports[PORT_CUTOFF] - 0.7696 * (1.0 - *ports[PORT_RESONANCE])); - e0 *= PI / vcf303->sample_rate; - - trigger = (*ports[PORT_TRIGGER] > 0.0); - if (trigger == 1 && vcf303->last_trigger == 0) - { - LADSPA_Data e1; - - e1 = exp (6.109 + 1.5876 * *ports[PORT_ENV_MOD] + 2.1553 * - *ports[PORT_CUTOFF] - 1.2 * (1.0 - *ports[PORT_RESONANCE])); - e1 *= PI / vcf303->sample_rate; - vcf303->c0 = e1 - e0; - } - vcf303->last_trigger = trigger; - - /* Update decay given envdecay. */ - d = 0.2 + (2.3 * *ports[PORT_DECAY]); - d *= vcf303->sample_rate; - d = pow (0.1, 1.0 / d); - decay = pow (d, 64); - - /* Update resonance. */ - resonance = exp (-1.20 + 3.455 * *ports[PORT_RESONANCE]); - - recalc_a_b_c (vcf303, e0, vcf303->c0, resonance, &a, &b, &c); - - for (i = 0; i < SampleCount; i++) - { - LADSPA_Data sample; - - sample = a * vcf303->d1 + b * vcf303->d2 + c * ports[PORT_IN][i]; - ports[PORT_OUT][i] = sample; - - vcf303->d2 = vcf303->d1; - vcf303->d1 = sample; - - vcf303->envpos++; - if (vcf303->envpos >= 64) - { - vcf303->envpos = 0; - vcf303->c0 *= decay; - recalc_a_b_c (vcf303, e0, vcf303->c0, resonance, &a, &b, &c); - } - } - } -}; - - -static LADSPA_PortDescriptor g_psPortDescriptors[] = -{ - LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT, - LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, - LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT -}; - -static const char * const g_psPortNames[] = -{ - "In", - "Out", - "Trigger", - "Cutoff", - "Resonance", - "Envelope Modulation", - "Decay" -}; - -static LADSPA_PortRangeHint g_psPortRangeHints[] = -{ - /* Hints, Lower bound, Upper bound */ - { 0, 0.0, 0.0 }, - { 0, 0.0, 0.0 }, - { LADSPA_HINT_TOGGLED, 0.0, 0.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, - { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 } -}; - -void -initialise_vcf303() { - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1224, - "vcf303", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "VCF 303", - CMT_MAKER("David A. Bartold"), - CMT_COPYRIGHT("1998-2000", "Andy Sloane, David A. Bartold"), - NULL, - CMT_Instantiate, - Vcf303::activate, - Vcf303::run, - NULL, - NULL, - NULL); - - for (int i = 0; i < NUM_PORTS; i++) - psDescriptor->addPort( - g_psPortDescriptors[i], - g_psPortNames[i], - g_psPortRangeHints[i].HintDescriptor, - g_psPortRangeHints[i].LowerBound, - g_psPortRangeHints[i].UpperBound); - - registerNewPluginDescriptor(psDescriptor); -} diff --git a/plugins/LadspaEffect/cmt/src/wshape_sine.cpp b/plugins/LadspaEffect/cmt/src/wshape_sine.cpp deleted file mode 100644 index 07eab074f88..00000000000 --- a/plugins/LadspaEffect/cmt/src/wshape_sine.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* wshape_sine.cpp - - Computer Music Toolkit - a library of LADSPA plugins. Copyright (C) - 2000-2002 Richard W.E. Furse. The author may be contacted at - richard@muse.demon.co.uk. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public Licence as - published by the Free Software Foundation; either version 2 of the - Licence, or (at your option) any later version. - - This library 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 library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. */ - -/*****************************************************************************/ - -#include -#include - -/*****************************************************************************/ - -#include "cmt.h" - -/*****************************************************************************/ - -#define WSS_CONTROL 0 -#define WSS_INPUT 1 -#define WSS_OUTPUT 2 - -/** This plugin applies a gain to a mono signal. */ -class SineWaveshaper : public CMT_PluginInstance { -public: - - SineWaveshaper(const LADSPA_Descriptor *, - unsigned long) - : CMT_PluginInstance(3) { - } - - friend void runSineWaveshaper(LADSPA_Handle Instance, - unsigned long SampleCount); - -}; - -/*****************************************************************************/ - -void -runSineWaveshaper(LADSPA_Handle Instance, - unsigned long SampleCount) { - - SineWaveshaper * poProcessor = (SineWaveshaper *)Instance; - - LADSPA_Data * pfInput = poProcessor->m_ppfPorts[WSS_INPUT]; - LADSPA_Data * pfOutput = poProcessor->m_ppfPorts[WSS_OUTPUT]; - LADSPA_Data fLimit = *(poProcessor->m_ppfPorts[WSS_CONTROL]); - LADSPA_Data fOneOverLimit = 1 / fLimit; - - for (unsigned long lSampleIndex = 0; - lSampleIndex < SampleCount; - lSampleIndex++) - *(pfOutput++) = fLimit * sin(*(pfInput++) * fOneOverLimit); -} - -/*****************************************************************************/ - -void -initialise_wshape_sine() { - - CMT_Descriptor * psDescriptor; - - psDescriptor = new CMT_Descriptor - (1097, - "wshape_sine", - LADSPA_PROPERTY_HARD_RT_CAPABLE, - "Wave Shaper (Sine-Based)", - CMT_MAKER("Richard W.E. Furse"), - CMT_COPYRIGHT("2000-2002", "Richard W.E. Furse"), - NULL, - CMT_Instantiate, - NULL, - runSineWaveshaper, - NULL, - NULL, - NULL); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - "Limiting Amplitude", - (LADSPA_HINT_BOUNDED_BELOW - | LADSPA_HINT_LOGARITHMIC - | LADSPA_HINT_DEFAULT_1), - 0, - 0); - psDescriptor->addPort - (LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, - "Input"); - psDescriptor->addPort - (LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - "Output"); - registerNewPluginDescriptor(psDescriptor); -} - -/*****************************************************************************/ - -/* EOF */ From f56cf60578c1e4de15baefa066312b8caecd19f5 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sun, 1 Nov 2020 18:56:36 -0300 Subject: [PATCH 131/180] Adds missing initializer to DataFile.cpp (#5739) The third constructor from DataFile was missing an initializer for the file version variable. --- src/core/DataFile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index c1b48af38c8..9dc413566d7 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -146,7 +146,8 @@ DataFile::DataFile( const QString & _fileName ) : DataFile::DataFile( const QByteArray & _data ) : QDomDocument(), m_content(), - m_head() + m_head(), + m_fileVersion( UPGRADE_METHODS.size() ) { loadData( _data, "" ); } From c39690d8e029c926880353475a5092abd445fe9a Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Mon, 2 Nov 2020 18:36:49 +0100 Subject: [PATCH 132/180] Arpeggiator - Cut off trailing short notes (#5523) Prevent triggering of extra notes at the end by not counting trailing notes shorter than one fifth of the notes arp_frames. This value being arbitrarily found by trial and error. --- src/core/InstrumentFunctions.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index c8ec24d6e45..42ad3f0a3e0 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -388,8 +388,10 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) while( frames_processed < Engine::mixer()->framesPerPeriod() ) { const f_cnt_t remaining_frames_for_cur_arp = arp_frames - ( cur_frame % arp_frames ); - // does current arp-note fill whole audio-buffer? - if( remaining_frames_for_cur_arp > Engine::mixer()->framesPerPeriod() ) + // does current arp-note fill whole audio-buffer or is the remaining time just + // a short bit that we can discard? + if( remaining_frames_for_cur_arp > Engine::mixer()->framesPerPeriod() || + _n->frames() - _n->totalFramesPlayed() < arp_frames / 5 ) { // then we don't have to do something! break; From 2a025c5428d856f8df700f9fe0f94cb8f4b75f78 Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 3 Nov 2020 19:13:59 +0100 Subject: [PATCH 133/180] Bump version to 1.3.0-alpha --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84fd91ee46d..2ef89f57599 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,10 +39,10 @@ SET(PROJECT_EMAIL "lmms-devel@lists.sourceforge.net") SET(PROJECT_DESCRIPTION "${PROJECT_NAME_UCASE} - Free music production software") SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") -SET(VERSION_MINOR "2") -SET(VERSION_RELEASE "2") -SET(VERSION_STAGE "") -SET(VERSION_BUILD "0") +SET(VERSION_MINOR "3") +SET(VERSION_RELEASE "0") +SET(VERSION_STAGE "alpha") +SET(VERSION_BUILD "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") IF(VERSION_STAGE) SET(VERSION "${VERSION}-${VERSION_STAGE}") From e2bb606341ca14c3c77b1970e0dca68dab3cce1d Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sat, 7 Nov 2020 09:54:04 -0300 Subject: [PATCH 134/180] Fixes createTCO method on some classes (#5699) * Fixes createTCO method on some classes Classes that inherit from "Track", also inherit the createTCO method. That method takes a MidiTime position as an argument, but except on the class SampleTrack that argument was ignored. That lead to unnecessary calls to TCO->movePosition after creating a TCO in many parts of the codebase (making the argument completely redundant) and even to a bug on the BBEditor, caused by a call to createTCO that expected the position to be set on the constructor (see issue #5673). That PR adds code to move the TCO to the appropriate position inside the constructor of the classes that didn't have it, fixes the code style on the SampleTrack createTCO method and removes the now unneeded calls to movePosition from source files on src/ and plugins/. On Track::loadSettings there was a call to saveJournallingState(false) followed immediately by restoreJournallingState() which was deleted because it's redundant (probably a left over from copying the code from pasteSelection?). * Fix code style issues Fixes code style issues on some files (except for ones where the current statements already had a different code style. In those the used code style was kept for consistency). * Fixes more code style issues * Fixes code style issues on the parameter name Fixes code style issue on the parameter name of createTCO, where _pos was supposed to be just pos. The existing code had the old style and I ended up replicating it on the other methods. * Code style fixes Fixes code style in the changed lines. * Fixes bug with dragging to negative positions There was a bug (already present before this PR) where dragging a selection before MidiTime 0 would result in some TCOs being placed on negative positions. This PR fixes this bug by applying the following changes: 1) TrackContentObject::movePosition now moves the TCO to positions equal or above 0 only. 2) Because of the previous change, I removed the line that calculated the max value between 0 and the new position on TrackContentObjectView::mouseMoveEvent when dragging a single TCO (and added a line updating the value to the real new position of the TCO so the label displays the position correctly). 3) Unrelated to this bug, but removed an unnecessary call to TrackContentWidget::changePosition on the left resize of sample TCOs because it will already be called when movePosition triggers the positionChanged signal. 4) Added some logic to the TrackContentWidget::pasteSelection method to find the left most TCO being pasted and make sure that the offset is corrected so it doesn't end up on a negative position (similar to the logic for the MoveSelection action). 5) Removed another line that calculated the max between 0 and the new position on Track::removeBar since it's now safe to call movePosition with negative values. * Uses std::max instead of a conditional statement As suggested by Spekular, we use std::max instead of a conditional statement to correct the value of offset if it positions a TCO on a negative position. --- include/AutomationTrack.h | 2 +- include/BBTrack.h | 2 +- include/InstrumentTrack.h | 2 +- include/SampleTrack.h | 2 +- plugins/HydrogenImport/HydrogenImport.cpp | 4 +-- plugins/MidiImport/MidiImport.cpp | 6 ++-- src/core/Track.cpp | 39 +++++++++++++---------- src/tracks/AutomationTrack.cpp | 7 ++-- src/tracks/BBTrack.cpp | 5 +-- src/tracks/InstrumentTrack.cpp | 6 ++-- 10 files changed, 40 insertions(+), 35 deletions(-) diff --git a/include/AutomationTrack.h b/include/AutomationTrack.h index 92a50dd04a9..c8a0009e966 100644 --- a/include/AutomationTrack.h +++ b/include/AutomationTrack.h @@ -46,7 +46,7 @@ class AutomationTrack : public Track } TrackView * createView( TrackContainerView* ) override; - TrackContentObject * createTCO( const MidiTime & _pos ) override; + TrackContentObject* createTCO(const MidiTime & pos) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, QDomElement & _parent ) override; diff --git a/include/BBTrack.h b/include/BBTrack.h index 36a10884547..5f9cfe408f0 100644 --- a/include/BBTrack.h +++ b/include/BBTrack.h @@ -106,7 +106,7 @@ class LMMS_EXPORT BBTrack : public Track virtual bool play( const MidiTime & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ) override; TrackView * createView( TrackContainerView* tcv ) override; - TrackContentObject * createTCO( const MidiTime & _pos ) override; + TrackContentObject* createTCO(const MidiTime & pos) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, QDomElement & _parent ) override; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 29348b98eb6..fa1d1692e63 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -136,7 +136,7 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor TrackView * createView( TrackContainerView* tcv ) override; // create new track-content-object = pattern - TrackContentObject * createTCO( const MidiTime & _pos ) override; + TrackContentObject* createTCO(const MidiTime & pos) override; // called by track diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 47cc0df3945..eda1299a538 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -140,7 +140,7 @@ class SampleTrack : public Track virtual bool play( const MidiTime & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ) override; TrackView * createView( TrackContainerView* tcv ) override; - TrackContentObject * createTCO( const MidiTime & _pos ) override; + TrackContentObject* createTCO(const MidiTime & pos) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp index 4a69bc4511c..c39c52416fc 100644 --- a/plugins/HydrogenImport/HydrogenImport.cpp +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -314,10 +314,8 @@ bool HydrogenImport::readSong() int i = pattern_id[patId]+song_num_tracks; Track *t = ( BBTrack * ) s->tracks().at( i ); - TrackContentObject *tco = t->createTCO( pos ); - tco->movePosition( pos ); + t->createTCO(pos); - if ( pattern_length[patId] > best_length ) { best_length = pattern_length[patId]; diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 8b2aa6117d6..a93e1e41e8d 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -193,8 +193,7 @@ class smfMidiCC { MidiTime pPos = MidiTime( time.getBar(), 0 ); ap = dynamic_cast( - at->createTCO(0) ); - ap->movePosition( pPos ); + at->createTCO(pPos)); ap->addObject( objModel ); } @@ -287,8 +286,7 @@ class smfMidiChannel if (!newPattern || n->pos() > lastEnd + DefaultTicksPerBar) { MidiTime pPos = MidiTime(n->pos().getBar(), 0); - newPattern = dynamic_cast(it->createTCO(0)); - newPattern->movePosition(pPos); + newPattern = dynamic_cast(it->createTCO(pPos)); } lastEnd = n->pos() + n->length(); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 70da1e1d73c..d73bdbed7ea 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -151,10 +151,11 @@ TrackContentObject::~TrackContentObject() */ void TrackContentObject::movePosition( const MidiTime & pos ) { - if( m_startPosition != pos ) + MidiTime newPos = qMax(0, pos.getTicks()); + if (m_startPosition != newPos) { Engine::mixer()->requestChangeInModel(); - m_startPosition = pos; + m_startPosition = newPos; Engine::mixer()->doneChangeInModel(); Engine::getSong()->updateLength(); emit positionChanged(); @@ -955,9 +956,8 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) { MidiTime newPos = draggedTCOPos( me ); - // Don't go left of bar zero - newPos = max( 0, newPos.getTicks() ); - m_tco->movePosition( newPos ); + m_tco->movePosition(newPos); + newPos = m_tco->startPosition(); // Get the real position the TCO was dragged to for the label m_trackView->getTrackContentWidget()->changePosition(); s_textFloat->setText( QString( "%1:%2" ). arg( newPos.getBar() + 1 ). @@ -1073,7 +1073,6 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) if( m_tco->length() + ( oldPos - t ) >= 1 ) { m_tco->movePosition( t ); - m_trackView->getTrackContentWidget()->changePosition(); m_tco->changeLength( m_tco->length() + ( oldPos - t ) ); sTco->setStartTimeOffset( sTco->startTimeOffset() + ( oldPos - t ) ); } @@ -1896,6 +1895,20 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, // The offset is quantized (rather than the positions) to preserve fine adjustments offset = offset.quantize(snapSize); + // Get the leftmost TCO and fix the offset if it reaches below bar 0 + MidiTime leftmostPos = grabbedTCOPos; + for(int i = 0; i < tcoNodes.length(); ++i) + { + QDomElement outerTCOElement = tcoNodes.item(i).toElement(); + QDomElement tcoElement = outerTCOElement.firstChildElement(); + + MidiTime pos = tcoElement.attributeNode("pos").value().toInt(); + + if(pos < leftmostPos) { leftmostPos = pos; } + } + // Fix offset if it sets the left most TCO to a negative position + offset = std::max(offset.getTicks(), -leftmostPos.getTicks()); + for( int i = 0; icreateTCO( pos ); tco->restoreState( tcoElement ); - tco->movePosition( pos ); + tco->movePosition(pos); // Because we restored the state, we need to move the TCO again. if( wasSelection ) { tco->selectViewOnCreate( true ); @@ -1967,11 +1980,7 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) getTrack()->addJournalCheckPoint(); const MidiTime pos = getPosition( me->x() ).getBar() * MidiTime::ticksPerBar(); - TrackContentObject * tco = getTrack()->createTCO( pos ); - - tco->saveJournallingState( false ); - tco->movePosition( pos ); - tco->restoreJournallingState(); + getTrack()->createTCO(pos); } } @@ -2697,8 +2706,6 @@ void Track::loadSettings( const QDomElement & element ) TrackContentObject * tco = createTCO( MidiTime( 0 ) ); tco->restoreState( node.toElement() ); - saveJournallingState( false ); - restoreJournallingState(); } } node = node.nextSibling(); @@ -2886,7 +2893,6 @@ void Track::createTCOsForBB( int bb ) { MidiTime position = MidiTime( numOfTCOs(), 0 ); TrackContentObject * tco = createTCO( position ); - tco->movePosition( position ); tco->changeLength( MidiTime( 1, 0 ) ); } } @@ -2932,8 +2938,7 @@ void Track::removeBar( const MidiTime & pos ) { if( ( *it )->startPosition() >= pos ) { - ( *it )->movePosition( qMax( ( *it )->startPosition() - - MidiTime::ticksPerBar(), 0 ) ); + (*it)->movePosition((*it)->startPosition() - MidiTime::ticksPerBar()); } } } diff --git a/src/tracks/AutomationTrack.cpp b/src/tracks/AutomationTrack.cpp index 430f54a5692..fe3622921d9 100644 --- a/src/tracks/AutomationTrack.cpp +++ b/src/tracks/AutomationTrack.cpp @@ -57,9 +57,11 @@ TrackView * AutomationTrack::createView( TrackContainerView* tcv ) -TrackContentObject * AutomationTrack::createTCO( const MidiTime & ) +TrackContentObject* AutomationTrack::createTCO(const MidiTime & pos) { - return new AutomationPattern( this ); + AutomationPattern* p = new AutomationPattern(this); + p->movePosition(pos); + return p; } @@ -133,7 +135,6 @@ void AutomationTrackView::dropEvent( QDropEvent * _de ) TrackContentObject * tco = getTrack()->createTCO( pos ); AutomationPattern * pat = dynamic_cast( tco ); pat->addObject( mod ); - pat->movePosition( pos ); } } diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 2bd0fdaf546..5ec03db30b4 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -382,9 +382,10 @@ TrackView * BBTrack::createView( TrackContainerView* tcv ) -TrackContentObject * BBTrack::createTCO( const MidiTime & _pos ) +TrackContentObject* BBTrack::createTCO(const MidiTime & pos) { - BBTCO * bbtco = new BBTCO( this ); + BBTCO* bbtco = new BBTCO(this); + bbtco->movePosition(pos); return bbtco; } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 5a51bb8aced..0f3f7b31e96 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -713,9 +713,11 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, -TrackContentObject * InstrumentTrack::createTCO( const MidiTime & ) +TrackContentObject* InstrumentTrack::createTCO(const MidiTime & pos) { - return new Pattern( this ); + Pattern* p = new Pattern(this); + p->movePosition(pos); + return p; } From a6e3958c93497136842930ff566de2fe74d6ee2f Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Sat, 7 Nov 2020 15:52:32 +0100 Subject: [PATCH 135/180] Fix EffectRackView appearance (GUI). (#5766) * Fix EffectRackView appearance (GUI). * Elide the name of the effect when it tends to be too big. (#5752) * Evenly space the controls (W/D, Decay Gate). (#5750) * Show the scrollbar in the default theme to close the gap. (#5752) * Reduce the gap between the effect and the scrollbar. (#5757) * Use always the same width for the EffectRackview (InstrumentTrack and SampleTrack) to avoid gaps or cutoffs of the background. * Widen the background in the default theme. * Widen the embossed space in the background in the classic theme to fit the controls. * Changes for improving the EffectRackView after reviews. * Reduce the background for the default theme by 1 pixel. * Reduce the background for the classic theme by 2 pixels and remove the darker line at the bottom right. * Reduce the width of long names of the plugin also by 2 pixels. * Put the controls 2 pixels closer to each other. --- data/themes/classic/effect_plugin.png | Bin 14593 -> 14355 bytes data/themes/default/effect_plugin.png | Bin 492 -> 443 bytes data/themes/default/style.css | 8 ++++++++ include/EffectRackView.h | 1 + include/EffectView.h | 1 + src/gui/FxMixerView.cpp | 2 +- src/gui/widgets/EffectRackView.cpp | 2 +- src/gui/widgets/EffectView.cpp | 16 +++++++++------- src/tracks/InstrumentTrack.cpp | 4 +--- src/tracks/SampleTrack.cpp | 2 +- 10 files changed, 23 insertions(+), 13 deletions(-) diff --git a/data/themes/classic/effect_plugin.png b/data/themes/classic/effect_plugin.png index 567bc31ae5280454230d522589b2f778bcac626e..6a759672ff59121a87eaf6dcd08bd8424d88f372 100644 GIT binary patch literal 14355 zcmV+uIPAxXP)89G9I)cUHbq4AOJ~3 zK~#9!?R`tHZCQ2RH^yB1JZ{~(^#F=00=96#FJfbC83?+_^-FiRn`<%Vz7-`J$nrokPZOh1=a3#3A?%Heb zwbz<+JihUbZxHOypZ&#O{_fGy@gI`vXYQWwPj25n!|gk_are$`?DrkLcPJF=x`(Pl zdIu4Mh_EaT`*nqMfvAE2hzf`RQGfstF`x$^<2uKQ;|uJo;Qt{sP9qS&`PK&i9N4wTq47Q$ z|1P3X2=WhYeusdF(A7VozLi0U+XaRKq);&Yhg1AGJH_s&UU2+D$LjG1{uz%6#02e{ z&k(@c*XcfxeP*|{0ATw3aoY$qp60X`s9YV_O3 zri*_b0ZIg#7S1^DBTqbzv$HdN_3JO-^*3IB?TB|zKmAAlNo=aawi zDf#}3e?89n%3po`KlU#F{`S4Ie*4y)cIVzb+_`fXckkTAy6$6RsA6m?(Q#8%Djt3siWT<=OD~y`r%m>K*pY=jX(~^RO^v8;3Zzo~o_8%{BOH7{;;~6lzeFcx@ga zj5iT*e0+r8`|Z!-kDq=TzxL@*dep*YAD*2lC4G>+SpBdG2?J>BU<&-+$}j zYme;dzy9g}{@q6OXZO!L-oN=C?%&_z=FOWpKR-v8J}y=Pg+Tm)t6LM8C(*dEVTtUb zRL!_3;{71P{d@Ot_s-q&zVs}y4eZB_hg&v-@9g}x1Iw!J86|IgJ}iMM!WeY-K<4F! z6<7Ngv+4>orm`69ht=z;mQWQU3TBE8?e`8)5I1{|#C0(YWx=D5K5B+&y#IXNSLY4&2!QhWFw*<@Z;|4Zx$LL> znRK|p*zYC-<#+hCYT0aldfk}$sA@!1jBGjn+n@avy!gHE;>8zV#8Xc_g`=ZmoSmKF z`rCK$fBx5>Dl?sZ^SQ76TL51J@W%4yTW|m22S4z^e)Hyg?dHupxOev+?w{Qo_sJKC zU0G`cm2uO@S4*sEzc_%#&4?f97fN6z^uFS`=by*<`FSkfT%j88O|LR6oQkIHVw(Jq zDu#QebQNLY8S&p^qx9i9&@qfIQ^>`eMXh1r6u^f*^g(>; zmwp)v1#<(b4sUMHp)N{|D4UBnB-?;m!sX#zMDseDoeu!q#=T~{UM=As6^}<$LJ8f3 zs?*AuqZlSCMn~qO>~mPxb6mT61#LOrgaf{O6{_}dnJfp=aPbj?`%NwR*+@bKbt=l)E8P6wut@(y08OK!=w3H3P04a~qv_u8WWJ@kzduXjHU~W+DxP9j? ze*gD>A7A;(SMY@|d;w2B`6RyjwXcBzeB`MQ+w3!y9-QpccRG*A>q{_jT;g&++iXS3!J)eP7Xd z2O4R8S|?;^7z+>tPhK<oebm4w7Mmw&ISts8=}U9Wm57cM?Lk$Y8$4yzpR;(QK7Ae8~T z_~x__=(1y%o{Mg|L`E3?{9wj|nQ?q{f{%avDLnDS6S#Hj7D(MliRFvFPW%B9h-4U` zCUJeB5mAtb17x8+*sf`0!?C>;V3GiIhW`WC+Vt?%IDzw~Lm{pMT#8y;5CPXPGP($_uSfA<}no!!HJ zT{qU*)=)eMn!*j$ylxui0ir9=IW~rZAo~?xf9_eVXXm(j?J872tC|7 zdmzmQjfn@;zP{~C8wz%$d9n46Xt}S3uGxD8eMJOE zvpA9G(BFP0fzXv;DN@bCGT!&i#pO5qpVojbK%;?Xsard-e{ZpfEt(q=qzpj%WuPGU8#?x~_Qs`RA~n zpX1siSHXM)5O8{Wij&iaKm;^qvq%cGx|tiWqu3Y(hBkNQbP0wlfN-u05r7%G3a|iu zRWv4a8H=T{ni0@QoEaIzj}3QZ$H#Rr5r}r>Hf<0@;<4%6`r5{9lF#dfo~>& z{R$*J(VR!RN49_K#Tcc@zx>#x=honABsye32-XqqEo}jzfxr+V5RVPz3q(D4vh~r} zbDkOUj%dR}Ob1|K`LE?z5n%HS^+%ea8GwV>f&?95DY1pq02-Bi-hgt>*P3zWxExhL zhZlbGV?Ph5;-jA!PUZZ34`K!p!_xN!KwF3!x?550jmUzcZh{L{ zw6=J@Gd`=p_sj9|4tMX~8|erUsExW0H#SeJqb@`WX2-}pA|CI7U?lwJen|mY1uwkt z4fKAFM<2fm#u3m6rw^UtcsU*qK&!X2i}a9cvZXw|W@3ougB?f5`C=O!+Uy}VTeQOh z4~wf1w#6jrIS35p0~-$X@f~qv1?Vz7TtN0Esv9qC_jO5U!GH?eLtm!AYTkG&K#WE_ zqLXqnNEqF2Iuno2i2MO)7{}T1mkW+vb}mS4!w5(@ykf)wtY(oLh=wO2w)NUZD5c?p znr#I0s;uQ%w=5Toq%!hKvHX@W023z8Oxj~|&jKu>81|AjB?|L=qT^U#x@`NG;BZW! zcpy;oniOTervtH6PB4U-4~pLlFAe+sxp@h%TTxX0+fuDv$6eYXqb4_reR` zKwnopcI_Il908=ba^)eM93N+0!N~^ zH`@28K+!&L9)=#7hHKj0SgY7v&B8+2$73jNVJ`M#MH;aabPvru? zE0;B#{29BqBe?U|oLE*s>y$C+0IQJoIHt|@16XRQ2Ug!0X$F_#Cuf9Vo4-*qb!y>E zMDhF!-@w{C9)09tP&*n3(D4aQPftfwL1P1e3FLn>0jq`wY2_BMSsSrXfM~iyV0J`@ zN|8|`WYc0}sZrE3f{G&Jp@0ZYM;OsK!@XunFewE>DG?7YXzY|00b?1Pz)dzbVFEY% zi5d^BL&3ZQplHYQm-aft(Hi%frAC zjHnY);x#16P^1Z-If~3&z%J$tAE|mKLtvbap+aLJQK4vP7A~Wp=s&nQ6=uY@-+2Bx ztos#@K719tI|3=;^r0&_IXN*4HqLFW#HMabb``-Ii>z(45T;EHNH@Wf7TQet@tJOU z?Q|@RK0Y%jFrKA#XD_s=z#0&^N&ppcZI)1xv?+cMXe46O0lGi{A@i15IULuig$_{A zD?<4#>UkkK8dQ!y=-9>QI?fRm*P;an0$3foXAm{^Ij!q~go`@m?&0;DyJ$>^_fBQ1}y2m3|Pq7@IfT#_y;pAvoGcUl7rwO20 zQo7o&=S+^54i+jjprCwOUS-gv*o}6+VElJLJW~w|;>N(NP6HGj-3YS2)^?4@?-GYs zEtGUN2$84e;q|16A=jSeQGrktt#R3a0%>Gk08=dmP;{~)ts;ux(lW#xfaS=0M$D*S zr2>##e(ch76=>9;=*9E%*o1xL9&KSPjYnEw8=zTTk%AC7)rodiqYKL5qpRt5Gd7fta!O4r&XIuUvx&aOKJ=P9M60gBkkC1ECrE6+m`^hGl%30IgZ*>!An}NF)Gr z-VLa~c~o!LdfBlDohuA9?83A=T(3Ng(b7gf$IMukHoORt!TuDU!WsJUyMhUkC9G!o z4QS>r5@_Uh>gXP(WS@xwv$v=->Jj)gvwQV#(MYGbxXZ{(*a{Jr7 zNIs9TF_($zNy#8rE%`Ji8dvfOhvRTQDS;xl6JPI!TDPcMQ2`(Tv^L`3`b7xlgIGmB zQx7$$u*TKyoqR(ry9K+p%w*0@-oZ)<&6@RMOKN$d^{^e|4Jpy%xmI46yUBU(biC$Q zGdsOD0*G-``2mXs)w~ZSbT4^<7o4qumGbueIh`ffW8ARX0*Kol;Wcy*x7gA?MU(C`Fk$`-i zht~qsp&;}&yYXp#7jzg&gc7w>D~=E`I#m+MCC4r~H<|lppGHY`ye7|!2(68L309j7 z&qtXnDtYnMA#5waDjSg*wpkM|X};N7i&&*0zdLd&${x3=@sNVP5b1~pYrFJW`NpN( z#7lrIS`bdP#!Ny}iV%{SOBYay7GzzaeT9lZ$r3d~=tL88o(z`)x*9^l-bANZyFjc9 zQv~Q;Y@u2XrZhpN0v(&d;^Wv%;ygdevLX8>vU6FC+S77Tt9dgA1lRO%EFNz7zx7O( z1sjmXHYtm3Zixb(An8VaiuWxEMst*h5M5^PM4Uy(F2+5xd~8)6VS;K@>%CErf;gHE z&DtSB1gw?=VWRKorN=HiH@JAg$x~~&7{8CK)kV6PrS|{U2$#F#NWps?zegy^ueCKJ zh()!hs6;#(qy);>T@x&0YgvgXYOusR)X>M=7~|vT)B$BPPu;}+yj7EM>#ZyzgMOEU z$^k%hk#eybe~%7J4iD26AStl4H(N>ZkAi4Dj!Iiy=s} z)y?X22?MLA;Nd+g*T|5o$OW|eeS@s2;Hl&ryoRL-ZA&&gCD%@pTHiRYEiFE>N6ZM8 zHN06wXKI}>w4+19+Q6IVBg|OWj@EeOesTHZxQbqS?9y{R8u6T!2GAtdS2Q94!mcrv zWrt9hIq0`df|^vud?C5ZIPc^iHnV43%~q?*pSpl8&pInVz^c%%UZF~iK?~o z-4Z~_Ycelro{1sFs}3+Azu%1kP4PzsTtsUj*HAIeP zjpQ51JF`XSW{C1S*!d)rR6u$lBs+p?`cvvmvJR%WWN5nO6cdw{>NJt<^ zUm?<=QcyK#mHDvGF&2%Kswo)t;XM-Y0@B2tfM7cZO3XJ=_(`CD`86?0dA=ID3=8qm3_{9ORbqq zOXb>7Fb1ZMO4-zgWC|;LhJ>0NFi{O~L4pOK1wHhRqQb=^wdJoR*xq$Z@WuI5Q?OO6C(Gu#_5`-oL-f1oQTNCDY$oQgTm!B&ZhH?p6jn%;o%d+6;_;@hC zPL4nXobC4kX!tc#$;&79iF4fy44{I*1?uKuwZr!`w_cDHa(#&hr(AR0`8{%DuSFK2 zLiAfP718Wtiwzs%G6tu2HHV>LEE0fNJJw#%35>lp5^yp8m?Wvy+r-1TiM6Q@YR5nU zN9@^EK!e`#}wn=i++G-@Qv56xz zqqNxmzNsdVn_`7bIUP2QupFLN6GRc2iop|jqLZMUElkbpJ`k`oz(aM@?oZ|TgN|Kt zZak!7nQa5Kw&3{q1iUQJ-tp9jpRl#k4OpPu@1^jGg2Yh~E1Byx|XDH?;yP znbE06UaROuCaYRt??dXU2^Y%<#X3fEUl*bSDjrgFw^lt1wott=FH{2?YGN;X5k*o< zNDK%iJzq8pUL_H;Dp{36oH!8p+#b91T)2_cqG>^By9LL`$5@sHXJ_a5@|Qo4g%m%$ z{sy{;g-2ZKjvP@i1*AQ*!K;oP89J-(c{yocp>AY!Bq}vB=@Pk%8VIJ@4f_U)pM%sZ%h-VRp#x1rdG!GT+y^;kcj}w^;{m6e@x)gI?23doDOxQn(}qi}ssUJ&BhC$TnBC z`d#Kq{AfvWMt!%+eAa-Z>Le5mS2m;5+Ca=&n_=BSFUq`d=4$E`j`;hu^ucn`3v^Vh z7c0pRJa+lH0tH#^iJ6w=D<6kY_CSv5?fW9f)7Q(W1 zMO(}>7|*XY<00Db=*CH87r`uy=x!g>3WCx~;XF50zMqTbb*2G5@Yu!YvVE_M=B`f>MY_N*K8ecC|v3 zqm7}V#RoKRd#^baYF^{cZO6EafQxjz_>+$=ksi_b|o^r9>_!SlZV^IkQSM zjamjF6HZU^HNr&(VX;1<4magNPLLA6aVG3IDsvhbz<|z+qZJCIpe!WlwPz=NB(3DHv|#GPFYyvhG4THX=ezur#nS{z#Va#;&fO|k)9@c-R9a~&R+ zDCf*2nvlFR(6E9YOGK@PqCNGYN6|&_<~z40Ej6uY8zwoBoa|m**VSUmkyB{z!(qm= z)Tlv4vt2axbrb_KE9D5R7&xO^#O;9Cs+-=KUWEDq!n$gR#?1KJSPY))48W^T9i!&c zMwk}52tIruxpVH>x@wS4`?TRRPa%ufr;gkBen}R_rg_l4RQFZ~lUnn|BYOF<%g!Yb z+OpvIcrzRy-HNd$( zl!|o*loz&`_4H`L?|kVu@buG9&Cvs#SNG40H*ho&583^cbTbK(rX2MHBUeFECK)k7nDgvK~&xgTfKr)Lyo%7 zLmf14y+$o*4xzM`6bctlBsB?(c2VS$T2NR=ey7k*r35s^CIzx@`Dj*d2cL)L~M3SRiaRukQ5!`I;s zF?#5db8=*jy}w_~j7g=U;(=dbpC3{o{Iq@8oK$enLj$j+ir)p>`dyc z^fP(Ve|~l!&ph*$1I7PufA3{{_^FR!S(Z}osN*~gJRk~*D&jUKBhdI;ikgwAVrebB zQ1_b_JlYoY5h6k(`(D!Mp_%UvWujl;Wd_Q02TYtchxrFbtw?W`hE@S^!5 zHazkU2+P$4f!4*s=&lK|#OrZBgG;~S(yI10Ps{=TZ!QCsSd)qF8-v8eGma+*mB?oai3o&{4v6eSu6+uY{X zoEV`g!(2LIqdZaSTQD8*Fr1~8g(iR~HOV zA91HtHUS@4lPFRe3u2Xyy{y<-(7zf7h{S6|!0M8~M(I%iv;tBrP{Qg%ADrES2f|fG zyHHU93hMoQy({`aeN=yKS;D6e4BB zodZZ^YR1NHpu+j!iD$T~8w$ze4?1?yxuoc84{vhs-aRW&YjmahR7aBJ;7RYnG++#QOOsFnAga4s;gdY6z6__ zjvXDL@7GaZD70nSc8>ehywo~O7P}ua#Z*{bNwB6&6?EFrR62Hj6|UG9#uANgRpNro z(Sh$D;aN9cX`d)b2zGTZ3xqaZ!!ohFCw|`ra|+@fig5|GB7HjP0K$uoI@eo-6uG12 z(qosM+iXH1Fs1?a-m$7cZOkUKbDmo8hTipoK7<#rfxL(xtVk&L41$~UGmH`IS zY_96fWIN6k69p;2UGK?^C`cz6yV{NGfP^#rGiS=WL^tw4-z?_k{QMlJrzhC=j$3zE zy!YNs{Et8X3%v2>JNU(4`3yul+OpUe*u{58&J;9;HW@=f!^*2y+IRydY{aw05`-ya zC_UvA63k+YzNb8iY^ZH01GRJvGlMZBoTb9yEPB*hx|UWF@5i!`qf531R7%ni6+yPL zYeF5a09vbvT$&@9a+-svPVM%gt`pXBV}0mwJZ2b`>MdhJ*x|9u&Q&uoVpjUla$!z1YI#P;Eph?ffGaAqFsBi<&=KLdVTX?e)d!E( za+z=M`1M&|-jip~fGQKn?m4Qe#1ypDxxff|A7!oKp3$_xC#4P0CG=$g_s`CT(UF{2T;+#r4$@2a zaVQ5tu$`=-N`Zg^z+ToUaZJ+~B}qYA<|EI;?&=(?8qp{Oz zN;M?Rp?c$)uhO%P-Z#@rFTI3Uu3yLfv$H`QbPWN$h>NKQ38Vk8dw1@Fn6Yc66S9v1 zSKWo(OU#o+*|Cuvt%*q|T@H$UylpJ>c6OkQ{?$Yfco>8z)@<wT#82@&JH7Jcpg5 zqLy^C)u2+ED+XsxrQlJd?i)&LNK2K8h+MLA@ddKVL7p)-zPHbGsQvD4`CJyXWz3Ee zy7bs(=dQW}%)y0Y5cT5DhYA<-`D&whOcSs4=0E7zrN?b!_)zTQqaFIX&qUmdL>_2+DB2ao zJ(vvv9{41{Ak5VQZ50GfqI8f}MRTl6X*8@Lk#q@9l7>p+JNh)Ew-U#7`pg*1%2b}B zOAE{eTe7wj={*`KGiTbLj|Q)94N*1~V9?piJkZiC=QksJqjG0A0(QG&w6+`Jo}+5M z=q)4~j9m>layx`e#KJ4e-k_pY84Afm3h}jw zTo7M=>_O*BFB*ZiSY_25dIs4HtR7_xr=(5NTZ(y-phXDLUSqjiGxk9RDTGGkQkwy- zteq2cgV$_?Xc{bUutKdpL&~|1{-MmTorENdBA1AU1mW=JCKqn`UXS@=>faKy} zE-VX+@4M@sF=H170>S2HRdh~qL4g{#ZiEuO73kd%z@g)B1kcj38{v=hiME0rmZaz` zXc6CsM)O37Xel>~*cj5KQW+)>#cdvZiKPq&b2VPC-nYU~uw>tBjm(T?w+yx0rJ?uL z2+W0jpxDeaE^>98@HADF6Ujk_bs>|Ddr{*l=G1g=P4_|Gbt1chB4+@CJC=qm9 zwX_-du1V(*p9mhn(HNT<0D%bF(qe9@#t?uZp5ZkHwAs2cbNWKN89`Gi3N|fT+I5=f z)8bKb=cGwaRj*5G0e`fJPZl+(P0RJD3vsrTQ_)xwYYWM#_bKU|f=9~?=PQMW z^|7@O%o9UKKNDlwwbBPak8uf;w~JBan6$yOpdEsey~{L~gd-7M=6Vj*1v`N?}m zRn((uw7ED*t>!TuW*gj!SjeO~uCs{91r<`>Rtuf#Uzr#SPhMNH5n)4)D~xcp89`OC zs6yi&-m-;3tNcVhr=F{*`HK^T8=idP2^=3Y?%cjREmE&5K~qX#G?x~zKqP?#?`MLe z%cbj}dY0fD+EsPk5S@$KuLsmsqfZNQz*nUpTlK?ZS(X-Kfwbs~Xb^EHMoyI@u@-qz zy-=%nx%TiwI6gkb*|}A_1;%8qgUp62_h?06vrp0xMH$hPd@b|9)0wh4MIK}?1tJ} zALFBptu6DyQfvP7+%M-R8G9m@xEAHB^3c6|fQER<5a7uktx!IaqTvt%Ep#woF6>Va zB7|+01Qmu-?;yR7kTV$~o{PD{kwS+Gm#)SPDrnx-tJm=G)rW9)c4Z7IkPRWRd;f$+ z3$c`*l96;**KZS+_u zeoxJTm`cHUx=xz31}eFAAfOFuljrBLYy~;md=}@B(CF6+fe&VrB>u;LPKfKPSXj5% z-{ZBY;+^;2#T#$FiT%3A-XLI~lhlp+6}d*&Y;$liP~l{Dh%>UOND242SIivzOjQ|k zv#2)ltyvo?im+NH(a%duiy_4%uv!z@G@x|wUZ76^g&UGCG1e>d@ZjaZZEyLm6u0i|TFRcu84a8Xi-%NfX14Z}ZkE|Og|Jp$8p z^CS&6M%!f0cF1b3fAiG)br>VhuX8|h8C{9P8L5+|f%Koq=lFB=?r5EIu56hg%-yIm z(Ud5sO%jE&af);Xt2c!*ioQMw*N^Cc2j?rY6sU9(l0b&SvO_YakVotCEuyi8=uX!)`ra{&qq8qZcokka_v)%v~H%* zN83y@q<|{7E;Mg)7H&C-Iu2vf;g^nZL8o;Su!tCXpzanB=#634ciFOgH)miL8|^tjBS+sIW{g*Y*MSP zeWJZO(X6xpM73!Yv$lDi`{c-TVZ?)Gsv3xMgHuY5RR#*93_TOzXFl=-*1qEUt8b#O zz1)Wl&=nU~Izf9CAV*!?Kvfm?1P&WpFWOD(oTG|pN#zOBpkhb29x?^6%brM93n5h2Q+uPe5heuzHJU z@?I3_Yq9)<{bp)KJm4VwO=xi$BjVc5w%@zN_)Jx^1g(yk?}hG>Qd%`(bgJwcUa!Z^ zklquvbp_AfBhN*+XPLq}^4@#v`%&G3ANk*}4g$BIP|^Qz-HJ@BH%TKZ9qVeHQP$ z+cjDF~tW?R2Tl za2jD+vX*%G!v1@Kv2(tZPlY|<;T^3*Glv(n}RT1ANg1gs$qF<5Usnn zgv4(3sWKfz=X__PqUGmI$XzcCl3u)>;9|shiahi}#iG=JEQSb&6e+0U)~(wI-RFPx z)vrQCaPR(E)SY58-Me%5^;<_bKMXufJDUX8ZS(XH&@a?^g|#D8RhVc$F&%@MvqY-F zq}FPAQKu2$pi{D2i#);eGk*^!yS;)KF{q77ShryaTP4Oxw6)_?bu_DKiTOs6xu9gF zlh?HaVz{+W?L(qZz5x2#!@~~=V%I42#p^vgh`|BMqe?FYDccJ2M>Cz48X-*+Z)RP= z+N@F zy9(w*nUqn-w`l2*(<0=-%n^E2)T$q+pq=qu+3?aoD?iqKyqF8 z7`f@;r1h71cS-1l1S>~E>}Sihi*Qg2pOH$;ql&Y$GkoX8AEW6#{P#ckQ~daq8+hzP z{|xu<-NpI6+W>&;KYIBLz+C`m1mN*c{p!Db8`$g5|MIWVV~;Yt z^WS7rl7v)d4(moJnrF36gXXzj$$uo@{!$t07PmryN5DT9H@Yl66b2CAAd;Q@_)Ya_eb;l}fE^tcA94Z1^F+ z?CAxAOuns4w+%#PM%17rxh8|38+A!rYil1-K$pS(PLN8B2*T57n;`?w=b=4MPRkS}R)f-(-y5)L0-g$kX6kXWg86)J73cTQI#I2j}f8oiGeC!XdT)q111Ux)-=ICZ^{*(Nnf5Pwj3H(t#C@gO^ z0_IJjhf8ZyHK)Imm{i-f|IWRGzvo;2sh;lN|Gk8N_`*~DhyPwma`%N-Nrbce_vQMJ zUOEGC8^8|${274%2H-`1_YS}ZiTT%6Zvpr^ zfWHB7XOc<=@DP9x+5fHrhpkON{qxg5|DXR+0A~Q+1MoV4ckRFR{{gB4J4(zvMhyS} N002ovPDHLkV1fX2eboQ} literal 14593 zcmV+cIsV3pP)6quKKC`n zs$wTmBqQ07Be_#-S~e^(l5D{P2bl_)1sP=GUl1U3&+`v53oklsPWAR;VF!+u>MT_CC;0HOjS zKolSVL=30`02B%$g6jAVC{$s`14IO>3f1vu0+9b#D2<qoqM=9xeF&;S0@zw(*=4_^M8JL8GlyT10<&;N(s<=@@D zch+y;y3_95yN5e>?&9v9yI9wKYzkG34JA4rhN_(-LDd$&D2N$Q0olR@0A`LG&R{4j zvm`mq&Q(NWeGn5!ZQ-lrCh!I98&b#Sg%Y|ME22D4T(Cj3Y@)G-r7TvSjZWF! zO5>Ou&nXSgI4_>90S_xfwrPlS+o{_6+nkH9in0F{ZqkVw%<_lPCIXI+kMJ*k|8w}$ zXP&`tJ@pjs-o1+#zw-nA{ttdA*RNe`Klttoe?Uwx-@5t1+W_|TU%vXk{;(1K`Tg^b z4{pAX`}g;_dGjXD&(G1Nj}uftArODq>ed71K{OsnSRgwoRWm03Z0t<$-@AvqckY(g zrALVeV;46TMcD|xtMk(itg*UBl)TybfCOW$jO*#{d2GR`nzc6jHO>uaOl2Y1h1K(! zR~SST%oLm1?;W5ZZuT09^I~SeJo4h7RTdzZ<5M`#ac<1b7Hlp=KelgLUrx)i;Gu^e zGDD(q{P91T7O^51ZN6@JbS8$_y)RB1>YktS`7q`P#G?Zyf)uCBWiRFB(&6S}zpD(C zd*RosWuy7q>!!>{R*Y-;-S2Jj&XK&hHLNK#sB`FUsYy0 z`_>EJ_%{H)0pQK$t+(I# zjX`m3es4U`qv{`hA+{Qg;@~|^QJSxF6Q3)k<6XI?n z7bYr(NwGga!?Vvmi>p_!;>L{|xO3+Yo__jiy!`5S@Z`sTiC(|)qmw5;{tJI}`B`RPx#{ds$1W5vC=Inq*H_i#E7s#&GPKm6)(Qx)ED;n=W zBZW_EgbW8`@qysUibhgtpGo8IOav?dWEtn$EcR8qQ6yo-sEH-cBg9hGFzgZ=+jBA= z3p7I1kvECT_!~0BBPz*@Z!G^@Ra+|rWjkN>@GV?>cp&$t5FJ)HUd8zwh(Ia>Vj+T} zgFKnH=)j)=Z1XcSj*m|8$xl9k#~ynOw{G3CX9@rv5VnPi;Z6PAK>;R(L9vB6E?+D^ z^mXD7kU%8E_%!h|5AO#W5e0cTKo-`6?Yc%59Ogj*CJBIQO`#yje#P@IJcsq{9G9a3W8A-g7GWWkdo%DLPL$J8I?50c=E2BwlgXnih?YbKg(7-j^FZnz zjoL5@23U=wi|xk&N{rh|Sj9p+&GaHadu$kA9)^KJKx2d1eW%vO24=ENY#+yjRXW}| z6TJVyE&Pwa_)EO`_Ph9JpZ#6jyMI6OUR5+e9|mxF89TnAcL~&Hj;TG?pQDc&#`t?g zJJ?VV52e<1#fvY#fc5+wS0B6#<|BZB)6-L&oIU^|pfQ^TQlQn1+<+a$rXVo1x!a}> z!FXj@R9{ab05fzIU;+B7XiOs~5Jh7(1E7&4Gcr~m3+l-JKF))QK(s5jX@e9Jj}7P6 z);47;7_(T;=++btZZiSwXCUE$=Dg88vHh+WLo@|LBRL@hF0hUeZ)pn%4FrY|fw+yF zM8v`Nj4lFEtBb$_Io~);@i!0mJjHJ8p00$O7G@GkwBeztgDwWQd!8A=HPZz7qd@l_ ze1q761Rde5<#0p*8kIaAng7{w$xmE|*M9sHzYM72u zv3=UwMH{+dwm?!)3{ysbz~eO#jO5;2Eh!+Y;H8(oiQdog&?A?@I072s^np_xFUM%DO?d>7our3R z6Uf5T&rECy?pP4xujBn4CNWz5di4pGvdYy&}DeG zfaOahH-504*Cq1+22|J{^fCov^QKz|GDbWik#aLg7~O9AfS+M`+St?}vCSeNoL_24E4zu$iG_mCE`Jf=a z@Y1l~pPP5^x)eo4zfDDp3}w{3r1A)f68j$^!b>lG6MbFr@YSopas-g#(xnG*a(tXM z4}UOXVUvb)hF~r#(uj>B4QOKpfx)9*V+nt2yFk;r-CW3jrb^+S$fYNVkQl zGLl;v-e7nk9{aQ`{QNEzWJ8#IwYt$#3;+OO2}8h9*ky>UV2n*N!Ui7qKzVEs+c+I! z8+ZT;&D^1O9Z?GyXoRkergjgSLGKoNH6Hm{YYmMFjmLM=R@eiSUavsRUZ)xn)3`^f z1s#Kh_FaL-0d>dv;o0q~)^x%c3%2hFiAFj)7zUwb9u5T>x2$=2Ey>pym$h83$LnC6 zm{oe;C@hV&=+P39hxVI|R7*Lq`o%~qxD+-yqYB&Xjgoy+i(w*)7hn1&*52{ZgI7T9 zXrM&LCpbMl9Z?94#SbQse`f+#4bRcaWwTiqF}F`N-4!r9Vne0)s1c@VvCGsb>J&jM zk?|lvgr*~8Xq+KlvqYDa0*RD}0T(oOdWwLtj14f%{2Z>Sj?b|~+aFqof_Vo((aK6= zL$$adTK02F*umwQGFi-TtP)16QOn2^Jwx~0t`_kuEJP{R zW2uX3tcyS%ot2^hHO@urMR*5Bf^CqH{s`%^aL$>)fdl=g^p=5?4$#U&zY&aB7$`|@ zB2AFYkz(coYB6V+NYx`*!sv0a3XMfXC05ofN=C8JFS0ppWW=%GeDMXW`xOsexeVSN zfs}Ciz$KiVoS^xEMJ=yz@|s=9t|C=qfwgVc!L+II=q5nQzj3|zOs~8yIu=wP#|&DF zXJy@432iE#2F$GzphTRTB~T=7iqiuIiFkAXED%7*yku4e$9ZZg?-z86P_9Ki4%Q=?5qy4Gl&_D+qh0cXQ4bXC_n%&ysc)&Es?sIEyUMe5KK-0hoa_}YiR(f zh3sexS8e0fG7CixhX_j{2*4v>lYy6L0XS46>f=HqQbrm+ol7llJi_QG9pthAuy_K= zzSAs8baE*1ME)44mECyv0`7-j`MX!q*F7G*e2V4x1Vn8B3nxdz>UnW>tO5Yd642GI zm@}MMI#{Ssdr%Oip2b_So9q0)_;)}!Q;iDZhQKUN0~8(I$gaLdca6t&i9@FrK03>T z$W!?6cGASqY0v7Yz$J>-xNJUwG&0YDsYU`QI#~f$5e05(8CDKpa^y%OPE@dt@TXCO zq8G0{zy$P>C$xpJG#=r+EnGKp44wF0kvmPLkE|JRb5G_**ETV-m8 zXb*vCngLHfUBbp{ksy0nq8%caZDI4M4rU;7eK$Up_(}YmQW{j3NVFQ&A`*xhYww`8;P}#2hya%^o#OO?OIWyN0bUhotw87s z;K(5D#Ot~;+G3%PSu1~Fpj|9@wDG%Uu%@sj;@k!r=gF?7x6)YL18iVf%!3G2#C#Az zg~xf>CbewdjW1fmd9U%H))_#^iuik%S7R%WP+=fJQ{@?I2?v^LkSf5oA_Ec zWV%HyiVD~OptTVz&j)u#gIJ|LQw=ppu*Tr-m3%WTy9K+p%*4!1p2129-I~>6OMZEx z>ahKdHzY%k;ad5*+(FLwPJgfY6wThsH~3VmD&X2c zZONoc^Yc3?8qIYyvCTamEF1l227&zNxHrmE2Nbby;044A#WF%nX0*20_ZICJR0^7L zi%K{(qgCX4t%QPB<5P}wuVK!0eADn^z>kiyLoyI7d^NJK`vSI&LXP%Xv6m)>d+AQF z=fPay{|ytIHl+?i?XU7uxYf4Ekj~aD1S}g#v=Bsz!fl63riOw9mL8g|M` z42EyvW4PcPvOSn{fGphQ4=G=YNRmB`SJwi-$s%a88=uy8L5DksTL<0d;Yp5hNr3e+7xpeWA2tn2r+E=Iu zlq?xDBu+G8<;mD7psOJu>bp1SQIJ5>k$HYX1gw@X`SHM1ICg5BxN(kTEho~&EbaKW zN-qJ!xKEt~q}e({wND|sR+8; zJ$r+c6u&5lHsPqA)kPje%Ak^iZDj;ZY43BZ4YI{6MdoH10^$!3HVH3k8%meFO^&#fictOsl-UxF*Vmy@g6c-v#I58WU4} zurLy)0yat&htnve+u`Ann^nW2C|p82EzM(8PE?44U>MQ{Q!YbBxyhB6IDWY&Oc#`| z()$Qyr9(WSB)zb=P6`)n;svlvRH%EdixtR3xnglUr}GdZ`5AK}cqAss*`2 zWp?Lc6^z^t&1gu5QVJNXl3}4p*`!i7wG(zH} z+r*2i5K5GoGBJ)EvyeEsF_vWkL2-O^grnVVSZ)l?ofNI22(MkBBp3%2Au-WdFClD0 zC4AGuMW^gzg6LEx_iJkfg$yX4OY3{xu*Q{3Zve52)-2>871VdyrNLq`unw3=*bf!r&K(UTCYPH>}`IoLT!n+cx3^f`K z)g9%sIHO|EnO~`OI%=sz8`{OwL+~(+HVEO|n1^VG&jsf|f&?=_3rgr6L4{jKYD>T5 z1{>bh*o@vS*9T@mE=o{ZYMfW+sRb2zHZ2=;+u=R(#%P!i**1zEEdn52%~5C=i3A(gj=Mj8UyVc@n%;6tpic;99A$oXa}k#BX%#jm}>I3LVQG{ zi)FMhcn{7L=9Ol*srZXxp*51!*uW8vQCfL_-Guw+hFBp}4u?$xD~E^G1WZJxLhl5W zD5k%$owHPxolSlzm5(@j=Z9n!4zzW|PeCkEs}qs2nIMyL)gsc35UCS;S41adtK#%3X^mXZK<#0uDtccB;ZRoS zauiK>x_bL?j}GG4WJUO7G&{?@##rrhv!}lj5}iKeHHcoMtg6-aKBBH(-xzVd7z?{G zXP8;Q6AvFe;g+7oSg2l@2dV)J6#x*ui1Mf<3~Q%$Fb*x+$QmjJJ!utaiBPmv z*$hr=166BnhHM9ADD%LXZ>dx0;jh!u`^rVn&rz?gA(95N`V=dHcRQS(T*7W?SobTw z^!rbvsp3c1-@^6l*UdO_>Q+cHE;JiSWBEJOPkKn1A`|bdN&f^@71=ItX~{k#P0^g_ z3zCvLmrWHxuLi8HMVBxP7ZT`~-u(TsI<&-T6bjy2w3nFKns>NG9jHny%Hf7sFShcQbi!Bu6?X_mSLc5M` z97A@2%0j{EcA!?!l2(f1xuNp)Tr4fydtJ0s#!T9PH#!eahoKVgsfFre?Ug>O zC^zcd+EUU4iatWhM$rPXYYmlqH=PWEP955*7VNr(pcKrJ5-u)-C9M$UXhmpfae(IS zw^JXGmQajB1DuyZeryYa7m9~8DbCjV(6RvT9i|ow3fP&v_uuP;WmAcx%Wb0{TxviS zRqEkuswUkq2#xaEx`l{NQ0d}c!|FF)oj2UrMNPDi4V@wdxez_%Y)s|hS^*bQs4f)U zuz(ILnIwgxgKE^7Vlj8l^fcM6DEeC{M*mDk@xTpseg`sB5a}VLP6?IomPdpkM#oD}RQXE=LWvZ%F z2S*e|RK|L_7Vn_7oMM9TmzE-yMy@4;xuvv?c(kmQgdOZ}4_&MXO5{R+rR6+ss7~~H zq86l!#ZI>zaxQ!H(VE_;0Oi=P97aP7w1xc=Jf6Gzhdh0bmuaO(2~rANXd zkM`^~skv4Xs5g1~hB{FF0Tp~kCgjeG!L>%88*@Wh({enLLZS8WR43NNxRxXo80bCn zUPVCQ66Kn?1QU{UQbOKjf767PJ@L_p&_(dpySFBtG_CJ8yit0ODGVaOx~>*SjvPXJ zACA%vLzd?tdRaKQX6o6yRC0B)Lr0-+Mz4t50ZCOiy)V6p<^zOv)ev}@aW=JfYXdSc z{@_c$gJ+(322VZp6dt^^ z;QN338hE!e3q=tb)=F3zSdrI_GIZH!TK&9u64n)s@WjWeGMe)?H{vw`x?s>fP&O7+t_r5nmgJlodT`I2H zLl$>;l<*!Yc1H`^(y&`b;qSQZK&?%(+5`?XY}Q=Z3Lk0`Q=PVAn@8O6Uc}k+rr^EagJg7shDTvuEgZro~QkH6O&&PDa*pf}SLnBs-0%w11R>sk1DC zIO(cJrAd#$5Rzzm6iO=4LL)3*jz);{#V5G{G{;d^uRjJ>a4%zQ~jq4dsCkhRTFq3+rmC*JyQ z$Q@~RFpS3NKG=!`wA~JE*_n)4=|}Pe|NQJeo_+Rf2kQIZ`Tnc;*b|??vMi;ZQAcSQ zP+&bRLz|(2xq)bWm!di(s#sbJ@6-K81t+!z6@-Y;$Uc|yVb6DjO4C^(AOdAV1164| zL-~WRR^+itBdP#6a-h-0F>Fy&8=iOvgw^VTK;>dFbk}fL;^*;x2A5vLrRDu3<%tpC z|K>6bi8UGJzM)78_)eqk|MfTC#L3ACUc2!cxY3|NSrc5SH@Q12+&ZEvzqQ#YR5!x5 zVK3`eIaMS@?`NK&a-S792(vaZ9f-uV#qeUU!mOQ|dYKCm89fHZc)Djj=Sv9ln5HC_*i*MNZ4 zd0kdAR{*pEQY=nF=R>cX-Es$#RmQ7OQR@lX{CvJE`apG5gR$WrL2BSjgjI9%Oq8CE zS=+k{ppv1DZloW(6n468urZt=Wxbu#M`e1&#%7>G_TY&(xT-7RDEiuC1KhiJ&kD$D zaX!aHM3UsijpDC{WLKbIuIW)>=H92_T~u1O*+Vf+HL@Np~*ZV3pCv4Qt zSqC4SrJp%c)*ZT$0{UhlFX!jyI6XbVzIWWZyW;)#Z{olI#b4shx8B9C{>En^($SX1 zKEO`6J3^+QF*M1T2O8F0y?VwQ;9w(WEtU{Oh*2o9E}TD_2E`V9Pbm@EP}NX}S&czr zFvf#3PB@%Ik2*}((hlPNRAw=|B(1;FjD~m!vi)2W)^K&sS{dZh*vORA6hw7ukq`Bo zu#~bjoGK+E=2@4$&jvIIQI8XiQBd(;7ZmH0yd}x8Qd+a`_ z=m;B%A(3?jdQWzPZ=?_2f8Ufg2}j2#_}C{t4I;+Jf63s`aF00wE%X{n;2jdbFzA-1SGXd<7YD)AL5<=T)dWo(N&I=I1Cdw9fdBK(Nl z(`5k?U6lCz`)6mvXh_cZt#ZCKUyx|ltP?HDggq{;5gT%7FI~f-?6c9s%K>shlaGjI zFKd)DrfG0In%xKt->#QRVRu45B98WWRBx*Qbp zc-s`{E$BcQud9g`u*u^JW>zAy{t%&Mx#>V39>d`6DeLwW$G+O2Lg1M>1>iTeO zcnu||9B$hW#y#tZ*xZ=s9oRGmCJHxoWQ$}TCF^v(O1*#3PbEr zkz#(o+Bh6r%ptBKF+}?KXotS;GkK0ej?aZEG@n3^(=63Fu3Bn5!%ACN-vQztBQvRn zhJ^qNWAJZ$ zqUEGV(`4pI`*WP__e1NN7#fqRtrNxPZk9$jw^kV&=dNx9>~_a!Z8t(ZN6&o0TNpGH zMm1o^?eHuS3#ll3dy0-^s1^?~p5)Gn4p6`O%}_z#yLzkHZOPoN3x^=0b{(?7{@E=> zA){4bZvcfs*iz6qfr2}Yk(<4e5o(!Jv&Fy{lNT0aQ1b@qieX{)@C3o$6e%QD_c*=i z@Yy1hRde7NWHaD7?{$Wll3v4$l*Oxn%fj@@_FGvj#XP}JvS!jT87L2LM@|LIrLq#ZAiCbEm>>jkP9}(l z<)j2IR$lEk)5r&QVH^;wY*t0*6#o;bapgwX&|87m4e1-2`bPBJpjG0KYs2AiJxgeG z7NCgFL!)^jL$r{aHEbN=PEO*`>>x!Fewi7|ZW&6lOGEFg5sC{VKyeVF5!0-o>&CLk z1XG!Lo3AV7B}QRKU}|3p1Mck|C}A1HAag0x$(=mwAz&>KD@bw<>@4Hq1FkU*x~8Z* zX2T$D?>7%G>tdgyK+E8k6DSFNwAS=GtPEH#ot)x<(@SHTx%s>vw2o3&untPlX0W;@ zHAB3gKT0fPRAqnxB4|sC`Jftt{6pZvYX@jW3`kt$PjiZ!QAVa*%`Do8zI2+V)@JtnDy#y-b|akcYEJ$oTkse%1Q4nbj#h#)jX9Dt@Mi?2bIn|>CkI*((*A02OsPR_fY?kzS_sLF+HT;pIes5(xjnO_- zuG;Y6Gf3|VW@|1)b;h(8)6jrgo-Ma5(MVx8t6fAuuVhuY{f#$WQ=&ehxZ8)Z`!uSLb=7hkDo;q@4o*Y-hAsV?AJZ^2G{zGp>C9{$Pv14 zC|48EqKKN)zQ^wz?vsEVyQ z`F&nuT8!y-<}Ehl+4^{Uo&5Pv_DIFKgHg}*}UuTvZ_oi|VNU1PjV=4gkk}gE%s_I6Z6?!D4qf0-LbgkMt1=1$SS}OH+{9X{e%5ZSC zqON@ob1_06B^kYHlyYum4r#P3xE!{KZN4IEnF23*kXyRv>qy24nX;1>uS>Pzp)l-y z<4+(;HFTO11ffG>7r*vBq^}2cz9lgG;zfwC83j4#a5XvA$`Ju=tbi6Cn?C@Z7`vrh zd9ZLeD$qFSw5*Fi(Ur_H)f{FKyBbE)9GaF^Wp=r$x>;!+ zm{kFRBz0~!6m5EaoZ?b?f>cq2=F7R48b*N6iU{XXdZQGhb%3UZ!XhXG&7$WdMJ8EH z_kN{N36eMmsXa#lC6CRsYELDh3rNvn_)ykQnK4{#mP|=^s_53?(G{kz&9q~q_S9c5 zgoLU7SvOK>pKT@w5@lQP>@M@+I%E972VeFxLvXE1FU)Xe~v&I$18+^n3xqOD(VBU=Ifrp&X-M!I_Ut z2c23#Xwr5=X*H2@U&hdATUcGJO`l>>W78h4-a}BB^YL80b~7;_Qi4m%7?g=|T^Qjp zip7;nOACJmD-`pwl0u~g6snD%8_n?&A^0q#%8m!kG&B$==ulWCj52UcfM0m>F|2*X zwb$N4Uwa_g2Y^|;@x449!YROM+gX3>H&v5kjlb462cGgp*-Mm28Mf66zF@2-n;~OKD&< zM#GAcfJ>;9jvC|8FGL(S39phwEvrYydwn#E#J`trqxtp>EJbOGy%|@yNp4_1J*Yd&0V| zsM$wY;$03N=G0J*sGwiQ|GH>yPYezQS^I$tfuQ6^u5~75#*g>@P{6dA(`W%WFf$5J zW{8w3c+DwOnz%sj2OXt|#YRIt&x21A=iFp(=uv^|NpqnFP{@g{1+peq7+F*ILEqSbPS zI7bPchQe*u%c4zO4;uk}0Y~GVVlx3S!y4L@0aWy&?u~=~DXmEoZJ5(ra#SeE&UL7{ zW7V`3QQ&?qLy!x}*xPEtXwXI=28RGu3XoO?$7ao_VqN!m_St6-I>v9k^f&0*TmwAr z#PaH^ujr$XeuR#XkJ6%Vmes!Z1KN796?6_QrUEIYs9;Q$m;@|(Sd@c0P$k8d_&Y&K z7gMTg4_jvPhEA_K_qK#}71Z)fYG~sC`i7xW6UD~ItT@tBkU;OZ=~5fmG(xk;ZwZrT zgb(@B>3m0Hq=?K~3O$FVO>BY6ciXZEHf<0(y}qSDzGh$Y!SL%7$UuRJHf>W@f3OJg~~#y)p$skf+}v^x_!_w{?}jsIz$Bb?w>_n zDK^r*J9po>b#(J%z!lor^tNsrr-zt*;_72qgJxr2BJf)tJpY6vvz2zjs`O=ao#8(7gTM6fwfG|l;Fnc zmiP&vuRT2bkQ8%bM4>x8u7>CI5tJiEm_Z&D zog4AG)5Uu6O7@I<@l;fWQ3%gI{~i3&lMmyOM;?ie@xT81bGZD-pFQ*Xjo1HeYpq?ray8JJnBA(WX<+iuq08)wl#!@WYA4iDhp<_*gv~T*I-FdP zVHAVO+c?b9ixvoYLpoGzOZLu9le40!xI0gHyJ%c|_P5l5SQ@pdU^JA875+QXf*UHu zwfS5m4?U9LnOY6R%{EhdAJW<4lJgSXYjm-Rfih_u_%n+|Lq+G8T^eh)#-gqnp5sxx z4&?G*F5BB(WTAK*k!{FNVO%Qs6T_D!TlTq>7Mjx?Iq6l7GP>S+jJfns()z?)ohfua zf^{Py(6i;yq0C6CI^t(%XZY^RKS9%b_-}vqRs7`o4LtnOe}en>?&AF3Z2-WvAHRAA z;4XkO0`SOZe)FHd1MKyefBm=U;YS|9(QYZqFqkJ>(*g$U=1{?Ds4BdX8(?ui@IOKLivw zKRc6G|L*_10pL#n{73rquYBgpYuA7LJHPnJPyZQ!6S^pez(MEpPf;_Q(~F<>71ew$ z%~l(c!U75Y44+uSF`~`yON|Kg{+%wUw9n7){xB^!Ewucikh6Wo#%4@;oNTlP0uiEV zK@)Isk-U^D^g~${7tymWXp7k}Pk^nZ!iOM=MHjy2U-Z+S+(q}X5WT2+l|zlZ16Fz& zCSH5w+_T2_Ex=S*F;kC{iMY)7RdIg*OkVlhmv0034uJmz;7GknyC6HoE@^*It(m?Rjd3XIp zLy3C!5>H=O_9xsdqUz$mJea*17#KY~T^vIyZoR#;(f4qGNZZBxebUKp4o#XdsjJUS zJorIG;bJ>e#)f3BDV%?FZl1E_{X5z2ZnbXfs@&bllZq$V9SCxs6nu5t-Pe~td;h;+ zTO}0n;`wiF`S~vxqLj}yvALkYjdy^@;h12QhhO|_mPz!6G&VrbT zQ%#=|PjcOfP|(?NU;4Cv$H5~_u|7JVxqNgAk{df&Ik?UCaz(Ku_DXw4ZE%X%#bhqV z!TqjO^ozZI5zvY-M**>b0?{qpKvPp#vwd_xCT}|jwk$*R3v&|C5Rg?sV``Jn?u%>a zWZlhi`bd*ep0259Tjrm)uMZzweZc8@rq`r}S-0QKcrs_ncbyG4COrAVp48|5--Aa> P0uaT+^qm#z$3ZS55iEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}?nVv3= zAr*7p-m&$QP84CikZpYSh>l`V#LwvZTs8F0O8U ztZ>db10GMKx09b#*sP1@&AF0Z_D%ab<9}AirlyB~KJUvD-B9Q}Y0qgvountCMjou&oSTOsE-N`L^o*O)iC++5UadrQ{-EXHbFlHD$ MUHx3vIVCg!03#=}f&c&j diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 39d32fcf6cf..767ac830b8c 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -297,6 +297,12 @@ QScrollBar::handle:horizontal:disabled, QScrollBar::handle:vertical:disabled { border: none; } +EffectRackView QScrollBar::handle:vertical:disabled { + background: #3f4750; + border: none; + border-radius: 4px; +} + /* arrow buttons */ QScrollBar::add-line, QScrollBar::sub-line { @@ -349,6 +355,8 @@ QScrollBar::left-arrow:horizontal:disabled { background-image: url(resources:sba QScrollBar::right-arrow:horizontal:disabled { background-image: url(resources:sbarrow_right_d.png);} QScrollBar::up-arrow:vertical:disabled { background-image: url(resources:sbarrow_up_d.png);} QScrollBar::down-arrow:vertical:disabled { background-image: url(resources:sbarrow_down_d.png);} +EffectRackView QScrollBar::up-arrow:vertical:disabled { background-image: url(resources:sbarrow_up.png);} +EffectRackView QScrollBar::down-arrow:vertical:disabled { background-image: url(resources:sbarrow_down.png);} /* background for song editor and bb-editor */ diff --git a/include/EffectRackView.h b/include/EffectRackView.h index 698bad7fb25..d2a9dd52840 100644 --- a/include/EffectRackView.h +++ b/include/EffectRackView.h @@ -46,6 +46,7 @@ class EffectRackView : public QWidget, public ModelView EffectRackView( EffectChain* model, QWidget* parent = NULL ); virtual ~EffectRackView(); + static constexpr int DEFAULT_WIDTH = 245; public slots: void clearViews(); diff --git a/include/EffectView.h b/include/EffectView.h index 6e994dd7e5b..a2509dc5b1f 100644 --- a/include/EffectView.h +++ b/include/EffectView.h @@ -57,6 +57,7 @@ class EffectView : public PluginView return castModel(); } + static constexpr int DEFAULT_WIDTH = 215; public slots: void editControls(); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 257e16c958c..149e132e200 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -319,7 +319,7 @@ FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv, // Create EffectRack for the channel m_rackView = new EffectRackView( &fxChannel->m_fxChain, _mv->m_racksWidget ); - m_rackView->setFixedSize( 245, FxLine::FxLineHeight ); + m_rackView->setFixedSize( EffectRackView::DEFAULT_WIDTH, FxLine::FxLineHeight ); } diff --git a/src/gui/widgets/EffectRackView.cpp b/src/gui/widgets/EffectRackView.cpp index 6af490a42a8..c0761e7f8f0 100644 --- a/src/gui/widgets/EffectRackView.cpp +++ b/src/gui/widgets/EffectRackView.cpp @@ -211,7 +211,7 @@ void EffectRackView::update() } } - w->setFixedSize( 210 + 2*EffectViewMargin, m_lastY ); + w->setFixedSize( EffectView::DEFAULT_WIDTH + 2*EffectViewMargin, m_lastY); QWidget::update(); } diff --git a/src/gui/widgets/EffectView.cpp b/src/gui/widgets/EffectView.cpp index a159eedf956..936a786d0c7 100644 --- a/src/gui/widgets/EffectView.cpp +++ b/src/gui/widgets/EffectView.cpp @@ -49,7 +49,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : m_subWindow( NULL ), m_controlView( NULL ) { - setFixedSize( 210, 60 ); + setFixedSize( EffectView::DEFAULT_WIDTH, 60 ); // Disable effects that are of type "DummyEffect" bool isEnabled = !dynamic_cast( effect() ); @@ -62,21 +62,21 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : m_wetDry = new Knob( knobBright_26, this ); m_wetDry->setLabel( tr( "W/D" ) ); - m_wetDry->move( 27, 5 ); + m_wetDry->move( 40 - m_wetDry->width() / 2, 5 ); m_wetDry->setEnabled( isEnabled ); m_wetDry->setHintText( tr( "Wet Level:" ), "" ); m_autoQuit = new TempoSyncKnob( knobBright_26, this ); m_autoQuit->setLabel( tr( "DECAY" ) ); - m_autoQuit->move( 60, 5 ); + m_autoQuit->move( 78 - m_autoQuit->width() / 2, 5 ); m_autoQuit->setEnabled( isEnabled && !effect()->m_autoQuitDisabled ); m_autoQuit->setHintText( tr( "Time:" ), "ms" ); m_gate = new Knob( knobBright_26, this ); m_gate->setLabel( tr( "GATE" ) ); - m_gate->move( 93, 5 ); + m_gate->move( 116 - m_gate->width() / 2, 5 ); m_gate->setEnabled( isEnabled && !effect()->m_autoQuitDisabled ); m_gate->setHintText( tr( "Gate:" ), "" ); @@ -89,7 +89,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : this ); QFont f = ctls_btn->font(); ctls_btn->setFont( pointSize<8>( f ) ); - ctls_btn->setGeometry( 140, 14, 50, 20 ); + ctls_btn->setGeometry( 150, 14, 50, 20 ); connect( ctls_btn, SIGNAL( clicked() ), this, SLOT( editControls() ) ); @@ -219,10 +219,12 @@ void EffectView::paintEvent( QPaintEvent * ) f.setBold( true ); p.setFont( f ); + QString elidedText = p.fontMetrics().elidedText( model()->displayName(), Qt::ElideRight, width() - 22 ); + p.setPen( palette().shadow().color() ); - p.drawText( 6, 55, model()->displayName() ); + p.drawText( 6, 55, elidedText ); p.setPen( palette().text().color() ); - p.drawText( 5, 54, model()->displayName() ); + p.drawText( 5, 54, elidedText ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 0f3f7b31e96..c61999a0ee7 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1508,9 +1508,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 ); adjustTabSize(m_ssView); adjustTabSize(instrumentFunctions); - adjustTabSize(m_effectView); - // stupid bugfix, no one knows why - m_effectView->resize(INSTRUMENT_WIDTH - 4, INSTRUMENT_HEIGHT - 4 - 1); + m_effectView->resize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1); adjustTabSize(m_midiView); adjustTabSize(m_miscView); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 6515be3e42a..b84d2980311 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -1100,7 +1100,7 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : generalSettingsLayout->addLayout(basicControlsLayout); m_effectRack = new EffectRackView(tv->model()->audioPort()->effects()); - m_effectRack->setFixedSize(240, 242); + m_effectRack->setFixedSize(EffectRackView::DEFAULT_WIDTH, 242); vlayout->addWidget(generalSettingsWidget); vlayout->addWidget(m_effectRack); From 437172a542afea86ea20ef485bb3da3fc4522610 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 8 Nov 2020 14:09:58 +0100 Subject: [PATCH 136/180] Stop sample previews on mouse release or browser focus loss (#5764) Stop previews on mouse up and focus loss. --- include/FileBrowser.h | 1 + src/gui/FileBrowser.cpp | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index a890506bdf3..8cc50f48e79 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -108,6 +108,7 @@ class FileBrowserTreeWidget : public QTreeWidget void keyPressEvent( QKeyEvent * ke ) override; void keyReleaseEvent( QKeyEvent * ke ) override; void hideEvent( QHideEvent * he ) override; + void focusOutEvent( QFocusEvent * fe ) override; private: diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 0524146069e..a7d78bf5463 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -433,6 +433,16 @@ void FileBrowserTreeWidget::hideEvent(QHideEvent* he) +void FileBrowserTreeWidget::focusOutEvent(QFocusEvent* fe) +{ + // Cancel previews when the user clicks outside the browser + stopPreview(); + QTreeWidget::focusOutEvent(fe); +} + + + + void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) { FileItem * file = dynamic_cast( itemAt( e->pos() ) ); @@ -674,22 +684,8 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me ) QMutexLocker previewLocker(&m_pphMutex); - if (m_previewPlayHandle != nullptr) - { - // If less than 3 seconds remain of the sample, we don't - // stop them if the user releases mouse-button... - if (m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle) - { - SamplePlayHandle* s = dynamic_cast(m_previewPlayHandle); - auto second = static_cast(Engine::mixer()->processingSampleRate()); - if (s && s->totalFrames() - s->framesDone() <= second * 3) - { - s->setDoneMayReturnTrue(true); - } - else { stopPreview(); } - } - else { stopPreview(); } - } + //TODO: User setting to allow samples to play until completion instead + if (m_previewPlayHandle != nullptr) { stopPreview(); } } From e1d187810831ce52e900cb17eab37c335844b671 Mon Sep 17 00:00:00 2001 From: Pause for Affliction Date: Tue, 10 Nov 2020 04:40:53 -0700 Subject: [PATCH 137/180] Fix memory leak that the rpmalloc assert warns of (#5776) (Fixes #5733) Hack to take care of the assertion sent by the rpmalloc memory manager. Creates a static "free" function for NotePlayHandleManager and then shoves it right before the program ends. Co-authored-by: Pause for Affliction <47124830+Epsilon-13@users.noreply.github.com> --- include/NotePlayHandle.h | 1 + src/core/NotePlayHandle.cpp | 5 +++++ src/core/main.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index e17a5e3a25e..71866d13b46 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -349,6 +349,7 @@ class NotePlayHandleManager NotePlayHandle::Origin origin = NotePlayHandle::OriginPattern ); static void release( NotePlayHandle * nph ); static void extend( int i ); + static void free(); private: static NotePlayHandle ** s_available; diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index c101e4edf98..d540792c048 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -623,3 +623,8 @@ void NotePlayHandleManager::extend( int c ) ++n; } } + +void NotePlayHandleManager::free() +{ + MM_FREE(s_available); +} diff --git a/src/core/main.cpp b/src/core/main.cpp index 29be5ff6745..36fe587574b 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -1009,5 +1009,8 @@ int main( int argc, char * * argv ) } #endif + + NotePlayHandleManager::free(); + return ret; } From 4fb66542a077f0b8b05dbfb5355ebab0e0a74319 Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Sat, 14 Nov 2020 16:45:49 +0100 Subject: [PATCH 138/180] Fix for the font of truncated sidebar items (#5714). (#5777) * Fix for the font of truncated sidebar items (#5714). For windows platforms, retrieve the system font and set it for the FileBrowserTreeWidget. This makes sure that truncated items will use the font as non-truncated items. * Add TODO to remove the fix when all builds use a recent enough version of qt. * Add check on QT version and conditionally include the fix. --- include/GuiApplication.h | 4 ++++ src/gui/FileBrowser.cpp | 9 +++++++-- src/gui/GuiApplication.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/GuiApplication.h b/include/GuiApplication.h index 8b4284c026b..825c258372e 100644 --- a/include/GuiApplication.h +++ b/include/GuiApplication.h @@ -28,6 +28,7 @@ #include #include "lmms_export.h" +#include "lmmsconfig.h" class QLabel; @@ -48,6 +49,9 @@ class LMMS_EXPORT GuiApplication : public QObject ~GuiApplication(); static GuiApplication* instance(); +#ifdef LMMS_BUILD_WIN32 + static QFont getWin32SystemFont(); +#endif MainWindow* mainWindow() { return m_mainWindow; } FxMixerView* fxMixerView() { return m_fxMixerView; } diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index a7d78bf5463..a6f01c54187 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -55,8 +55,6 @@ #include "StringPairDrag.h" #include "TextFloat.h" - - enum TreeWidgetItemTypes { TypeFileItem = QTreeWidgetItem::UserType, @@ -335,6 +333,13 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) : connect( this, SIGNAL( itemExpanded( QTreeWidgetItem * ) ), SLOT( updateDirectory( QTreeWidgetItem * ) ) ); +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 2) && defined LMMS_BUILD_WIN32 + // Set the font for the QTreeWidget to the Windows System font to make sure that + // truncated (elided) items use the same font as non-truncated items. + // This is a workaround for this qt bug, fixed in 5.12.2: https://bugreports.qt.io/browse/QTBUG-29232 + // TODO: remove this when all builds use a recent enough version of qt. + setFont( GuiApplication::getWin32SystemFont() ); +#endif } diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index fb2e3eae376..dbd95cdfc25 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -45,6 +45,10 @@ #include #include +#ifdef LMMS_BUILD_WIN32 +#include +#endif + GuiApplication* GuiApplication::s_instance = nullptr; GuiApplication* GuiApplication::instance() @@ -211,3 +215,24 @@ void GuiApplication::childDestroyed(QObject *obj) m_controllerRackView = nullptr; } } + +#ifdef LMMS_BUILD_WIN32 +/*! + * @brief Returns the Windows System font. + */ +QFont GuiApplication::getWin32SystemFont() +{ + NONCLIENTMETRICS metrics = { sizeof( NONCLIENTMETRICS ) }; + SystemParametersInfo( SPI_GETNONCLIENTMETRICS, sizeof( NONCLIENTMETRICS ), &metrics, 0 ); + int pointSize = metrics.lfMessageFont.lfHeight; + if ( pointSize < 0 ) + { + // height is in pixels, convert to points + HDC hDC = GetDC( NULL ); + pointSize = MulDiv( abs( pointSize ), 72, GetDeviceCaps( hDC, LOGPIXELSY ) ); + ReleaseDC( NULL, hDC ); + } + + return QFont( QString::fromUtf8( metrics.lfMessageFont.lfFaceName ), pointSize ); +} +#endif From 7dd6a3936610390d9e5034c9a76338b1226af366 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Nov 2020 01:45:27 +0100 Subject: [PATCH 139/180] Introduce blacklisted plugins to core --- include/Engine.h | 2 ++ include/PluginIssue.h | 2 ++ src/core/Engine.cpp | 12 ++++++++++++ src/core/PluginIssue.cpp | 2 ++ 4 files changed, 18 insertions(+) diff --git a/include/Engine.h b/include/Engine.h index 8f988a1250e..a8a15e8a45e 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -88,6 +88,8 @@ class LMMS_EXPORT LmmsCore : public QObject return s_projectJournal; } + static bool ignorePluginBlacklist(); + #ifdef LMMS_HAVE_LV2 static class Lv2Manager * getLv2Manager() { diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 00c90b756f1..30affde834f 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -44,6 +44,7 @@ enum PluginIssueType portHasNoMax, featureNotSupported, //!< plugin requires functionality LMMS can't offer badPortType, //!< port type not supported + blacklisted, noIssue }; @@ -60,6 +61,7 @@ class PluginIssue : m_issueType(it), m_info(msg) { } + PluginIssueType type() const { return m_issueType; } friend QDebug operator<<(QDebug stream, const PluginIssue& iss); }; diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index 81cd0acdb17..06ccfe37a74 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -116,6 +116,18 @@ void LmmsCore::destroy() delete ConfigManager::inst(); } + + + +bool LmmsCore::ignorePluginBlacklist() +{ + const char* envVar = getenv("LMMS_IGNORE_BLACKLIST"); + return (envVar && *envVar); +} + + + + float LmmsCore::framesPerTick(sample_rate_t sampleRate) { return sampleRate * 60.0f * 4 / diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 8e1938e5685..9e7492dcae1 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -54,6 +54,8 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "required feature not supported"; case badPortType: return "unsupported port type"; + case blacklisted: + return "blacklisted plugin"; case noIssue: return nullptr; } From 01f2fa5c29eae31e1959e04b55d6371708e8976f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Nov 2020 01:47:17 +0100 Subject: [PATCH 140/180] Introduce blacklisted plugins to Lv2 interface --- include/Lv2Manager.h | 8 ++++++++ src/core/lv2/Lv2Manager.cpp | 35 +++++++++++++++++++++++++++++++++++ src/core/lv2/Lv2Proc.cpp | 13 +++++++++++++ 3 files changed, 56 insertions(+) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 6261d70f6d7..585fdafc57d 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -131,6 +131,11 @@ class Lv2Manager } bool isFeatureSupported(const char* featName) const; + static const std::set& getPluginBlacklist() + { + return pluginBlacklist; + } + private: // general data bool m_debug; //!< if set, debug output will be printed @@ -144,6 +149,9 @@ class Lv2Manager // URID cache for fast URID access Lv2UridCache m_uridCache; + // static + static const std::set pluginBlacklist; + // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index b1f03079bb6..4dbb30e1854 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -26,6 +26,7 @@ #ifdef LMMS_HAVE_LV2 +#include #include #include #include @@ -36,6 +37,7 @@ #include #include "ConfigManager.h" +#include "Engine.h" #include "Plugin.h" #include "PluginFactory.h" #include "Lv2ControlBase.h" @@ -44,6 +46,13 @@ +const std::set Lv2Manager::pluginBlacklist = +{ +}; + + + + Lv2Manager::Lv2Manager() : m_uridCache(m_uridMap) { @@ -100,6 +109,7 @@ void Lv2Manager::initPlugins() QElapsedTimer timer; timer.start(); + unsigned blacklisted = 0; LILV_FOREACH(plugins, itr, plugins) { const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); @@ -111,6 +121,15 @@ void Lv2Manager::initPlugins() m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] = std::move(info); if(issues.empty()) { ++pluginsLoaded; } + else + { + if(std::any_of(issues.begin(), issues.end(), + [](const PluginIssue& iss) { + return iss.type() == PluginIssueType::blacklisted; })) + { + ++blacklisted; + } + } ++pluginCount; } @@ -133,6 +152,22 @@ void Lv2Manager::initPlugins() " environment variable \"LMMS_LV2_DEBUG\" to nonempty."; } } + + // TODO: might be better in the LMMS core + if(Engine::ignorePluginBlacklist()) + { + qWarning() << + "WARNING! Plugin blacklist disabled! If you want to use the blacklist,\n" + " please set environment variable \"LMMS_IGNORE_BLACKLIST\" to empty or\n" + " do not set it."; + } + else if(blacklisted > 0) + { + qDebug() << + "Lv2 Plugins blacklisted:" << blacklisted << "of" << pluginCount << "\n" + " If you want to ignore the blacklist (dangerous!), please set\n" + " environment variable \"LMMS_IGNORE_BLACKLIST\" to nonempty."; + } } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 925dca5bd0c..ee310a504cb 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -65,6 +65,19 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, unsigned audioChannels[maxCount] = { 0, 0 }; // audio input and output count unsigned midiChannels[maxCount] = { 0, 0 }; // MIDI input and output count + const char* pluginUri = lilv_node_as_uri(lilv_plugin_get_uri(plugin)); + //qDebug() << "Checking plugin" << pluginUri << "..."; + + // TODO: manage a global blacklist outside of the code + // for now, this will help + // this is only a fix for the meantime + const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist(); + if (!Engine::ignorePluginBlacklist() && + pluginBlacklist.find(pluginUri) != pluginBlacklist.end()) + { + issues.emplace_back(blacklisted); + } + for (unsigned portNum = 0; portNum < maxPorts; ++portNum) { Lv2Ports::Meta meta; From 48bc9db71dc53396a04ed348ff98c5af975b2641 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Nov 2020 01:48:05 +0100 Subject: [PATCH 141/180] Forbid crashing Calf Analyzer/BassEnhancer --- src/core/lv2/Lv2Manager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 4dbb30e1854..93946d4262b 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -48,6 +48,9 @@ const std::set Lv2Manager::pluginBlacklist = { + // github.com/calf-studio-gear/calf, #278 + "http://calf.sourceforge.net/plugins/Analyzer", + "http://calf.sourceforge.net/plugins/BassEnhancer" }; From 1c2107f4c6fb017b6d5e62004544760fc42f32cc Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 12 Nov 2020 21:31:40 +0100 Subject: [PATCH 142/180] Fix missing support for lv2core#sampleRate (Fixes #5767) This multiplies port's min/max value with the processing sample rate that is used for the plugin. This fixes damaged audio in GLAME Butterworth High-/Lowpass from #5767. --- include/Lv2Ports.h | 8 +++++++- src/core/lv2/Lv2Ports.cpp | 1 + src/core/lv2/Lv2Proc.cpp | 20 +++++++++++--------- src/gui/Lv2ViewBase.cpp | 6 +++++- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index e61f9bbd1cf..a0d68f24fd8 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -108,11 +108,17 @@ struct Meta Flow m_flow = Flow::Unknown; Vis m_vis = Vis::None; - float m_def = .0f, m_min = .0f, m_max = .0f; bool m_optional = false; bool m_used = true; std::vector get(const LilvPlugin* plugin, std::size_t portNum); + + float def() const { return m_def; } + float min(sample_rate_t sr) const { return m_sampleRate ? sr * m_min : m_min; } + float max(sample_rate_t sr) const { return m_sampleRate ? sr * m_max : m_max; } +private: + float m_def = .0f, m_min = .0f, m_max = .0f; + bool m_sampleRate = false; }; struct PortBase : public Meta diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index cc8ecf6ca43..7415fa9117a 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -155,6 +155,7 @@ std::vector Meta::get(const LilvPlugin *plugin, { takeRangeValue(min.get(), m_min, portHasNoMin); takeRangeValue(max.get(), m_max, portHasNoMax); + if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } if (m_max - m_min > 15.0f) { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index ee310a504cb..fdc316606a7 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -468,12 +468,13 @@ void Lv2Proc::createPort(std::size_t portNum) { AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); QString dispName = lilv_node_as_string(node.get()); + sample_rate_t sr = Engine::mixer()->processingSampleRate(); switch (meta.m_vis) { case Lv2Ports::Vis::None: { // allow ~1000 steps - float stepSize = (meta.m_max - meta.m_min) / 1000.0f; + float stepSize = (meta.max(sr) - meta.min(sr)) / 1000.0f; // make multiples of 0.01 (or 0.1 for larger values) float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; @@ -481,15 +482,15 @@ void Lv2Proc::createPort(std::size_t portNum) stepSize = std::max(stepSize, minStep); ctrl->m_connectedModel.reset( - new FloatModel(meta.m_def, meta.m_min, meta.m_max, + new FloatModel(meta.def(), meta.min(sr), meta.max(sr), stepSize, nullptr, dispName)); break; } case Lv2Ports::Vis::Integer: ctrl->m_connectedModel.reset( - new IntModel(static_cast(meta.m_def), - static_cast(meta.m_min), - static_cast(meta.m_max), + new IntModel(static_cast(meta.def()), + static_cast(meta.min(sr)), + static_cast(meta.max(sr)), nullptr, dispName)); break; case Lv2Ports::Vis::Enumeration: @@ -514,7 +515,7 @@ void Lv2Proc::createPort(std::size_t portNum) } case Lv2Ports::Vis::Toggled: ctrl->m_connectedModel.reset( - new BoolModel(static_cast(meta.m_def), + new BoolModel(static_cast(meta.def()), nullptr, dispName)); break; } @@ -748,9 +749,10 @@ void Lv2Proc::dumpPort(std::size_t num) qDebug() << " visualization: " << Lv2Ports::toStr(port.m_vis); if (port.m_type == Lv2Ports::Type::Control || port.m_type == Lv2Ports::Type::Cv) { - qDebug() << " default:" << port.m_def; - qDebug() << " min:" << port.m_min; - qDebug() << " max:" << port.m_max; + sample_rate_t sr = Engine::mixer()->processingSampleRate(); + qDebug() << " default:" << port.def(); + qDebug() << " min:" << port.min(sr); + qDebug() << " max:" << port.max(sr); } qDebug() << " optional: " << port.m_optional; qDebug() << " => USED: " << port.m_used; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 488705bcb66..a3ef260581d 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -44,6 +44,7 @@ #include "Lv2Proc.h" #include "Lv2Ports.h" #include "MainWindow.h" +#include "Mixer.h" #include "SubWindow.h" @@ -70,9 +71,12 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : m_control = new KnobControl(m_par); break; case PortVis::Integer: - m_control = new LcdControl((port.m_max <= 9.0f) ? 1 : 2, + { + sample_rate_t sr = Engine::mixer()->processingSampleRate(); + m_control = new LcdControl((port.max(sr) <= 9.0f) ? 1 : 2, m_par); break; + } case PortVis::Enumeration: m_control = new ComboControl(m_par); break; From 3a74bad0c9ca068ab5060853582e5182648209c4 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 13 Nov 2020 20:58:41 +0100 Subject: [PATCH 143/180] Lv2Ports: Smash plugins with out-of-bounds defaults --- include/PluginIssue.h | 1 + src/core/PluginIssue.cpp | 2 ++ src/core/lv2/Lv2Ports.cpp | 25 ++++++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 30affde834f..7f42a9409d9 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -42,6 +42,7 @@ enum PluginIssueType portHasNoDef, portHasNoMin, portHasNoMax, + defaultValueNotInRange, featureNotSupported, //!< plugin requires functionality LMMS can't offer badPortType, //!< port type not supported blacklisted, diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 9e7492dcae1..6214eba0281 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -50,6 +50,8 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "port is missing min value"; case portHasNoMax: return "port is missing max value"; + case defaultValueNotInRange: + return "default value is not in range [min, max]"; case featureNotSupported: return "required feature not supported"; case badPortType: diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 7415fa9117a..3bca0c89f1e 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -151,12 +151,35 @@ std::vector Meta::get(const LilvPlugin *plugin, }; takeRangeValue(def.get(), m_def, portHasNoDef); - if (!isToggle) + if (isToggle) + { + m_min = .0f; + m_max = 1.f; + if(def.get() && m_def != m_min && m_def != m_max) + { + issue(defaultValueNotInRange, portName); + } + } + else { takeRangeValue(min.get(), m_min, portHasNoMin); takeRangeValue(max.get(), m_max, portHasNoMax); if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } + if (def.get()) + { + if (m_def < m_min) { issue(defaultValueNotInRange, portName); } + else if (m_def > m_max) + { + if(m_sampleRate) + { + // multiplying with sample rate will hopefully lead us + // to a good default value + } + else { issue(defaultValueNotInRange, portName); } + } + } + if (m_max - m_min > 15.0f) { // range too large for spinbox visualisation, use knobs From 060d0dc5dc5da6f5c200ff2c1ed65a34e8226ae4 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 15 Nov 2020 17:27:06 +0100 Subject: [PATCH 144/180] Lv2Proc: Check def in [min,max] when creating port --- src/core/lv2/Lv2Proc.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index fdc316606a7..e27ffaa9de5 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -469,6 +469,16 @@ void Lv2Proc::createPort(std::size_t portNum) AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); QString dispName = lilv_node_as_string(node.get()); sample_rate_t sr = Engine::mixer()->processingSampleRate(); + if(meta.def() < meta.min(sr) || meta.def() > meta.max(sr)) + { + qWarning() << "Warning: Plugin" + << qStringFromPluginNode(m_plugin, lilv_plugin_get_name) + << "(URI:" + << lilv_node_as_uri(lilv_plugin_get_uri(m_plugin)) + << ") has a default value for port" + << dispName + << "which is not in range [min, max]."; + } switch (meta.m_vis) { case Lv2Ports::Vis::None: @@ -511,6 +521,7 @@ void Lv2Proc::createPort(std::size_t portNum) } lilv_scale_points_free(sps); ctrl->m_connectedModel.reset(comboModel); + // TODO: use default value on comboModel, too? break; } case Lv2Ports::Vis::Toggled: From a42d2d2d70b5de96d1e7c2fbef603a9873b6a456 Mon Sep 17 00:00:00 2001 From: Kumar Date: Mon, 16 Nov 2020 23:02:57 +0530 Subject: [PATCH 145/180] Color mixer channels if they are made by a colored track (#5780) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Color mixer channels if they are made by a coloured track using the “Assign to new FX channel option.” --- include/FxMixer.h | 8 +++++++- src/gui/widgets/FxLine.cpp | 11 ++++------- src/tracks/InstrumentTrack.cpp | 4 +++- src/tracks/SampleTrack.cpp | 4 +++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/FxMixer.h b/include/FxMixer.h index 920d4e27bc3..e7af1d50e1c 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -73,7 +73,13 @@ class FxChannel : public ThreadableJob void unmuteForSolo(); - // TODO C++17 and above: use std::optional insteads + void setColor (QColor newColor) + { + m_color = newColor; + m_hasColor = true; + } + + // TODO C++17 and above: use std::optional instead QColor m_color; bool m_hasColor; diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index 476d8773b2b..254e4068d94 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -418,11 +418,9 @@ void FxLine::setStrokeInnerInactive( const QColor & c ) void FxLine::changeColor() { auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); - auto new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Mixer )->getColor( channel->m_color ); - if( ! new_color.isValid() ) - { return; } - channel->m_color = new_color; - channel->m_hasColor = true; + auto new_color = ColorChooser(this).withPalette(ColorChooser::Palette::Mixer)->getColor(channel->m_color); + if(!new_color.isValid()) { return; } + channel->setColor (new_color); update(); } @@ -439,7 +437,6 @@ void FxLine::resetColor() void FxLine::randomColor() { auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); - channel->m_color = ColorChooser::getPalette( ColorChooser::Palette::Mixer )[ rand() % 48 ]; - channel->m_hasColor = true; + channel->setColor (ColorChooser::getPalette(ColorChooser::Palette::Mixer)[rand() % 48]); update(); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index c61999a0ee7..5c75245f39e 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1098,8 +1098,10 @@ InstrumentTrackWindow * InstrumentTrackView::topLevelInstrumentTrackWindow() void InstrumentTrackView::createFxLine() { int channelIndex = gui->fxMixerView()->addNewChannel(); + auto channel = Engine::fxMixer()->effectChannel(channelIndex); - Engine::fxMixer()->effectChannel( channelIndex )->m_name = getTrack()->name(); + channel->m_name = getTrack()->name(); + if (getTrack()->useColor()) { channel->setColor (getTrack()->color()); } assignFxLine(channelIndex); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index b84d2980311..398fbd28a2e 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -1169,8 +1169,10 @@ void SampleTrackWindow::modelChanged() void SampleTrackView::createFxLine() { int channelIndex = gui->fxMixerView()->addNewChannel(); + auto channel = Engine::fxMixer()->effectChannel(channelIndex); - Engine::fxMixer()->effectChannel(channelIndex)->m_name = getTrack()->name(); + channel->m_name = getTrack()->name(); + if (getTrack()->useColor()) { channel->setColor (getTrack()->color()); } assignFxLine(channelIndex); } From f26296037ad1899b5007882b7c3269d125a33a58 Mon Sep 17 00:00:00 2001 From: dj-pixus Date: Wed, 18 Nov 2020 21:28:13 +0100 Subject: [PATCH 146/180] Fixed Stereo Matrix icon (#5792) --- plugins/stereo_matrix/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stereo_matrix/CMakeLists.txt b/plugins/stereo_matrix/CMakeLists.txt index 088107f5c9c..edc8475f6c4 100644 --- a/plugins/stereo_matrix/CMakeLists.txt +++ b/plugins/stereo_matrix/CMakeLists.txt @@ -1,4 +1,4 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(stereomatrix stereo_matrix.cpp stereomatrix_controls.cpp stereomatrix_control_dialog.cpp stereo_matrix.h stereomatrix_controls.h stereomatrix_control_dialog.h MOCFILES stereomatrix_controls.h stereomatrix_control_dialog.h EMBEDDED_RESOURCES artwork.png) +BUILD_PLUGIN(stereomatrix stereo_matrix.cpp stereomatrix_controls.cpp stereomatrix_control_dialog.cpp stereo_matrix.h stereomatrix_controls.h stereomatrix_control_dialog.h MOCFILES stereomatrix_controls.h stereomatrix_control_dialog.h EMBEDDED_RESOURCES artwork.png logo.png) From 87875a18e321ffe4278fd693d3eadcd5802b86ec Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Thu, 19 Nov 2020 21:34:08 +0100 Subject: [PATCH 147/180] Fix glitch in undo/redo of note edits via the menu (#5789) --- src/gui/editors/PianoRoll.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 27198ce8b19..194867ce463 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3563,13 +3563,14 @@ void PianoRoll::focusOutEvent( QFocusEvent * ) update(); } -void PianoRoll::focusInEvent( QFocusEvent * ) +void PianoRoll::focusInEvent( QFocusEvent * ev ) { if ( hasValidPattern() ) { // Assign midi device m_pattern->instrumentTrack()->autoAssignMidiDevice(true); } + QWidget::focusInEvent(ev); } From 83e51ffc450dbc19b3f3a8aebad48601f02bddb4 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Fri, 20 Nov 2020 12:49:15 -0300 Subject: [PATCH 148/180] Remove unused stuff (#5685) --- include/Engine.h | 7 ------- include/Mixer.h | 2 -- include/TrackContainer.h | 26 -------------------------- src/core/Engine.cpp | 3 --- src/core/Mixer.cpp | 15 --------------- src/core/TrackContainer.cpp | 13 ------------- src/tracks/InstrumentTrack.cpp | 3 --- 7 files changed, 69 deletions(-) diff --git a/include/Engine.h b/include/Engine.h index a8a15e8a45e..4ec146b2bbd 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -35,7 +35,6 @@ #include "lmms_basics.h" class BBTrackContainer; -class DummyTrackContainer; class FxMixer; class ProjectJournal; class Mixer; @@ -102,11 +101,6 @@ class LMMS_EXPORT LmmsCore : public QObject return s_ladspaManager; } - static DummyTrackContainer * dummyTrackContainer() - { - return s_dummyTC; - } - static float framesPerTick() { return s_framesPerTick; @@ -151,7 +145,6 @@ class LMMS_EXPORT LmmsCore : public QObject static Song * s_song; static BBTrackContainer * s_bbTrackContainer; static ProjectJournal * s_projectJournal; - static DummyTrackContainer * s_dummyTC; #ifdef LMMS_HAVE_LV2 static class Lv2Manager* s_lv2Manager; diff --git a/include/Mixer.h b/include/Mixer.h index 32eeb8977bf..d9b60d188f5 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -173,8 +173,6 @@ class LMMS_EXPORT Mixer : public QObject //! Set new audio device. Old device will be deleted, //! unless it's stored using storeAudioDevice - void setAudioDevice( AudioDevice * _dev , bool startNow ); - //! See overloaded function void setAudioDevice( AudioDevice * _dev, const struct qualitySettings & _qs, bool _needs_fifo, diff --git a/include/TrackContainer.h b/include/TrackContainer.h index fd853a73c1e..af1e8187d49 100644 --- a/include/TrackContainer.h +++ b/include/TrackContainer.h @@ -115,30 +115,4 @@ class LMMS_EXPORT TrackContainer : public Model, public JournallingObject } ; -class DummyTrackContainer : public TrackContainer -{ -public: - DummyTrackContainer(); - - virtual ~DummyTrackContainer() - { - } - - QString nodeName() const override - { - return "DummyTrackContainer"; - } - - InstrumentTrack * dummyInstrumentTrack() - { - return m_dummyInstrumentTrack; - } - - -private: - InstrumentTrack * m_dummyInstrumentTrack; - -} ; - - #endif diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index 06ccfe37a74..214e7b59e2e 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -47,7 +47,6 @@ Lv2Manager * LmmsCore::s_lv2Manager = nullptr; #endif Ladspa2LMMS * LmmsCore::s_ladspaManager = NULL; void* LmmsCore::s_dndPluginKey = nullptr; -DummyTrackContainer * LmmsCore::s_dummyTC = NULL; @@ -79,7 +78,6 @@ void LmmsCore::init( bool renderOnly ) s_mixer->initDevices(); PresetPreviewPlayHandle::init(); - s_dummyTC = new DummyTrackContainer; emit engine->initProgress(tr("Launching mixer threads")); s_mixer->startProcessing(); @@ -98,7 +96,6 @@ void LmmsCore::destroy() s_song->clearProject(); deleteHelper( &s_bbTrackContainer ); - deleteHelper( &s_dummyTC ); deleteHelper( &s_fxMixer ); deleteHelper( &s_mixer ); diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 29bc725b6b6..1a317e21f08 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -600,21 +600,6 @@ void Mixer::doSetAudioDevice( AudioDevice * _dev ) -void Mixer::setAudioDevice( AudioDevice * _dev, - bool startNow ) -{ - stopProcessing(); - - doSetAudioDevice( _dev ); - - emit sampleRateChanged(); - - if (startNow) {startProcessing();} -} - - - - void Mixer::setAudioDevice(AudioDevice * _dev, const struct qualitySettings & _qs, bool _needs_fifo, diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index 95dd46f298c..eac40ea6f84 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -331,16 +331,3 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra return valueMap; }; - - -DummyTrackContainer::DummyTrackContainer() : - TrackContainer(), - m_dummyInstrumentTrack( NULL ) -{ - setJournalling( false ); - m_dummyInstrumentTrack = dynamic_cast( - Track::create( Track::InstrumentTrack, - this ) ); - m_dummyInstrumentTrack->setJournalling( false ); -} - diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 5c75245f39e..a4b4d0231d1 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1135,9 +1135,6 @@ void InstrumentTrackView::freeInstrumentTrackWindow() model()->setHook( NULL ); m_window->setInstrumentTrackView( NULL ); m_window->parentWidget()->hide(); - //m_window->setModel( - // engine::dummyTrackContainer()-> - // dummyInstrumentTrack() ); m_window->updateInstrumentView(); s_windowCache << m_window; } From 53b003bc8ff561bdc66f356c927603822b30ef52 Mon Sep 17 00:00:00 2001 From: Kumar Date: Sat, 21 Nov 2020 09:56:06 +0530 Subject: [PATCH 149/180] Allow SampleTCOs/Sample Clips to be reversed (#5765) Enable the reverse option from `SampleBuffer.cpp`, and partially change the style and make more readable `SampleBuffer.cpp`. --- include/SampleBuffer.h | 10 +-- include/SampleTrack.h | 2 + src/core/SampleBuffer.cpp | 134 ++++++++++++++++--------------------- src/tracks/SampleTrack.cpp | 32 ++++++++- 4 files changed, 94 insertions(+), 84 deletions(-) diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 89a3add3b2a..680ec60c488 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -117,10 +117,10 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject const float _freq, const LoopMode _loopmode = LoopOff ); - void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ); - inline void visualize( QPainter & _p, const QRect & _dr, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ) + void visualize(QPainter & p, const QRect & dr, const QRect & clip, f_cnt_t from_frame = 0, f_cnt_t to_frame = 0); + inline void visualize(QPainter & p, const QRect & dr, f_cnt_t from_frame = 0, f_cnt_t to_frame = 0) { - visualize( _p, _dr, _dr, _from_frame, _to_frame ); + visualize(p, dr, dr, from_frame, to_frame); } inline const QString & audioFile() const @@ -266,8 +266,8 @@ public slots: void update( bool _keep_settings = false ); - void convertIntToFloat ( int_sample_t * & _ibuf, f_cnt_t _frames, int _channels); - void directFloatWrite ( sample_t * & _fbuf, f_cnt_t _frames, int _channels); + void convertIntToFloat(int_sample_t * & ibuf, f_cnt_t frames, int channels); + void directFloatWrite(sample_t * & fbuf, f_cnt_t frames, int channels); f_cnt_t decodeSampleSF( QString _f, sample_t * & _buf, ch_cnt_t & _channels, diff --git a/include/SampleTrack.h b/include/SampleTrack.h index eda1299a538..110b16576ad 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -94,6 +94,7 @@ public slots: signals: void sampleChanged(); + void wasReversed(); } ; @@ -109,6 +110,7 @@ class SampleTCOView : public TrackContentObjectView public slots: void updateSample(); + void reverseSample(); diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index ffe631ca44b..6494945682f 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -296,75 +296,49 @@ void SampleBuffer::update( bool _keep_settings ) } -void SampleBuffer::convertIntToFloat ( int_sample_t * & _ibuf, f_cnt_t _frames, int _channels) +void SampleBuffer::convertIntToFloat( + int_sample_t * & ibuf, + f_cnt_t frames, + int channels) { - // following code transforms int-samples into - // float-samples and does amplifying & reversing + // following code transforms int-samples into float-samples and does amplifying & reversing const float fac = 1 / OUTPUT_SAMPLE_MULTIPLIER; - m_data = MM_ALLOC( sampleFrame, _frames ); - const int ch = ( _channels > 1 ) ? 1 : 0; + m_data = MM_ALLOC(sampleFrame, frames); + const int ch = (channels > 1) ? 1 : 0; - // if reversing is on, we also reverse when - // scaling - if( m_reversed ) + // if reversing is on, we also reverse when scaling + bool isReversed = m_reversed; + int idx = isReversed ? (frames - 1) * channels : 0; + for (f_cnt_t frame = 0; frame < frames; ++frame) { - int idx = ( _frames - 1 ) * _channels; - for( f_cnt_t frame = 0; frame < _frames; - ++frame ) - { - m_data[frame][0] = _ibuf[idx+0] * fac; - m_data[frame][1] = _ibuf[idx+ch] * fac; - idx -= _channels; - } - } - else - { - int idx = 0; - for( f_cnt_t frame = 0; frame < _frames; - ++frame ) - { - m_data[frame][0] = _ibuf[idx+0] * fac; - m_data[frame][1] = _ibuf[idx+ch] * fac; - idx += _channels; - } + m_data[frame][0] = ibuf[idx+0] * fac; + m_data[frame][1] = ibuf[idx+ch] * fac; + idx += isReversed ? -channels : channels; } - delete[] _ibuf; + delete[] ibuf; } -void SampleBuffer::directFloatWrite ( sample_t * & _fbuf, f_cnt_t _frames, int _channels) - +void SampleBuffer::directFloatWrite( + sample_t * & fbuf, + f_cnt_t frames, + int channels) { - m_data = MM_ALLOC( sampleFrame, _frames ); - const int ch = ( _channels > 1 ) ? 1 : 0; + m_data = MM_ALLOC(sampleFrame, frames); + const int ch = (channels > 1) ? 1 : 0; - // if reversing is on, we also reverse when - // scaling - if( m_reversed ) + // if reversing is on, we also reverse when scaling + bool isReversed = m_reversed; + int idx = isReversed ? (frames - 1) * channels : 0; + for (f_cnt_t frame = 0; frame < frames; ++frame) { - int idx = ( _frames - 1 ) * _channels; - for( f_cnt_t frame = 0; frame < _frames; - ++frame ) - { - m_data[frame][0] = _fbuf[idx+0]; - m_data[frame][1] = _fbuf[idx+ch]; - idx -= _channels; - } - } - else - { - int idx = 0; - for( f_cnt_t frame = 0; frame < _frames; - ++frame ) - { - m_data[frame][0] = _fbuf[idx+0]; - m_data[frame][1] = _fbuf[idx+ch]; - idx += _channels; - } + m_data[frame][0] = fbuf[idx+0]; + m_data[frame][1] = fbuf[idx+ch]; + idx += isReversed ? -channels : channels; } - delete[] _fbuf; + delete[] fbuf; } @@ -935,39 +909,45 @@ f_cnt_t SampleBuffer::getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t } -void SampleBuffer::visualize( QPainter & _p, const QRect & _dr, - const QRect & _clip, f_cnt_t _from_frame, f_cnt_t _to_frame ) +void SampleBuffer::visualize( + QPainter & p, + const QRect & dr, + const QRect & clip, + f_cnt_t from_frame, + f_cnt_t to_frame) { - if( m_frames == 0 ) return; + if (m_frames == 0) { return; } - const bool focus_on_range = _to_frame <= m_frames - && 0 <= _from_frame && _from_frame < _to_frame; - //_p.setClipRect( _clip ); - const int w = _dr.width(); - const int h = _dr.height(); + const bool focus_on_range = to_frame <= m_frames && 0 <= from_frame && from_frame < to_frame; + //p.setClipRect( clip ); + const int w = dr.width(); + const int h = dr.height(); - const int yb = h / 2 + _dr.y(); + const int yb = h / 2 + dr.y(); const float y_space = h*0.5f; - const int nb_frames = focus_on_range ? _to_frame - _from_frame : m_frames; + const int nb_frames = focus_on_range ? to_frame - from_frame : m_frames; - const int fpp = qBound( 1, nb_frames / w, 20 ); + const int fpp = qBound(1, nb_frames / w, 20); QPointF * l = new QPointF[nb_frames / fpp + 1]; QPointF * r = new QPointF[nb_frames / fpp + 1]; int n = 0; - const int xb = _dr.x(); - const int first = focus_on_range ? _from_frame : 0; - const int last = focus_on_range ? _to_frame : m_frames; - for( int frame = first; frame < last; frame += fpp ) + const int xb = dr.x(); + const int first = focus_on_range ? from_frame : 0; + const int last = focus_on_range ? to_frame - 1 : m_frames - 1; + + for (int frame = first; frame <= last; frame += fpp) { - l[n] = QPointF( xb + ( (frame - first) * double( w ) / nb_frames ), - ( yb - ( m_data[frame][0] * y_space * m_amplification ) ) ); - r[n] = QPointF( xb + ( (frame - first) * double( w ) / nb_frames ), - ( yb - ( m_data[frame][1] * y_space * m_amplification ) ) ); + auto x = xb + ((frame - first) * double(w) / nb_frames); + // Partial Y calculation + auto py = y_space * m_amplification; + l[n] = QPointF(x, (yb - (m_data[frame][0] * py))); + r[n] = QPointF(x, (yb - (m_data[frame][1] * py))); ++n; } - _p.setRenderHint( QPainter::Antialiasing ); - _p.drawPolyline( l, nb_frames / fpp ); - _p.drawPolyline( r, nb_frames / fpp ); + + p.setRenderHint(QPainter::Antialiasing); + p.drawPolyline(l, nb_frames / fpp); + p.drawPolyline(r, nb_frames / fpp); delete[] l; delete[] r; } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 398fbd28a2e..197e3fcf1a9 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -280,6 +280,10 @@ void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this ) { _this.setAttribute( "color", color().name() ); } + if (m_sampleBuffer->reversed()) + { + _this.setAttribute("reversed", "true"); + } // TODO: start- and end-frame } @@ -310,6 +314,12 @@ void SampleTCO::loadSettings( const QDomElement & _this ) useCustomClipColor( true ); setColor( _this.attribute( "color" ) ); } + + if(_this.hasAttribute("reversed")) + { + m_sampleBuffer->setReversed(true); + emit wasReversed(); // tell SampleTCOView to update the view + } } @@ -332,8 +342,9 @@ SampleTCOView::SampleTCOView( SampleTCO * _tco, TrackView * _tv ) : updateSample(); // track future changes of SampleTCO - connect( m_tco, SIGNAL( sampleChanged() ), - this, SLOT( updateSample() ) ); + connect(m_tco, SIGNAL(sampleChanged()), this, SLOT(updateSample())); + + connect(m_tco, SIGNAL(wasReversed()), this, SLOT(update())); setStyle( QApplication::style() ); } @@ -408,6 +419,13 @@ void SampleTCOView::contextMenuEvent( QContextMenuEvent * _cme ) tr( "Set/clear record" ), m_tco, SLOT( toggleRecord() ) );*/ + contextMenu.addAction( + embed::getIconPixmap("flip_x"), + tr("Reverse sample"), + this, + SLOT(reverseSample()) + ); + contextMenu.addSeparator(); contextMenu.addAction( embed::getIconPixmap( "colorize" ), @@ -625,6 +643,16 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) +void SampleTCOView::reverseSample() +{ + m_tco->sampleBuffer()->setReversed(!m_tco->sampleBuffer()->reversed()); + Engine::getSong()->setModified(); + update(); +} + + + + SampleTrack::SampleTrack(TrackContainer* tc) : From b00adeadc56bfde62dd7a3c6b7dce3c614ed7a62 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 21 Nov 2020 11:04:23 +0000 Subject: [PATCH 150/180] annotate Track::getActivityIndicator implementations as override. (#5798) --- include/InstrumentTrack.h | 2 +- include/SampleTrack.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index fa1d1692e63..944791552dd 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -368,7 +368,7 @@ private slots: QPoint m_lastPos; - FadeButton * getActivityIndicator() + FadeButton * getActivityIndicator() override { return m_activityIndicator; } diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 110b16576ad..e944936b332 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -253,7 +253,7 @@ private slots: TrackLabelButton * m_tlb; - FadeButton * getActivityIndicator() + FadeButton * getActivityIndicator() override { return m_activityIndicator; } From 8d4bcd71054f15addb06fcc8cfea7acc4d48d17f Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 23 Nov 2020 04:18:17 -0300 Subject: [PATCH 151/180] Fixes bug on SampleBuffer::decodeSampleSF (#5796) As requested, this PR extracts the bug fix from #5971. More details about the bug on the mentioned PR page, but basically the variable sf_rr was declared with the wrong type causing the conditional that checks for errors on the loading to return true even when there were no errors (only noticeable with DEBUG builds). This renames the variable and uses the correct type. --- src/core/SampleBuffer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 6494945682f..1872b920f29 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -391,7 +391,7 @@ f_cnt_t SampleBuffer::decodeSampleSF(QString _f, SF_INFO sf_info; sf_info.format = 0; f_cnt_t frames = 0; - bool sf_rr = false; + sf_count_t sfFramesRead; // Use QFile to handle unicode file names on Windows @@ -402,9 +402,9 @@ f_cnt_t SampleBuffer::decodeSampleSF(QString _f, frames = sf_info.frames; _buf = new sample_t[sf_info.channels * frames]; - sf_rr = sf_read_float( snd_file, _buf, sf_info.channels * frames ); + sfFramesRead = sf_read_float(snd_file, _buf, sf_info.channels * frames); - if( sf_rr < sf_info.channels * frames ) + if (sfFramesRead < sf_info.channels * frames) { #ifdef DEBUG_LMMS qDebug( "SampleBuffer::decodeSampleSF(): could not read" From ed9abe58c66f71346d529ed39e634d4a7b7e547f Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 24 Nov 2020 21:49:54 +0100 Subject: [PATCH 152/180] Add option to continue sidebar previews when mouse released (#5787) * Add option to continue sidebar previews when mouse released * Cancel non-sample previews regardless of setting --- include/SetupDialog.h | 2 ++ src/gui/FileBrowser.cpp | 12 +++++++++--- src/gui/SetupDialog.cpp | 14 +++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 7aabde08618..931708b6a0b 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -75,6 +75,7 @@ private slots: void toggleCompactTrackButtons(bool enabled); void toggleOneInstrumentTrackWindow(bool enabled); void toggleSideBarOnRight(bool enabled); + void toggleLetPreviewsFinish(bool enabled); void toggleSoloLegacyBehavior(bool enabled); void toggleMMPZ(bool enabled); void toggleDisableBackup(bool enabled); @@ -133,6 +134,7 @@ private slots: bool m_compactTrackButtons; bool m_oneInstrumentTrackWindow; bool m_sideBarOnRight; + bool m_letPreviewsFinish; bool m_soloLegacyBehavior; bool m_MMPZ; bool m_disableBackup; diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index a6f01c54187..5a2648aa08c 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -687,10 +687,16 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me ) { m_mousePressed = false; + // If a preview is running, we may need to stop it. Otherwise, we're done QMutexLocker previewLocker(&m_pphMutex); - - //TODO: User setting to allow samples to play until completion instead - if (m_previewPlayHandle != nullptr) { stopPreview(); } + if (m_previewPlayHandle == nullptr) { return; } + + // Only sample previews may continue after mouse up. Is this a sample preview? + bool isSample = m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle; + // Even sample previews should only continue if the user wants them to. Do they? + bool shouldContinue = ConfigManager::inst()->value("ui", "letpreviewsfinish").toInt(); + // If both are true the preview may continue, otherwise we stop it + if (!(isSample && shouldContinue)) { stopPreview(); } } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 86ba61eb108..cf6b7b7e06b 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -104,6 +104,8 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : "ui", "oneinstrumenttrackwindow").toInt()), m_sideBarOnRight(ConfigManager::inst()->value( "ui", "sidebaronright").toInt()), + m_letPreviewsFinish(ConfigManager::inst()->value( + "ui", "letpreviewsfinish").toInt()), m_soloLegacyBehavior(ConfigManager::inst()->value( "app", "sololegacybehavior", "0").toInt()), m_MMPZ(!ConfigManager::inst()->value( @@ -237,6 +239,8 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter, m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true); + addLedCheckBox(tr("Let sample previews continue when mouse is released"), gui_tw, counter, + m_letPreviewsFinish, SLOT(toggleLetPreviewsFinish(bool)), false); addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter, m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false); @@ -466,7 +470,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : as_w_layout->setMargin(0); #ifdef LMMS_HAVE_JACK - m_audioIfaceSetupWidgets[AudioJack::name()] = + m_audioIfaceSetupWidgets[AudioJack::name()] = new AudioJack::setupWidget(as_w); #endif @@ -911,6 +915,8 @@ void SetupDialog::accept() QString::number(m_oneInstrumentTrackWindow)); ConfigManager::inst()->setValue("ui", "sidebaronright", QString::number(m_sideBarOnRight)); + ConfigManager::inst()->setValue("ui", "letpreviewsfinish", + QString::number(m_letPreviewsFinish)); ConfigManager::inst()->setValue("app", "sololegacybehavior", QString::number(m_soloLegacyBehavior)); ConfigManager::inst()->setValue("app", "nommpz", @@ -1025,6 +1031,12 @@ void SetupDialog::toggleSideBarOnRight(bool enabled) } +void SetupDialog::toggleLetPreviewsFinish(bool enabled) +{ + m_letPreviewsFinish = enabled; +} + + void SetupDialog::toggleMMPZ(bool enabled) { m_MMPZ = enabled; From 571c425f4acb26141104b58ba00f036f83b46d35 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Tue, 24 Nov 2020 23:55:10 +0000 Subject: [PATCH 153/180] Support multiple instrument subplugin categories (#5801) --- include/PluginBrowser.h | 3 -- src/gui/PluginBrowser.cpp | 90 ++++++++++++++++++++------------------- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/include/PluginBrowser.h b/include/PluginBrowser.h index 3cc54c6e47a..fa13f246916 100644 --- a/include/PluginBrowser.h +++ b/include/PluginBrowser.h @@ -33,7 +33,6 @@ class QLineEdit; class QTreeWidget; -class QTreeWidgetItem; class PluginBrowser : public SideBarWidget @@ -53,8 +52,6 @@ private slots: QWidget * m_view; QTreeWidget * m_descTree; - QTreeWidgetItem * m_lmmsRoot; - QTreeWidgetItem * m_lv2Root; }; diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 7fd1a6301a0..f27ba51c396 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -79,18 +79,7 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) : view_layout->addWidget( searchBar ); view_layout->addWidget( m_descTree ); - // Add LMMS root to the tree - m_lmmsRoot = new QTreeWidgetItem(); - m_lmmsRoot->setText( 0, "LMMS" ); - m_descTree->insertTopLevelItem( 0, m_lmmsRoot ); - m_lmmsRoot->setExpanded( true ); - - // Add LV2 root to the tree - m_lv2Root = new QTreeWidgetItem(); - m_lv2Root->setText( 0, "LV2" ); - m_descTree->insertTopLevelItem( 1, m_lv2Root ); - - // Add plugins to the tree roots + // Add plugins to the tree addPlugins(); // Resize @@ -146,49 +135,62 @@ void PluginBrowser::onFilterChanged( const QString & filter ) void PluginBrowser::addPlugins() { - QList descs = pluginFactory->descriptors(Plugin::Instrument); - std::sort( - descs.begin(), - descs.end(), - []( const Plugin::Descriptor* d1, const Plugin::Descriptor* d2 ) -> bool - { - return qstricmp( d1->displayName, d2->displayName ) < 0 ? true : false; - } - ); - - typedef Plugin::Descriptor::SubPluginFeatures::KeyList PluginKeyList; - typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; - PluginKeyList subPluginKeys, pluginKeys; - - for (const Plugin::Descriptor* desc: descs) + // Add a root node to the plugin tree with the specified `label` and return it + const auto addRoot = [this](auto label) { - if ( desc->subPluginFeatures ) - { - desc->subPluginFeatures->listSubPluginKeys( - desc, - subPluginKeys ); - } - else + const auto root = new QTreeWidgetItem(); + root->setText(0, label); + m_descTree->addTopLevelItem(root); + return root; + }; + + // Add the plugin identified by `key` to the tree under the root node `root` + const auto addPlugin = [this](const auto& key, auto root) + { + const auto item = new QTreeWidgetItem(); + root->addChild(item); + m_descTree->setItemWidget(item, 0, new PluginDescWidget(key, m_descTree)); + }; + + // Remove any existing plugins from the tree + m_descTree->clear(); + + // Fetch and sort all instrument plugin descriptors + auto descs = pluginFactory->descriptors(Plugin::Instrument); + std::sort(descs.begin(), descs.end(), + [](auto d1, auto d2) { - pluginKeys << PluginKey( desc, desc->name ); + return qstricmp(d1->displayName, d2->displayName) < 0; } - } + ); - pluginKeys += subPluginKeys; + // Add a root node to the tree for native LMMS plugins + const auto lmmsRoot = addRoot("LMMS"); + lmmsRoot->setExpanded(true); - for (const PluginKey& key : pluginKeys) + // Add all of the descriptors to the tree + for (const auto desc : descs) { - QTreeWidgetItem * item = new QTreeWidgetItem(); - if ( key.desc->name == QStringLiteral("lv2instrument") ) + if (desc->subPluginFeatures) { - m_lv2Root->addChild( item ); + // Fetch and sort all subplugins for this plugin descriptor + auto subPluginKeys = Plugin::Descriptor::SubPluginFeatures::KeyList{}; + desc->subPluginFeatures->listSubPluginKeys(desc, subPluginKeys); + std::sort(subPluginKeys.begin(), subPluginKeys.end(), + [](const auto& l, const auto& r) + { + return QString::compare(l.displayName(), r.displayName(), Qt::CaseInsensitive) < 0; + } + ); + + // Create a root node for this plugin and add the subplugins under it + const auto root = addRoot(desc->displayName); + for (const auto& key : subPluginKeys) { addPlugin(key, root); } } else { - m_lmmsRoot->addChild( item ); + addPlugin(Plugin::Descriptor::SubPluginFeatures::Key(desc, desc->name), lmmsRoot); } - PluginDescWidget* p = new PluginDescWidget( key, m_descTree ); - m_descTree->setItemWidget( item, 0, p ); } } From 1949f93f107b9305c8ac805307c7ec2b7db7c27c Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Thu, 26 Nov 2020 23:25:32 +0100 Subject: [PATCH 154/180] Add checkboxes for selecting user and factory content (#5786) Co-authored-by: IanCaio Co-authored-by: Dominic Clark Co-authored-by: Kevin Zander --- data/themes/classic/style.css | 6 ++++ data/themes/default/style.css | 6 ++++ include/FileBrowser.h | 11 ++++++- src/gui/FileBrowser.cpp | 57 +++++++++++++++++++++++++++++++---- src/gui/MainWindow.cpp | 12 ++++++-- 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 5935b7749fc..860aa0da1ea 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -129,6 +129,12 @@ QMenu::indicator:selected { background-color: #747474; } +FileBrowser QCheckBox +{ + font-size: 10px; + color: white; +} + PositionLine { qproperty-tailGradient: false; qproperty-lineColor: rgb(255, 255, 255); diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 767ac830b8c..ce476f5a9ce 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -40,6 +40,12 @@ QMdiArea { background-color: #111314; } +FileBrowser QCheckBox +{ + font-size: 10px; + color: white; +} + Knob { qproperty-lineInactiveColor: rgb(120, 120, 120); qproperty-arcInactiveColor: rgba(120, 120, 120, 70); diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 8cc50f48e79..2e26199fcec 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -26,6 +26,7 @@ #ifndef FILE_BROWSER_H #define FILE_BROWSER_H +#include #include #include #include @@ -58,7 +59,10 @@ class FileBrowser : public SideBarWidget */ FileBrowser( const QString & directories, const QString & filter, const QString & title, const QPixmap & pm, - QWidget * parent, bool dirs_as_items = false, bool recurse = false ); + QWidget * parent, bool dirs_as_items = false, bool recurse = false, + const QString& userDir = "", + const QString& factoryDir = ""); + virtual ~FileBrowser() = default; private slots: @@ -83,6 +87,11 @@ private slots: bool m_dirsAsItems; bool m_recurse; + void addContentCheckBox(); + QCheckBox* m_showUserContent = nullptr; + QCheckBox* m_showFactoryContent = nullptr; + QString m_userDir; + QString m_factoryDir; } ; diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 5a2648aa08c..797e19c00e6 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "FileBrowser.h" #include "BBTrackContainer.h" @@ -63,17 +64,50 @@ enum TreeWidgetItemTypes +void FileBrowser::addContentCheckBox() +{ + auto filterWidget = new QWidget(contentParent()); + filterWidget->setFixedHeight(15); + auto filterWidgetLayout = new QHBoxLayout(filterWidget); + filterWidgetLayout->setMargin(0); + filterWidgetLayout->setSpacing(0); + + auto configCheckBox = [this, &filterWidgetLayout](QCheckBox* box) + { + box->setCheckState(Qt::Checked); + connect(box, SIGNAL(stateChanged(int)), this, SLOT(reloadTree())); + filterWidgetLayout->addWidget(box); + }; + + m_showUserContent = new QCheckBox(tr("User content")); + configCheckBox(m_showUserContent); + m_showFactoryContent = new QCheckBox(tr("Factory content")); + configCheckBox(m_showFactoryContent); + + addContentWidget(filterWidget); +}; + + FileBrowser::FileBrowser(const QString & directories, const QString & filter, const QString & title, const QPixmap & pm, - QWidget * parent, bool dirs_as_items, bool recurse ) : + QWidget * parent, bool dirs_as_items, bool recurse, + const QString& userDir, + const QString& factoryDir): SideBarWidget( title, pm, parent ), m_directories( directories ), m_filter( filter ), m_dirsAsItems( dirs_as_items ), - m_recurse( recurse ) + m_recurse( recurse ), + m_userDir(userDir), + m_factoryDir(factoryDir) { setWindowTitle( tr( "Browser" ) ); + if (!userDir.isEmpty() && !factoryDir.isEmpty()) + { + addContentCheckBox(); + } + QWidget * searchWidget = new QWidget( contentParent() ); searchWidget->setFixedHeight( 24 ); @@ -160,17 +194,28 @@ bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item ) } - void FileBrowser::reloadTree( void ) { QList expandedDirs = m_fileBrowserTreeWidget->expandedDirs(); const QString text = m_filterEdit->text(); m_filterEdit->clear(); m_fileBrowserTreeWidget->clear(); - QStringList paths = m_directories.split( '*' ); - for( QStringList::iterator it = paths.begin(); it != paths.end(); ++it ) + QStringList paths = m_directories.split('*'); + if (m_showUserContent && !m_showUserContent->isChecked()) { - addItems( *it ); + paths.removeAll(m_userDir); + } + if (m_showFactoryContent && !m_showFactoryContent->isChecked()) + { + paths.removeAll(m_factoryDir); + } + + if (!paths.isEmpty()) + { + for (QStringList::iterator it = paths.begin(); it != paths.end(); ++it) + { + addItems(*it); + } } expandItems(nullptr, expandedDirs); m_filterEdit->setText( text ); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 94f337576ed..7828dcfbc92 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -128,20 +128,26 @@ MainWindow::MainWindow() : "*.mmp *.mmpz *.xml *.mid", tr( "My Projects" ), embed::getIconPixmap( "project_file" ).transformed( QTransform().rotate( 90 ) ), - splitter, false, true ) ); + splitter, false, true, + confMgr->userProjectsDir(), + confMgr->factoryProjectsDir())); sideBar->appendTab( new FileBrowser( confMgr->userSamplesDir() + "*" + confMgr->factorySamplesDir(), "*", tr( "My Samples" ), embed::getIconPixmap( "sample_file" ).transformed( QTransform().rotate( 90 ) ), - splitter, false, true ) ); + splitter, false, true, + confMgr->userSamplesDir(), + confMgr->factorySamplesDir())); sideBar->appendTab( new FileBrowser( confMgr->userPresetsDir() + "*" + confMgr->factoryPresetsDir(), "*.xpf *.cs.xml *.xiz *.lv2", tr( "My Presets" ), embed::getIconPixmap( "preset_file" ).transformed( QTransform().rotate( 90 ) ), - splitter , false, true ) ); + splitter , false, true, + confMgr->userPresetsDir(), + confMgr->factoryPresetsDir())); sideBar->appendTab( new FileBrowser( QDir::homePath(), "*", tr( "My Home" ), embed::getIconPixmap( "home" ).transformed( QTransform().rotate( 90 ) ), From 9ca5497202220b4010efeba326b61ba11834ee74 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Fri, 27 Nov 2020 11:53:42 +0900 Subject: [PATCH 155/180] Improve STK rawwave path detection (#5804) --- src/core/ConfigManager.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 5336654aef0..a41f8a8c010 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -528,17 +528,16 @@ void ConfigManager::loadConfigFile(const QString & configFile) { #if defined(LMMS_BUILD_WIN32) m_stkDir = m_dataDir + "stk/rawwaves/"; -#elif defined(LMMS_BUILD_APPLE) - m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; #else - if ( qApp->applicationDirPath().startsWith("/tmp/") ) + // Look for bundled raw waves first + m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; + // Try system installations if not exists + if (!QDir(m_stkDir).exists()) { - // Assume AppImage bundle - m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; + m_stkDir = "/usr/local/share/stk/rawwaves/"; } - else + if (!QDir(m_stkDir).exists()) { - // Fallback to system provided location m_stkDir = "/usr/share/stk/rawwaves/"; } #endif From 246b822a6f6e6635337983c2e5259b5e77b3219b Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Fri, 27 Nov 2020 13:46:06 +0000 Subject: [PATCH 156/180] Rework Song::processNextBuffer (#5723) --- include/Song.h | 4 + src/core/Song.cpp | 245 +++++++++++++++++----------------------------- 2 files changed, 94 insertions(+), 155 deletions(-) diff --git a/include/Song.h b/include/Song.h index 47367828194..eeee01274ae 100644 --- a/include/Song.h +++ b/include/Song.h @@ -248,6 +248,10 @@ class LMMS_EXPORT Song : public TrackContainer { return m_playPos[pm]; } + inline PlayPos & getPlayPos() + { + return getPlayPos(m_playMode); + } inline const PlayPos & getPlayPos() const { return getPlayPos(m_playMode); diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 07f28821bfc..bb2e937d086 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -30,6 +30,8 @@ #include #include +#include +#include #include #include "AutomationTrack.h" @@ -191,226 +193,159 @@ void Song::savePos() void Song::processNextBuffer() { - m_vstSyncController.setPlaybackJumped( false ); + m_vstSyncController.setPlaybackJumped(false); - // if not playing, nothing to do - if( m_playing == false ) + // If nothing is playing, there is nothing to do + if (!m_playing) { return; } + + // At the beginning of the song, we have to reset the LFOs + if (m_playMode == Mode_PlaySong && getPlayPos() == 0) { - return; + EnvelopeAndLfoParameters::instances()->reset(); } TrackList trackList; - int tcoNum = -1; // track content object number + int clipNum = -1; // The number of the clip that will be played - // determine the list of tracks to play and the track content object - // (TCO) number - switch( m_playMode ) + // Determine the list of tracks to play and the clip number + switch (m_playMode) { case Mode_PlaySong: trackList = tracks(); - // at song-start we have to reset the LFOs - if( m_playPos[Mode_PlaySong] == 0 ) - { - EnvelopeAndLfoParameters::instances()->reset(); - } break; case Mode_PlayBB: - if( Engine::getBBTrackContainer()->numOfBBs() > 0 ) + if (Engine::getBBTrackContainer()->numOfBBs() > 0) { - tcoNum = Engine::getBBTrackContainer()-> - currentBB(); - trackList.push_back( BBTrack::findBBTrack( - tcoNum ) ); + clipNum = Engine::getBBTrackContainer()->currentBB(); + trackList.push_back(BBTrack::findBBTrack(clipNum)); } break; case Mode_PlayPattern: - if( m_patternToPlay != NULL ) + if (m_patternToPlay) { - tcoNum = m_patternToPlay->getTrack()-> - getTCONum( m_patternToPlay ); - trackList.push_back( - m_patternToPlay->getTrack() ); + clipNum = m_patternToPlay->getTrack()->getTCONum(m_patternToPlay); + trackList.push_back(m_patternToPlay->getTrack()); } break; default: return; - - } - - // if we have no tracks to play, nothing to do - if( trackList.empty() == true ) - { - return; } - // check for looping-mode and act if necessary - TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine; - bool checkLoop = - tl != NULL && m_exporting == false && tl->loopPointsEnabled(); + // If we have no tracks to play, there is nothing to do + if (trackList.empty()) { return; } - if( checkLoop ) + // If the playback position is outside of the range [begin, end), move it to + // begin and inform interested parties. + // Returns true if the playback position was moved, else false. + const auto enforceLoop = [this](const MidiTime& begin, const MidiTime& end) { - // if looping-mode is enabled and we are outside of the looping - // range, go to the beginning of the range - if( m_playPos[m_playMode] < tl->loopBegin() || - m_playPos[m_playMode] >= tl->loopEnd() ) + if (getPlayPos() < begin || getPlayPos() >= end) { - setToTime(tl->loopBegin()); - m_playPos[m_playMode].setTicks( - tl->loopBegin().getTicks() ); - - m_vstSyncController.setPlaybackJumped( true ); - + setToTime(begin); + m_vstSyncController.setPlaybackJumped(true); emit updateSampleTracks(); + return true; } - } + return false; + }; - if( m_playPos[m_playMode].jumped() ) + const auto timeline = getPlayPos().m_timeLine; + const auto loopEnabled = !m_exporting && timeline && timeline->loopPointsEnabled(); + + // Ensure playback begins within the loop if it is enabled + if (loopEnabled) { enforceLoop(timeline->loopBegin(), timeline->loopEnd()); } + + // Inform VST plugins if the user moved the play head + if (getPlayPos().jumped()) { - m_vstSyncController.setPlaybackJumped( true ); - m_playPos[m_playMode].setJumped( false ); + m_vstSyncController.setPlaybackJumped(true); + getPlayPos().setJumped(false); } - f_cnt_t framesPlayed = 0; - const float framesPerTick = Engine::framesPerTick(); + const auto framesPerTick = Engine::framesPerTick(); + const auto framesPerPeriod = Engine::mixer()->framesPerPeriod(); + + f_cnt_t frameOffsetInPeriod = 0; - while( framesPlayed < Engine::mixer()->framesPerPeriod() ) + while (frameOffsetInPeriod < framesPerPeriod) { - m_vstSyncController.update(); + auto frameOffsetInTick = getPlayPos().currentFrame(); - float currentFrame = m_playPos[m_playMode].currentFrame(); - // did we play a tick? - if( currentFrame >= framesPerTick ) + // If a whole tick has elapsed, update the frame and tick count, and check any loops + if (frameOffsetInTick >= framesPerTick) { - int ticks = m_playPos[m_playMode].getTicks() + - ( int )( currentFrame / framesPerTick ); - - // did we play a whole bar? - if( ticks >= MidiTime::ticksPerBar() ) + // Transfer any whole ticks from the frame count to the tick count + const auto elapsedTicks = static_cast(frameOffsetInTick / framesPerTick); + getPlayPos().setTicks(getPlayPos().getTicks() + elapsedTicks); + frameOffsetInTick -= elapsedTicks * framesPerTick; + getPlayPos().setCurrentFrame(frameOffsetInTick); + + // If we are playing a BB track, or a pattern with no loop enabled, + // loop back to the beginning when we reach the end + if (m_playMode == Mode_PlayBB) { - // per default we just continue playing even if - // there's no more stuff to play - // (song-play-mode) - int maxBar = m_playPos[m_playMode].getBar() - + 2; - - // then decide whether to go over to next bar - // or to loop back to first bar - if( m_playMode == Mode_PlayBB ) - { - maxBar = Engine::getBBTrackContainer() - ->lengthOfCurrentBB(); - } - else if( m_playMode == Mode_PlayPattern && - m_loopPattern == true && - tl != NULL && - tl->loopPointsEnabled() == false ) - { - maxBar = m_patternToPlay->length() - .getBar(); - } - - // end of played object reached? - if( m_playPos[m_playMode].getBar() + 1 - >= maxBar ) - { - // then start from beginning and keep - // offset - ticks %= ( maxBar * MidiTime::ticksPerBar() ); - - // wrap milli second counter - setToTimeByTicks(ticks); - - m_vstSyncController.setPlaybackJumped( true ); - } + enforceLoop(MidiTime{0}, MidiTime{Engine::getBBTrackContainer()->lengthOfCurrentBB(), 0}); + } + else if (m_playMode == Mode_PlayPattern && m_loopPattern && !loopEnabled) + { + enforceLoop(MidiTime{0}, m_patternToPlay->length()); } - m_playPos[m_playMode].setTicks( ticks ); - if (checkLoop || m_loopRenderRemaining > 1) + // Handle loop points, and inform VST plugins of the loop status + if (loopEnabled || m_loopRenderRemaining > 1) { m_vstSyncController.startCycle( - tl->loopBegin().getTicks(), tl->loopEnd().getTicks() ); + timeline->loopBegin().getTicks(), timeline->loopEnd().getTicks()); - // if looping-mode is enabled and we have got - // past the looping range, return to the - // beginning of the range - if( m_playPos[m_playMode] >= tl->loopEnd() ) + // Loop if necessary, and decrement the remaining loops if we did + if (enforceLoop(timeline->loopBegin(), timeline->loopEnd()) + && m_loopRenderRemaining > 1) { - if (m_loopRenderRemaining > 1) - m_loopRenderRemaining--; - ticks = tl->loopBegin().getTicks(); - m_playPos[m_playMode].setTicks( ticks ); - setToTime(tl->loopBegin()); - - m_vstSyncController.setPlaybackJumped( true ); - - emit updateSampleTracks(); + m_loopRenderRemaining--; } } else { m_vstSyncController.stopCycle(); } - - currentFrame = fmodf( currentFrame, framesPerTick ); - m_playPos[m_playMode].setCurrentFrame( currentFrame ); } - if( framesPlayed == 0 ) - { - // update VST sync position after we've corrected frame/ - // tick count but before actually playing any frames - m_vstSyncController.setAbsolutePosition( - m_playPos[m_playMode].getTicks() - + m_playPos[m_playMode].currentFrame() - / (double) framesPerTick ); - } + const f_cnt_t framesUntilNextPeriod = framesPerPeriod - frameOffsetInPeriod; + const f_cnt_t framesUntilNextTick = static_cast(std::ceil(framesPerTick - frameOffsetInTick)); - f_cnt_t framesToPlay = - Engine::mixer()->framesPerPeriod() - framesPlayed; + // We want to proceed to the next buffer or tick, whichever is closer + const auto framesToPlay = std::min(framesUntilNextPeriod, framesUntilNextTick); - f_cnt_t framesLeft = ( f_cnt_t )framesPerTick - - ( f_cnt_t )currentFrame; - // skip last frame fraction - if( framesLeft == 0 ) + if (frameOffsetInPeriod == 0) { - ++framesPlayed; - m_playPos[m_playMode].setCurrentFrame( currentFrame - + 1.0f ); - continue; - } - // do we have samples left in this tick but these are less - // than samples we have to play? - if( framesLeft < framesToPlay ) - { - // then set framesToPlay to remaining samples, the - // rest will be played in next loop - framesToPlay = framesLeft; + // First frame of buffer: update VST sync position. + // This must be done after we've corrected the frame/tick count, + // but before actually playing any frames. + m_vstSyncController.setAbsolutePosition(getPlayPos().getTicks() + + getPlayPos().currentFrame() / static_cast(framesPerTick)); + m_vstSyncController.update(); } - if( ( f_cnt_t ) currentFrame == 0 ) + if (static_cast(frameOffsetInTick) == 0) { - processAutomations(trackList, m_playPos[m_playMode], framesToPlay); - - // loop through all tracks and play them - for( int i = 0; i < trackList.size(); ++i ) + // First frame of tick: process automation and play tracks + processAutomations(trackList, getPlayPos(), framesToPlay); + for (const auto track : trackList) { - trackList[i]->play( m_playPos[m_playMode], - framesToPlay, - framesPlayed, tcoNum ); + track->play(getPlayPos(), framesToPlay, frameOffsetInPeriod, clipNum); } } - // update frame-counters - framesPlayed += framesToPlay; - m_playPos[m_playMode].setCurrentFrame( framesToPlay + - currentFrame ); + // Update frame counters + frameOffsetInPeriod += framesToPlay; + frameOffsetInTick += framesToPlay; + getPlayPos().setCurrentFrame(frameOffsetInTick); m_elapsedMilliSeconds[m_playMode] += MidiTime::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); m_elapsedBars = m_playPos[Mode_PlaySong].getBar(); - m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerBar() ) / 48; + m_elapsedTicks = (m_playPos[Mode_PlaySong].getTicks() % ticksPerBar()) / 48; } } From ee7175be7508233248942e35826036ff2dda8d4f Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Fri, 27 Nov 2020 16:42:23 +0100 Subject: [PATCH 157/180] Bitinvader - Fix saving with automation and division by 0 (#5805) * Prevent division by 0 in bitInvader::normalize(). * Save and load whole wavetable on save/load and also clear wavetable before loading a new one. Co-authored-by: Dominic Clark Co-authored-by: Spekular Co-authored-by: thmueller64 <64359888+thmueller64@users.noreply.github.com> --- plugins/bit_invader/bit_invader.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index f59f2bfd178..948b845f471 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -44,6 +44,8 @@ #include "plugin_export.h" +static const int wavetableSize = 200; + extern "C" { @@ -72,8 +74,8 @@ bSynth::bSynth( float * _shape, NotePlayHandle * _nph, bool _interpolation, sample_rate( _sample_rate ), interpolation( _interpolation) { - sample_shape = new float[200]; - for (int i=0; i < 200; ++i) + sample_shape = new float[wavetableSize]; + for (int i=0; i < wavetableSize; ++i) { sample_shape[i] = _shape[i] * _factor; } @@ -138,12 +140,12 @@ sample_t bSynth::nextStringSample( float sample_length ) bitInvader::bitInvader( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &bitinvader_plugin_descriptor ), - m_sampleLength( 128, 4, 200, 1, this, tr( "Sample length" ) ), - m_graph( -1.0f, 1.0f, 200, this ), + m_sampleLength(128, 4, wavetableSize, 1, this, tr("Sample length")), + m_graph(-1.0f, 1.0f, wavetableSize, this), m_interpolation( false, this ), m_normalize( false, this ) { - + lengthChanged(); m_graph.setWaveToSine(); @@ -177,8 +179,8 @@ void bitInvader::saveSettings( QDomDocument & _doc, QDomElement & _this ) // Save sample shape base64-encoded QString sampleString; - base64::encode( (const char *)m_graph.samples(), - m_graph.length() * sizeof(float), sampleString ); + base64::encode((const char *)m_graph.samples(), + wavetableSize * sizeof(float), sampleString); _this.setAttribute( "sampleShape", sampleString ); @@ -194,6 +196,9 @@ void bitInvader::saveSettings( QDomDocument & _doc, QDomElement & _this ) void bitInvader::loadSettings( const QDomElement & _this ) { + // Clear wavetable before loading a new + m_graph.clear(); + // Load sample length m_sampleLength.loadSettings( _this, "sampleLength" ); @@ -204,8 +209,9 @@ void bitInvader::loadSettings( const QDomElement & _this ) char * dst = 0; base64::decode( _this.attribute( "sampleShape"), &dst, &size ); - m_graph.setLength( sampleLength ); - m_graph.setSamples( (float*) dst ); + m_graph.setLength(size / sizeof(float)); + m_graph.setSamples(reinterpret_cast(dst)); + m_graph.setLength(sampleLength); delete[] dst; // Load LED normalize @@ -240,7 +246,7 @@ void bitInvader::samplesChanged( int _begin, int _end ) void bitInvader::normalize() { // analyze - float max = 0; + float max = std::numeric_limits::epsilon(); const float* samples = m_graph.samples(); for(int i=0; i < m_graph.length(); i++) { From f7128700b452609d9aa85a9b0417629cfa001c03 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 28 Nov 2020 15:15:28 +0900 Subject: [PATCH 158/180] Ensure instrument window resize correctly on instrument changes (#5797) --- src/tracks/InstrumentTrack.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a4b4d0231d1..4333627e807 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1517,14 +1517,14 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_pianoView->setMaximumHeight( PIANO_HEIGHT ); vlayout->addWidget( generalSettingsWidget ); - vlayout->addWidget( m_tabWidget, 1 ); + // Use QWidgetItem explicitly to make the size hint change on instrument changes + // QLayout::addWidget() uses QWidgetItemV2 with size hint caching + vlayout->insertItem(1, new QWidgetItem(m_tabWidget)); vlayout->addWidget( m_pianoView ); setModel( _itv->model() ); updateInstrumentView(); - resize( sizeHint() ); - QMdiSubWindow* subWin = gui->mainWindow()->addWindowedWidget( this ); Qt::WindowFlags flags = subWin->windowFlags(); flags |= Qt::MSWindowsFixedSizeDialogHint; @@ -1691,6 +1691,15 @@ void InstrumentTrackWindow::updateInstrumentView() adjustTabSize(m_instrumentView); m_pianoView->setVisible(m_track->m_instrument->hasNoteInput()); + // adjust window size + layout()->invalidate(); + resize(sizeHint()); + if (parentWidget()) + { + parentWidget()->resize(parentWidget()->sizeHint()); + } + update(); + m_instrumentView->update(); } } From 7a85b4d547bc22bde6cb3ca7e77ad45ae19306c6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 21 Nov 2020 04:08:21 +0100 Subject: [PATCH 159/180] Lv2Manager: Print issues uniq-ed --- include/Lv2ControlBase.h | 2 +- include/Lv2Proc.h | 2 +- include/PluginIssue.h | 2 ++ src/core/PluginIssue.cpp | 18 ++++++++++++++++++ src/core/lv2/Lv2ControlBase.cpp | 4 ++-- src/core/lv2/Lv2Manager.cpp | 15 ++++++++++++++- src/core/lv2/Lv2Proc.cpp | 12 +----------- 7 files changed, 39 insertions(+), 16 deletions(-) diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index acfbea25ee4..176b5045c56 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -70,7 +70,7 @@ class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups { public: static Plugin::PluginTypes check(const LilvPlugin* m_plugin, - std::vector &issues, bool printIssues = false); + std::vector &issues); const LilvPlugin* getPlugin() const { return m_plugin; } diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 81e25f785f5..702f41cb9a1 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -61,7 +61,7 @@ class Lv2Proc : public LinkedModelGroup { public: static Plugin::PluginTypes check(const LilvPlugin* plugin, - std::vector &issues, bool printIssues = false); + std::vector &issues); /* ctor/dtor diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 7f42a9409d9..18bb6c5e74a 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -63,6 +63,8 @@ class PluginIssue { } PluginIssueType type() const { return m_issueType; } + bool operator==(const PluginIssue& other) const; + bool operator<(const PluginIssue& other) const; friend QDebug operator<<(QDebug stream, const PluginIssue& iss); }; diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 6214eba0281..1b54fb41cc7 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -67,6 +67,24 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) +bool PluginIssue::operator==(const PluginIssue &other) const +{ + return (m_issueType == other.m_issueType) && (m_info == other.m_info); +} + + + + +bool PluginIssue::operator<(const PluginIssue &other) const +{ + return (m_issueType != other.m_issueType) + ? m_issueType < other.m_issueType + : m_info < other.m_info; +} + + + + QDebug operator<<(QDebug stream, const PluginIssue &iss) { stream << PluginIssue::msgFor(iss.m_issueType); diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 00326eeb88c..e7ede9dd60d 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -37,10 +37,10 @@ Plugin::PluginTypes Lv2ControlBase::check(const LilvPlugin *plugin, - std::vector &issues, bool printIssues) + std::vector &issues) { // for some reason, all checks can be done by one processor... - return Lv2Proc::check(plugin, issues, printIssues); + return Lv2Proc::check(plugin, issues); } diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 93946d4262b..635b5462c44 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -118,7 +118,20 @@ void Lv2Manager::initPlugins() const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); std::vector issues; - Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues, m_debug); + Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues); + std::sort(issues.begin(), issues.end()); + auto last = std::unique(issues.begin(), issues.end()); + issues.erase(last, issues.end()); + if (m_debug && issues.size()) + { + qDebug() << "Lv2 plugin" + << qStringFromPluginNode(curPlug, lilv_plugin_get_name) + << "(URI:" + << lilv_node_as_uri(lilv_plugin_get_uri(curPlug)) + << ") can not be loaded:"; + for (const PluginIssue& iss : issues) { qDebug() << " - " << iss; } + } + Lv2Info info(curPlug, type, issues.empty()); m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index e27ffaa9de5..e02991789de 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -58,7 +58,7 @@ struct MidiInputEvent Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, - std::vector& issues, bool printIssues) + std::vector& issues) { unsigned maxPorts = lilv_plugin_get_num_ports(plugin); enum { inCount, outCount, maxCount }; @@ -128,16 +128,6 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, } } - if (printIssues && issues.size()) - { - qDebug() << "Lv2 plugin" - << qStringFromPluginNode(plugin, lilv_plugin_get_name) - << "(URI:" - << lilv_node_as_uri(lilv_plugin_get_uri(plugin)) - << ") can not be loaded:"; - for (const PluginIssue& iss : issues) { qDebug() << " - " << iss; } - } - return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2) ? Plugin::Undefined : (audioChannels[inCount] > 0) From a2e71c81dec5e17dcabc05ea39360f1c9766ce26 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 15 Nov 2020 22:44:52 +0100 Subject: [PATCH 160/180] Lv2: Use port-property "logarithmic" This also adds more min/max checks, mostly for logarithmic scales. Since this raised some warnings for logarithmic CV ports, the CV metadata is now also read (but CV ports are still not supported). --- include/Lv2Ports.h | 2 + include/PluginIssue.h | 9 +++ src/core/PluginIssue.cpp | 8 +++ src/core/lv2/Lv2Ports.cpp | 145 ++++++++++++++++++++++++++------------ src/core/lv2/Lv2Proc.cpp | 7 +- 5 files changed, 124 insertions(+), 47 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index a0d68f24fd8..1b37d518b3c 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -108,6 +108,8 @@ struct Meta Flow m_flow = Flow::Unknown; Vis m_vis = Vis::None; + bool m_logarithmic = false; + bool m_optional = false; bool m_used = true; diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 18bb6c5e74a..8bfad5bf10f 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -32,18 +32,27 @@ //! LMMS Plugins should use this to indicate errors enum PluginIssueType { + // port flow & type unknownPortFlow, unknownPortType, + // channel count tooManyInputChannels, tooManyOutputChannels, tooManyMidiInputChannels, tooManyMidiOutputChannels, noOutputChannel, + // port metadata portHasNoDef, portHasNoMin, portHasNoMax, + minGreaterMax, defaultValueNotInRange, + logScaleMinMissing, + logScaleMaxMissing, + logScaleMinMaxDifferentSigns, + // features featureNotSupported, //!< plugin requires functionality LMMS can't offer + // misc badPortType, //!< port type not supported blacklisted, noIssue diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 1b54fb41cc7..7d43586f5b6 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -50,8 +50,16 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "port is missing min value"; case portHasNoMax: return "port is missing max value"; + case minGreaterMax: + return "port minimum is greater than maximum"; case defaultValueNotInRange: return "default value is not in range [min, max]"; + case logScaleMinMissing: + return "logscale requires minimum value"; + case logScaleMaxMissing: + return "logscale requires maximum value"; + case logScaleMinMaxDifferentSigns: + return "logscale with min < 0 < max"; case featureNotSupported: return "required feature not supported"; case badPortType: diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 3bca0c89f1e..4bd77f0bcbb 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -28,6 +28,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include "Engine.h" #include "Lv2Basics.h" @@ -126,75 +127,106 @@ std::vector Meta::get(const LilvPlugin *plugin, issue(unknownPortFlow, portName); } - m_def = .0f; m_min = .0f; m_max = .0f; + m_def = .0f; + m_min = std::numeric_limits::lowest(); + m_max = std::numeric_limits::max(); + auto m_min_set = [this]{ return m_min != std::numeric_limits::lowest(); }; + auto m_max_set = [this]{ return m_max != std::numeric_limits::max(); }; m_type = Type::Unknown; - if (isA(LV2_CORE__ControlPort)) + if (isA(LV2_CORE__ControlPort) || isA(LV2_CORE__CVPort)) { - m_type = Type::Control; + // Read metadata for control ports + // CV ports are mostly the same as control ports, so we take + // mostly the same metadata - if (m_flow == Flow::Input) + if (isA(LV2_CORE__CVPort)) { - bool isToggle = m_vis == Vis::Toggled; + // currently not supported, but we can still check the metadata + issue(badPortType, "cvPort"); + } - LilvNode * defN, * minN = nullptr, * maxN = nullptr; - lilv_port_get_range(plugin, lilvPort, &defN, - isToggle ? nullptr : &minN, - isToggle ? nullptr : &maxN); - AutoLilvNode def(defN), min(minN), max(maxN); + m_type = isA(LV2_CORE__CVPort) ? Type::Cv : Type::Control; - auto takeRangeValue = [&](LilvNode* node, - float& storeHere, PluginIssueType it) - { - if (node) { storeHere = lilv_node_as_float(node); } - else { issue(it, portName); } - }; + bool isToggle = m_vis == Vis::Toggled; - takeRangeValue(def.get(), m_def, portHasNoDef); - if (isToggle) + LilvNode * defN, * minN = nullptr, * maxN = nullptr; + lilv_port_get_range(plugin, lilvPort, &defN, + isToggle ? nullptr : &minN, + isToggle ? nullptr : &maxN); + AutoLilvNode def(defN), min(minN), max(maxN); + + auto takeRangeValue = [&](LilvNode* node, + float& storeHere, PluginIssueType it) + { + if (node) { storeHere = lilv_node_as_float(node); } + else { - m_min = .0f; - m_max = 1.f; - if(def.get() && m_def != m_min && m_def != m_max) + // CV ports do not require ranges + if(m_flow == Flow::Input && m_type != Type::Cv) { - issue(defaultValueNotInRange, portName); + issue(it, portName); } } - else + }; + + takeRangeValue(def.get(), m_def, portHasNoDef); + if (isToggle) + { + m_min = .0f; + m_max = 1.f; + if(def.get() && m_def != m_min && m_def != m_max) { - takeRangeValue(min.get(), m_min, portHasNoMin); - takeRangeValue(max.get(), m_max, portHasNoMax); - if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } + issue(defaultValueNotInRange, portName); + } + } + else + { + // take min/max + takeRangeValue(min.get(), m_min, portHasNoMin); + takeRangeValue(max.get(), m_max, portHasNoMax); + if(m_type == Type::Cv) + { + // no range is allowed and bashed to [-1,+1], + // but only min or only max does not make sense + if(!m_min_set() && !m_max_set()) + { + m_min = -1.f; + m_max = +1.f; + } + else if(!m_min_set()) { issue(portHasNoMin, portName); } + else if(!m_max_set()) { issue(portHasNoMax, portName); } + } + if(m_min > m_max) { issue(minGreaterMax, portName); } + + // sampleRate + if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } - if (def.get()) + // default value + if (def.get()) + { + if (m_def < m_min) { issue(defaultValueNotInRange, portName); } + else if (m_def > m_max) { - if (m_def < m_min) { issue(defaultValueNotInRange, portName); } - else if (m_def > m_max) + if(m_sampleRate) { - if(m_sampleRate) - { - // multiplying with sample rate will hopefully lead us - // to a good default value - } - else { issue(defaultValueNotInRange, portName); } + // multiplying with sample rate will hopefully lead us + // to a good default value } + else { issue(defaultValueNotInRange, portName); } } + } - if (m_max - m_min > 15.0f) - { - // range too large for spinbox visualisation, use knobs - // e.g. 0...15 would be OK - m_vis = Vis::None; - } + // visualization + if (m_max - m_min > 15.0f) + { + // range too large for spinbox visualisation, use knobs + // e.g. 0...15 would be OK + m_vis = Vis::None; } } } else if (isA(LV2_CORE__AudioPort)) { m_type = Type::Audio; } - else if (isA(LV2_CORE__CVPort)) - { - issue(badPortType, "cvPort"); - m_type = Type::Cv; - } else if (isA(LV2_ATOM__AtomPort)) { AutoLilvNode uriAtomSequence(Engine::getLv2Manager()->uri(LV2_ATOM__Sequence)); @@ -221,6 +253,27 @@ std::vector Meta::get(const LilvPlugin *plugin, } } + if (hasProperty(LV2_PORT_PROPS__logarithmic)) + { + // check min/max available + // we requre them anyways, but this will detect plugins that will + // be non-Lv2-conforming + if(m_min == std::numeric_limits::lowest()) + { + issue(PluginIssueType::logScaleMinMissing, portName); + } + if(m_max == std::numeric_limits::max()) + { + issue(PluginIssueType::logScaleMaxMissing, portName); + } + // forbid min < 0 < max + if(m_min < 0.f && m_max > 0.f) + { + issue(PluginIssueType::logScaleMinMaxDifferentSigns, portName); + } + m_logarithmic = true; + } + return portIssues; } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index e02991789de..0ab204ae971 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -520,7 +520,12 @@ void Lv2Proc::createPort(std::size_t portNum) nullptr, dispName)); break; } - } + if(meta.m_logarithmic) + { + ctrl->m_connectedModel->setScaleLogarithmic(); + } + + } // if m_flow == Input port = ctrl; break; } From 6e081265ba85c0933deb7e87c4d4b47ce26c3930 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Sun, 29 Nov 2020 15:46:13 -0300 Subject: [PATCH 161/180] Rename MidiTime to TimePos (#5684) Fixes #4866 --- include/AutomatableModel.h | 4 +- include/AutomationEditor.h | 8 +- include/AutomationPattern.h | 16 +- include/AutomationTrack.h | 4 +- include/BBTrack.h | 4 +- include/BBTrackContainer.h | 4 +- include/Instrument.h | 4 +- include/InstrumentTrack.h | 8 +- include/Lv2ControlBase.h | 2 +- include/Lv2Proc.h | 4 +- include/MidiAlsaSeq.h | 2 +- include/MidiApple.h | 2 +- include/MidiClient.h | 4 +- include/MidiController.h | 4 +- include/MidiEventProcessor.h | 6 +- include/MidiPort.h | 6 +- include/MidiWinMM.h | 2 +- include/Note.h | 36 ++-- include/NotePlayHandle.h | 8 +- include/Pattern.h | 2 +- include/PianoRoll.h | 18 +- include/SampleRecordHandle.h | 4 +- include/SampleTrack.h | 8 +- include/Song.h | 36 ++-- include/SongEditor.h | 4 +- include/StepRecorder.h | 14 +- include/StepRecorderWidget.h | 20 +-- include/TimeLineWidget.h | 26 +-- include/{MidiTime.h => TimePos.h} | 39 +++-- include/Track.h | 56 +++---- include/TrackContainer.h | 4 +- include/TrackContainerView.h | 6 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 2 +- plugins/Lv2Instrument/Lv2Instrument.h | 2 +- plugins/MidiImport/MidiImport.cpp | 14 +- plugins/OpulenZ/OpulenZ.cpp | 2 +- plugins/OpulenZ/OpulenZ.h | 2 +- plugins/carlabase/carla.cpp | 2 +- plugins/carlabase/carla.h | 2 +- plugins/sfxr/sfxr.cpp | 1 - plugins/vestige/vestige.cpp | 2 +- plugins/vestige/vestige.h | 2 +- plugins/zynaddsubfx/ZynAddSubFx.cpp | 2 +- plugins/zynaddsubfx/ZynAddSubFx.h | 2 +- src/core/AutomatableModel.cpp | 6 +- src/core/AutomationPattern.cpp | 42 ++--- src/core/BBTrackContainer.cpp | 14 +- src/core/CMakeLists.txt | 2 +- src/core/InstrumentFunctions.cpp | 2 +- src/core/Mixer.cpp | 2 +- src/core/Note.cpp | 8 +- src/core/NotePlayHandle.cpp | 8 +- src/core/SampleRecordHandle.cpp | 2 +- src/core/Song.cpp | 32 ++-- src/core/StepRecorder.cpp | 8 +- src/core/{midi/MidiTime.cpp => TimePos.cpp} | 58 +++---- src/core/Track.cpp | 172 ++++++++++---------- src/core/TrackContainer.cpp | 10 +- src/core/lv2/Lv2ControlBase.cpp | 2 +- src/core/lv2/Lv2Proc.cpp | 4 +- src/core/midi/MidiAlsaSeq.cpp | 16 +- src/core/midi/MidiApple.cpp | 2 +- src/core/midi/MidiClient.cpp | 2 +- src/core/midi/MidiController.cpp | 2 +- src/core/midi/MidiPort.cpp | 4 +- src/core/midi/MidiWinMM.cpp | 2 +- src/gui/AutomationPatternView.cpp | 2 +- src/gui/ControllerConnectionDialog.cpp | 2 +- src/gui/MainWindow.cpp | 2 +- src/gui/TimeLineWidget.cpp | 26 +-- src/gui/TrackContainerView.cpp | 4 +- src/gui/editors/AutomationEditor.cpp | 80 ++++----- src/gui/editors/BBEditor.cpp | 2 +- src/gui/editors/PianoRoll.cpp | 148 ++++++++--------- src/gui/editors/SongEditor.cpp | 42 ++--- src/gui/widgets/StepRecorderWidget.cpp | 14 +- src/tracks/AutomationTrack.cpp | 8 +- src/tracks/BBTrack.cpp | 12 +- src/tracks/InstrumentTrack.cpp | 16 +- src/tracks/Pattern.cpp | 46 +++--- src/tracks/SampleTrack.cpp | 16 +- tests/src/tracks/AutomationTrackTest.cpp | 20 +-- 82 files changed, 622 insertions(+), 618 deletions(-) rename include/{MidiTime.h => TimePos.h} (74%) rename src/core/{midi/MidiTime.cpp => TimePos.cpp} (64%) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index c332858b7b6..2091b3f9a86 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -30,7 +30,7 @@ #include "JournallingObject.h" #include "Model.h" -#include "MidiTime.h" +#include "TimePos.h" #include "ValueBuffer.h" #include "MemoryManager.h" #include "ModelVisitor.h" @@ -281,7 +281,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject return false; } - float globalAutomationValueAt( const MidiTime& time ); + float globalAutomationValueAt( const TimePos& time ); void setStrictStepSize( const bool b ) { diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 4813759697f..a4e9d528634 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -34,7 +34,7 @@ #include "lmms_basics.h" #include "JournallingObject.h" -#include "MidiTime.h" +#include "TimePos.h" #include "AutomationPattern.h" #include "ComboBoxModel.h" #include "Knob.h" @@ -153,7 +153,7 @@ protected slots: void pasteValues(); void deleteSelectedValues(); - void updatePosition( const MidiTime & t ); + void updatePosition( const TimePos & t ); void zoomingXChanged(); void zoomingYChanged(); @@ -215,7 +215,7 @@ protected slots: QScrollBar * m_leftRightScroll; QScrollBar * m_topBottomScroll; - MidiTime m_currentPosition; + TimePos m_currentPosition; Actions m_action; @@ -265,7 +265,7 @@ protected slots: signals: void currentPatternChanged(); - void positionChanged( const MidiTime & ); + void positionChanged( const TimePos & ); } ; diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index cad9d0a1d00..0e98c48d39f 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -34,7 +34,7 @@ class AutomationTrack; -class MidiTime; +class TimePos; @@ -74,19 +74,19 @@ class LMMS_EXPORT AutomationPattern : public TrackContentObject } void setTension( QString _new_tension ); - MidiTime timeMapLength() const; + TimePos timeMapLength() const; void updateLength(); - MidiTime putValue( const MidiTime & time, + TimePos putValue( const TimePos & time, const float value, const bool quantPos = true, const bool ignoreSurroundingPoints = true ); - void removeValue( const MidiTime & time ); + void removeValue( const TimePos & time ); - void recordValue(MidiTime time, float value); + void recordValue(TimePos time, float value); - MidiTime setDragValue( const MidiTime & time, + TimePos setDragValue( const TimePos & time, const float value, const bool quantPos = true, const bool controlKey = false ); @@ -134,8 +134,8 @@ class LMMS_EXPORT AutomationPattern : public TrackContentObject return m_timeMap.isEmpty() == false; } - float valueAt( const MidiTime & _time ) const; - float *valuesAfter( const MidiTime & _time ) const; + float valueAt( const TimePos & _time ) const; + float *valuesAfter( const TimePos & _time ) const; const QString name() const; diff --git a/include/AutomationTrack.h b/include/AutomationTrack.h index c8a0009e966..0d53faaef19 100644 --- a/include/AutomationTrack.h +++ b/include/AutomationTrack.h @@ -37,7 +37,7 @@ class AutomationTrack : public Track AutomationTrack( TrackContainer* tc, bool _hidden = false ); virtual ~AutomationTrack() = default; - virtual bool play( const MidiTime & _start, const fpp_t _frames, + virtual bool play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ) override; QString nodeName() const override @@ -46,7 +46,7 @@ class AutomationTrack : public Track } TrackView * createView( TrackContainerView* ) override; - TrackContentObject* createTCO(const MidiTime & pos) override; + TrackContentObject* createTCO(const TimePos & pos) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, QDomElement & _parent ) override; diff --git a/include/BBTrack.h b/include/BBTrack.h index 5f9cfe408f0..3c2f2ede518 100644 --- a/include/BBTrack.h +++ b/include/BBTrack.h @@ -103,10 +103,10 @@ class LMMS_EXPORT BBTrack : public Track BBTrack( TrackContainer* tc ); virtual ~BBTrack(); - virtual bool play( const MidiTime & _start, const fpp_t _frames, + virtual bool play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ) override; TrackView * createView( TrackContainerView* tcv ) override; - TrackContentObject* createTCO(const MidiTime & pos) override; + TrackContentObject* createTCO(const TimePos & pos) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, QDomElement & _parent ) override; diff --git a/include/BBTrackContainer.h b/include/BBTrackContainer.h index 17d6eb5fed1..1d8f73901e0 100644 --- a/include/BBTrackContainer.h +++ b/include/BBTrackContainer.h @@ -38,7 +38,7 @@ class LMMS_EXPORT BBTrackContainer : public TrackContainer BBTrackContainer(); virtual ~BBTrackContainer(); - virtual bool play( MidiTime _start, const fpp_t _frames, + virtual bool play( TimePos _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ); void updateAfterTrackAdd() override; @@ -62,7 +62,7 @@ class LMMS_EXPORT BBTrackContainer : public TrackContainer void fixIncorrectPositions(); void createTCOsForBB( int _bb ); - AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum) const override; + AutomatedValueMap automatedValuesAt(TimePos time, int tcoNum) const override; public slots: void play(); diff --git a/include/Instrument.h b/include/Instrument.h index 1dbf1ba5816..eab218081e6 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -30,8 +30,8 @@ #include "lmms_export.h" #include "lmms_basics.h" #include "MemoryManager.h" -#include "MidiTime.h" #include "Plugin.h" +#include "TimePos.h" // forward-declarations @@ -105,7 +105,7 @@ class LMMS_EXPORT Instrument : public Plugin // sub-classes can re-implement this for receiving all incoming // MIDI-events - inline virtual bool handleMidiEvent( const MidiEvent&, const MidiTime& = MidiTime(), f_cnt_t offset = 0 ) + inline virtual bool handleMidiEvent( const MidiEvent&, const TimePos& = TimePos(), f_cnt_t offset = 0 ) { return true; } diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 944791552dd..b4bac9136a7 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -80,8 +80,8 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor MidiEvent applyMasterKey( const MidiEvent& event ); - void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ) override; - void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ) override; + void processInEvent( const MidiEvent& event, const TimePos& time = TimePos(), f_cnt_t offset = 0 ) override; + void processOutEvent( const MidiEvent& event, const TimePos& time = TimePos(), f_cnt_t offset = 0 ) override; // silence all running notes played by this track void silenceAllNotes( bool removeIPH = false ); @@ -130,13 +130,13 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor } // play everything in given frame-range - creates note-play-handles - virtual bool play( const MidiTime & _start, const fpp_t _frames, + virtual bool play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ) override; // create new view for me TrackView * createView( TrackContainerView* tcv ) override; // create new track-content-object = pattern - TrackContentObject* createTCO(const MidiTime & pos) override; + TrackContentObject* createTCO(const TimePos & pos) override; // called by track diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 176b5045c56..de90960dcd7 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -134,7 +134,7 @@ class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups QString nodeName() const { return "lv2controls"; } bool hasNoteInput() const; void handleMidiInputEvent(const class MidiEvent &event, - const class MidiTime &time, f_cnt_t offset); + const class TimePos &time, f_cnt_t offset); private: //! Return the DataFile settings type diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 702f41cb9a1..312f9cf34f2 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -37,10 +37,10 @@ #include "Lv2Features.h" #include "LinkedModelGroups.h" #include "MidiEvent.h" -#include "MidiTime.h" #include "Plugin.h" #include "PluginIssue.h" #include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" +#include "TimePos.h" // forward declare port structs/enums namespace Lv2Ports @@ -144,7 +144,7 @@ class Lv2Proc : public LinkedModelGroup void run(fpp_t frames); void handleMidiInputEvent(const class MidiEvent &event, - const MidiTime &time, f_cnt_t offset); + const TimePos &time, f_cnt_t offset); /* misc diff --git a/include/MidiAlsaSeq.h b/include/MidiAlsaSeq.h index b6e4987210b..5db5357d829 100644 --- a/include/MidiAlsaSeq.h +++ b/include/MidiAlsaSeq.h @@ -66,7 +66,7 @@ class MidiAlsaSeq : public QThread, public MidiClient virtual void processOutEvent( const MidiEvent & _me, - const MidiTime & _time, + const TimePos & _time, const MidiPort * _port ) override; void applyPortMode( MidiPort * _port ) override; diff --git a/include/MidiApple.h b/include/MidiApple.h index 21b88073b08..4ea4805e5a8 100644 --- a/include/MidiApple.h +++ b/include/MidiApple.h @@ -60,7 +60,7 @@ class MidiApple : public QObject, public MidiClient } virtual void processOutEvent( const MidiEvent & _me, - const MidiTime & _time, + const TimePos & _time, const MidiPort * _port ); virtual void applyPortMode( MidiPort * _port ); diff --git a/include/MidiClient.h b/include/MidiClient.h index f06cac89394..ff441e24f1d 100644 --- a/include/MidiClient.h +++ b/include/MidiClient.h @@ -46,7 +46,7 @@ class MidiClient // to be implemented by sub-classes virtual void processOutEvent( const MidiEvent & _me, - const MidiTime & _time, + const TimePos & _time, const MidiPort * _port ) = 0; // inheriting classes can re-implement this for being able to update @@ -141,7 +141,7 @@ class MidiClientRaw : public MidiClient private: // this does MIDI-event-process void processParsedEvent(); - void processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ) override; + void processOutEvent( const MidiEvent& event, const TimePos& time, const MidiPort* port ) override; // small helper function returning length of a certain event - this // is necessary for parsing raw-MIDI-data diff --git a/include/MidiController.h b/include/MidiController.h index 43f928a25cb..74b408f867d 100644 --- a/include/MidiController.h +++ b/include/MidiController.h @@ -44,10 +44,10 @@ class MidiController : public Controller, public MidiEventProcessor virtual ~MidiController(); virtual void processInEvent( const MidiEvent & _me, - const MidiTime & _time, f_cnt_t offset = 0 ) override; + const TimePos & _time, f_cnt_t offset = 0 ) override; virtual void processOutEvent( const MidiEvent& _me, - const MidiTime & _time, f_cnt_t offset = 0 ) override + const TimePos & _time, f_cnt_t offset = 0 ) override { // No output yet } diff --git a/include/MidiEventProcessor.h b/include/MidiEventProcessor.h index b48f75d2bfb..3ded20430d2 100644 --- a/include/MidiEventProcessor.h +++ b/include/MidiEventProcessor.h @@ -26,8 +26,8 @@ #define MIDI_EVENT_PROCESSOR_H #include "MidiEvent.h" -#include "MidiTime.h" #include "MemoryManager.h" +#include "TimePos.h" // all classes being able to process MIDI-events should inherit from this class MidiEventProcessor @@ -43,8 +43,8 @@ class MidiEventProcessor } // to be implemented by inheriting classes - virtual void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ) = 0; - virtual void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ) = 0; + virtual void processInEvent( const MidiEvent& event, const TimePos& time = TimePos(), f_cnt_t offset = 0 ) = 0; + virtual void processOutEvent( const MidiEvent& event, const TimePos& time = TimePos(), f_cnt_t offset = 0 ) = 0; } ; diff --git a/include/MidiPort.h b/include/MidiPort.h index acf95992fad..9592147da5e 100644 --- a/include/MidiPort.h +++ b/include/MidiPort.h @@ -31,7 +31,7 @@ #include #include "Midi.h" -#include "MidiTime.h" +#include "TimePos.h" #include "AutomatableModel.h" @@ -102,8 +102,8 @@ class MidiPort : public Model, public SerializingObject return outputChannel() ? outputChannel() - 1 : 0; } - void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime() ); - void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime() ); + void processInEvent( const MidiEvent& event, const TimePos& time = TimePos() ); + void processOutEvent( const MidiEvent& event, const TimePos& time = TimePos() ); void saveSettings( QDomDocument& doc, QDomElement& thisElement ) override; diff --git a/include/MidiWinMM.h b/include/MidiWinMM.h index a51fa7ed669..cbb13998313 100644 --- a/include/MidiWinMM.h +++ b/include/MidiWinMM.h @@ -64,7 +64,7 @@ class MidiWinMM : public QObject, public MidiClient virtual void processOutEvent( const MidiEvent & _me, - const MidiTime & _time, + const TimePos & _time, const MidiPort * _port ); virtual void applyPortMode( MidiPort * _port ); diff --git a/include/Note.h b/include/Note.h index 30969b4c8f9..c08a3e24e55 100644 --- a/include/Note.h +++ b/include/Note.h @@ -30,8 +30,8 @@ #include "volume.h" #include "panning.h" -#include "MidiTime.h" #include "SerializingObject.h" +#include "TimePos.h" class DetuningHelper; @@ -81,8 +81,8 @@ const float MaxDetuning = 4 * 12.0f; class LMMS_EXPORT Note : public SerializingObject { public: - Note( const MidiTime & length = MidiTime( 0 ), - const MidiTime & pos = MidiTime( 0 ), + Note( const TimePos & length = TimePos( 0 ), + const TimePos & pos = TimePos( 0 ), int key = DefaultKey, volume_t volume = DefaultVolume, panning_t panning = DefaultPanning, @@ -93,9 +93,9 @@ class LMMS_EXPORT Note : public SerializingObject // used by GUI inline void setSelected( const bool selected ) { m_selected = selected; } inline void setOldKey( const int oldKey ) { m_oldKey = oldKey; } - inline void setOldPos( const MidiTime & oldPos ) { m_oldPos = oldPos; } + inline void setOldPos( const TimePos & oldPos ) { m_oldPos = oldPos; } - inline void setOldLength( const MidiTime & oldLength ) + inline void setOldLength( const TimePos & oldLength ) { m_oldLength = oldLength; } @@ -105,8 +105,8 @@ class LMMS_EXPORT Note : public SerializingObject } - void setLength( const MidiTime & length ); - void setPos( const MidiTime & pos ); + void setLength( const TimePos & length ); + void setPos( const TimePos & pos ); void setKey( const int key ); virtual void setVolume( volume_t volume ); virtual void setPanning( panning_t panning ); @@ -138,12 +138,12 @@ class LMMS_EXPORT Note : public SerializingObject return m_oldKey; } - inline MidiTime oldPos() const + inline TimePos oldPos() const { return m_oldPos; } - inline MidiTime oldLength() const + inline TimePos oldLength() const { return m_oldLength; } @@ -153,23 +153,23 @@ class LMMS_EXPORT Note : public SerializingObject return m_isPlaying; } - inline MidiTime endPos() const + inline TimePos endPos() const { const int l = length(); return pos() + l; } - inline const MidiTime & length() const + inline const TimePos & length() const { return m_length; } - inline const MidiTime & pos() const + inline const TimePos & pos() const { return m_pos; } - inline MidiTime pos( MidiTime basePos ) const + inline TimePos pos( TimePos basePos ) const { const int bp = basePos; return m_pos - bp; @@ -205,7 +205,7 @@ class LMMS_EXPORT Note : public SerializingObject return classNodeName(); } - static MidiTime quantized( const MidiTime & m, const int qGrid ); + static TimePos quantized( const TimePos & m, const int qGrid ); DetuningHelper * detuning() const { @@ -226,15 +226,15 @@ class LMMS_EXPORT Note : public SerializingObject // for piano roll editing bool m_selected; int m_oldKey; - MidiTime m_oldPos; - MidiTime m_oldLength; + TimePos m_oldPos; + TimePos m_oldLength; bool m_isPlaying; int m_key; volume_t m_volume; panning_t m_panning; - MidiTime m_length; - MidiTime m_pos; + TimePos m_length; + TimePos m_pos; DetuningHelper * m_detuning; }; diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 71866d13b46..de324729afa 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -244,19 +244,19 @@ class LMMS_EXPORT NotePlayHandle : public PlayHandle, public Note } /*! Process note detuning automation */ - void processMidiTime( const MidiTime& time ); + void processTimePos( const TimePos& time ); /*! Updates total length (m_frames) depending on a new tempo */ void resize( const bpm_t newTempo ); /*! Set song-global offset (relative to containing pattern) in order to properly perform the note detuning */ - void setSongGlobalParentOffset( const MidiTime& offset ) + void setSongGlobalParentOffset( const TimePos& offset ) { m_songGlobalParentOffset = offset; } /*! Returns song-global offset */ - const MidiTime& songGlobalParentOffset() const + const TimePos& songGlobalParentOffset() const { return m_songGlobalParentOffset; } @@ -323,7 +323,7 @@ class LMMS_EXPORT NotePlayHandle : public PlayHandle, public Note float m_unpitchedFrequency; BaseDetuning* m_baseDetuning; - MidiTime m_songGlobalParentOffset; + TimePos m_songGlobalParentOffset; int m_midiChannel; Origin m_origin; diff --git a/include/Pattern.h b/include/Pattern.h index 5192da9faf8..ab7c9423005 100644 --- a/include/Pattern.h +++ b/include/Pattern.h @@ -128,7 +128,7 @@ protected slots: private: - MidiTime beatPatternLength() const; + TimePos beatPatternLength() const; void setType( PatternTypes _new_pattern_type ); void checkType(); diff --git a/include/PianoRoll.h b/include/PianoRoll.h index a876e03a013..5e0ea0762c9 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -187,9 +187,9 @@ protected slots: void pasteNotes(); bool deleteSelectedNotes(); - void updatePosition(const MidiTime & t ); - void updatePositionAccompany(const MidiTime & t ); - void updatePositionStepRecording(const MidiTime & t ); + void updatePosition(const TimePos & t ); + void updatePositionAccompany(const TimePos & t ); + void updatePositionStepRecording(const TimePos & t ); void zoomingChanged(); void zoomingYChanged(); @@ -266,9 +266,9 @@ protected slots: PianoRoll( const PianoRoll & ); virtual ~PianoRoll(); - void autoScroll(const MidiTime & t ); + void autoScroll(const TimePos & t ); - MidiTime newNoteLen() const; + TimePos newNoteLen() const; void shiftPos(int amount); void shiftPos(NoteVector notes, int amount); @@ -331,7 +331,7 @@ protected slots: QScrollBar * m_leftRightScroll; QScrollBar * m_topBottomScroll; - MidiTime m_currentPosition; + TimePos m_currentPosition; bool m_recording; QList m_recordingNotes; @@ -377,12 +377,12 @@ protected slots: // remember these values to use them // for the next note that is set - MidiTime m_lenOfNewNotes; + TimePos m_lenOfNewNotes; volume_t m_lastNoteVolume; panning_t m_lastNotePanning; //When resizing several notes, we want to calculate a common minimum length - MidiTime m_minResizeLen; + TimePos m_minResizeLen; int m_startKey; // first key when drawing int m_lastKey; @@ -447,7 +447,7 @@ protected slots: QBrush m_blackKeyInactiveBackground; signals: - void positionChanged( const MidiTime & ); + void positionChanged( const TimePos & ); } ; diff --git a/include/SampleRecordHandle.h b/include/SampleRecordHandle.h index fc40d062295..f2a6fd63be4 100644 --- a/include/SampleRecordHandle.h +++ b/include/SampleRecordHandle.h @@ -29,8 +29,8 @@ #include #include -#include "MidiTime.h" #include "PlayHandle.h" +#include "TimePos.h" class BBTrack; class SampleBuffer; @@ -60,7 +60,7 @@ class SampleRecordHandle : public PlayHandle typedef QList > bufferList; bufferList m_buffers; f_cnt_t m_framesRecorded; - MidiTime m_minLength; + TimePos m_minLength; Track * m_track; BBTrack * m_bbTrack; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index e944936b332..7a9c25ab2c0 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -50,7 +50,7 @@ class SampleTCO : public TrackContentObject SampleTCO( Track * _track ); virtual ~SampleTCO(); - void changeLength( const MidiTime & _length ) override; + void changeLength( const TimePos & _length ) override; const QString & sampleFile() const; void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; @@ -65,7 +65,7 @@ class SampleTCO : public TrackContentObject return m_sampleBuffer; } - MidiTime sampleLength() const; + TimePos sampleLength() const; void setSampleStartFrame( f_cnt_t startFrame ); void setSamplePlayLength( f_cnt_t length ); TrackContentObjectView * createView( TrackView * _tv ) override; @@ -139,10 +139,10 @@ class SampleTrack : public Track SampleTrack( TrackContainer* tc ); virtual ~SampleTrack(); - virtual bool play( const MidiTime & _start, const fpp_t _frames, + virtual bool play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ) override; TrackView * createView( TrackContainerView* tcv ) override; - TrackContentObject* createTCO(const MidiTime & pos) override; + TrackContentObject* createTCO(const TimePos & pos) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, diff --git a/include/Song.h b/include/Song.h index eeee01274ae..e06658f4ced 100644 --- a/include/Song.h +++ b/include/Song.h @@ -81,11 +81,11 @@ class LMMS_EXPORT Song : public TrackContainer bool hasErrors(); QString errorSummary(); - class PlayPos : public MidiTime + class PlayPos : public TimePos { public: PlayPos( const int abs = 0 ) : - MidiTime( abs ), + TimePos( abs ), m_timeLine( NULL ), m_currentFrame( 0.0f ) { @@ -131,27 +131,27 @@ class LMMS_EXPORT Song : public TrackContainer return m_elapsedMilliSeconds[playMode]; } - inline void setToTime(MidiTime const & midiTime) + inline void setToTime(TimePos const & pos) { - m_elapsedMilliSeconds[m_playMode] = midiTime.getTimeInMilliseconds(getTempo()); - m_playPos[m_playMode].setTicks(midiTime.getTicks()); + m_elapsedMilliSeconds[m_playMode] = pos.getTimeInMilliseconds(getTempo()); + m_playPos[m_playMode].setTicks(pos.getTicks()); } - inline void setToTime(MidiTime const & midiTime, PlayModes playMode) + inline void setToTime(TimePos const & pos, PlayModes playMode) { - m_elapsedMilliSeconds[playMode] = midiTime.getTimeInMilliseconds(getTempo()); - m_playPos[playMode].setTicks(midiTime.getTicks()); + m_elapsedMilliSeconds[playMode] = pos.getTimeInMilliseconds(getTempo()); + m_playPos[playMode].setTicks(pos.getTicks()); } inline void setToTimeByTicks(tick_t ticks) { - m_elapsedMilliSeconds[m_playMode] = MidiTime::ticksToMilliseconds(ticks, getTempo()); + m_elapsedMilliSeconds[m_playMode] = TimePos::ticksToMilliseconds(ticks, getTempo()); m_playPos[m_playMode].setTicks(ticks); } inline void setToTimeByTicks(tick_t ticks, PlayModes playMode) { - m_elapsedMilliSeconds[playMode] = MidiTime::ticksToMilliseconds(ticks, getTempo()); + m_elapsedMilliSeconds[playMode] = TimePos::ticksToMilliseconds(ticks, getTempo()); m_playPos[playMode].setTicks(ticks); } @@ -162,7 +162,7 @@ class LMMS_EXPORT Song : public TrackContainer inline int ticksPerBar() const { - return MidiTime::ticksPerBar(m_timeSigModel); + return TimePos::ticksPerBar(m_timeSigModel); } // Returns the beat position inside the bar, 0-based @@ -273,7 +273,7 @@ class LMMS_EXPORT Song : public TrackContainer } //TODO: Add Q_DECL_OVERRIDE when Qt4 is dropped - AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const override; + AutomatedValueMap automatedValuesAt(TimePos time, int tcoNum = -1) const override; // file management void createNewProject(); @@ -409,7 +409,7 @@ private slots: void removeAllControllers(); - void processAutomations(const TrackList& tracks, MidiTime timeStart, fpp_t frames); + void processAutomations(const TrackList& tracks, TimePos timeStart, fpp_t frames); void setModified(bool value); @@ -462,11 +462,11 @@ private slots: int m_loopRenderCount; int m_loopRenderRemaining; - MidiTime m_exportSongBegin; - MidiTime m_exportLoopBegin; - MidiTime m_exportLoopEnd; - MidiTime m_exportSongEnd; - MidiTime m_exportEffectiveLength; + TimePos m_exportSongBegin; + TimePos m_exportLoopBegin; + TimePos m_exportLoopEnd; + TimePos m_exportSongEnd; + TimePos m_exportEffectiveLength; friend class LmmsCore; friend class SongEditor; diff --git a/include/SongEditor.h b/include/SongEditor.h index a4394fc569c..8d279c594a5 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -80,7 +80,7 @@ public slots: void setEditModeSelect(); void toggleProportionalSnap(); - void updatePosition( const MidiTime & t ); + void updatePosition( const TimePos & t ); void updatePositionLine(); void selectAllTcos( bool select ); @@ -150,7 +150,7 @@ private slots: QPoint m_scrollPos; QPoint m_mousePos; int m_rubberBandStartTrackview; - MidiTime m_rubberbandStartMidipos; + TimePos m_rubberbandStartTimePos; int m_currentZoomingValue; int m_trackHeadWidth; bool m_selectRegion; diff --git a/include/StepRecorder.h b/include/StepRecorder.h index 8240cc41a61..13b3577a86c 100644 --- a/include/StepRecorder.h +++ b/include/StepRecorder.h @@ -41,14 +41,14 @@ class StepRecorder : public QObject StepRecorder(PianoRoll& pianoRoll, StepRecorderWidget& stepRecorderWidget); void initialize(); - void start(const MidiTime& currentPosition,const MidiTime& stepLength); + void start(const TimePos& currentPosition,const TimePos& stepLength); void stop(); void notePressed(const Note & n); void noteReleased(const Note & n); bool keyPressEvent(QKeyEvent* ke); bool mousePressEvent(QMouseEvent* ke); void setCurrentPattern(Pattern* newPattern); - void setStepsLength(const MidiTime& newLength); + void setStepsLength(const TimePos& newLength); QVector getCurStepNotes(); @@ -73,7 +73,7 @@ class StepRecorder : public QObject void dismissStep(); void prepareNewStep(); - MidiTime getCurStepEndPos(); + TimePos getCurStepEndPos(); void updateCurStepNotes(); void updateWidget(); @@ -84,11 +84,11 @@ class StepRecorder : public QObject StepRecorderWidget& m_stepRecorderWidget; bool m_isRecording = false; - MidiTime m_curStepStartPos = 0; - MidiTime m_curStepEndPos = 0; + TimePos m_curStepStartPos = 0; + TimePos m_curStepEndPos = 0; - MidiTime m_stepsLength; - MidiTime m_curStepLength; // current step length refers to the step currently recorded. it may defer from m_stepsLength + TimePos m_stepsLength; + TimePos m_curStepLength; // current step length refers to the step currently recorded. it may defer from m_stepsLength // since the user can make current step larger QTimer m_updateReleasedTimer; diff --git a/include/StepRecorderWidget.h b/include/StepRecorderWidget.h index 67f2a4b6547..dfaeaed0aa6 100644 --- a/include/StepRecorderWidget.h +++ b/include/StepRecorderWidget.h @@ -44,15 +44,15 @@ class StepRecorderWidget : public QWidget //API used by PianoRoll void setPixelsPerBar(int ppb); - void setCurrentPosition(MidiTime currentPosition); + void setCurrentPosition(TimePos currentPosition); void setMargins(const QMargins &qm); void setBottomMargin(const int marginBottom); QMargins margins(); //API used by StepRecorder - void setStepsLength(MidiTime stepsLength); - void setStartPosition(MidiTime pos); - void setEndPosition(MidiTime pos); + void setStepsLength(TimePos stepsLength); + void setStartPosition(TimePos pos); + void setEndPosition(TimePos pos); void showHint(); @@ -62,16 +62,16 @@ class StepRecorderWidget : public QWidget int xCoordOfTick(int tick); void drawVerLine(QPainter* painter, int x, const QColor& color, int top, int bottom); - void drawVerLine(QPainter* painter, const MidiTime& pos, const QColor& color, int top, int bottom); + void drawVerLine(QPainter* painter, const TimePos& pos, const QColor& color, int top, int bottom); void updateBoundaries(); - MidiTime m_stepsLength; - MidiTime m_curStepStartPos; - MidiTime m_curStepEndPos; + TimePos m_stepsLength; + TimePos m_curStepStartPos; + TimePos m_curStepEndPos; int m_ppb; // pixels per bar - MidiTime m_currentPosition; // current position showed by on PianoRoll + TimePos m_currentPosition; // current position showed by on PianoRoll QColor m_colorLineStart; QColor m_colorLineEnd; @@ -88,7 +88,7 @@ class StepRecorderWidget : public QWidget const int m_marginRight; signals: - void positionChanged(const MidiTime & t); + void positionChanged(const TimePos & t); } ; #endif //STEP_RECOREDER_WIDGET_H diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 8a9dc5044ea..c7ac0124c9e 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -73,7 +73,7 @@ class TimeLineWidget : public QWidget, public JournallingObject TimeLineWidget(int xoff, int yoff, float ppb, Song::PlayPos & pos, - const MidiTime & begin, Song::PlayModes mode, QWidget * parent); + const TimePos & begin, Song::PlayModes mode, QWidget * parent); virtual ~TimeLineWidget(); inline QColor const & getBarLineColor() const { return m_barLineColor; } @@ -123,23 +123,23 @@ class TimeLineWidget : public QWidget, public JournallingObject return m_loopPoints == LoopPointsEnabled; } - inline const MidiTime & loopBegin() const + inline const TimePos & loopBegin() const { return ( m_loopPos[0] < m_loopPos[1] ) ? m_loopPos[0] : m_loopPos[1]; } - inline const MidiTime & loopEnd() const + inline const TimePos & loopEnd() const { return ( m_loopPos[0] > m_loopPos[1] ) ? m_loopPos[0] : m_loopPos[1]; } - inline void savePos( const MidiTime & pos ) + inline void savePos( const TimePos & pos ) { m_savedPos = pos; } - inline const MidiTime & savedPos() const + inline const TimePos & savedPos() const { return m_savedPos; } @@ -162,10 +162,10 @@ class TimeLineWidget : public QWidget, public JournallingObject return "timeline"; } - inline int markerX( const MidiTime & _t ) const + inline int markerX( const TimePos & _t ) const { return m_xOffset + static_cast( ( _t - m_begin ) * - m_ppb / MidiTime::ticksPerBar() ); + m_ppb / TimePos::ticksPerBar() ); } signals: @@ -175,10 +175,10 @@ class TimeLineWidget : public QWidget, public JournallingObject public slots: - void updatePosition( const MidiTime & ); + void updatePosition( const TimePos & ); void updatePosition() { - updatePosition( MidiTime() ); + updatePosition( TimePos() ); } void toggleAutoScroll( int _n ); void toggleLoopPoints( int _n ); @@ -218,11 +218,11 @@ public slots: int m_posMarkerX; float m_ppb; Song::PlayPos & m_pos; - const MidiTime & m_begin; + const TimePos & m_begin; const Song::PlayModes m_mode; - MidiTime m_loopPos[2]; + TimePos m_loopPos[2]; - MidiTime m_savedPos; + TimePos m_savedPos; TextFloat * m_hint; @@ -242,7 +242,7 @@ public slots: signals: - void positionChanged( const MidiTime & _t ); + void positionChanged( const TimePos & _t ); void loopPointStateLoaded( int _n ); void positionMarkerMoved(); void loadBehaviourAtStop( int _n ); diff --git a/include/MidiTime.h b/include/TimePos.h similarity index 74% rename from include/MidiTime.h rename to include/TimePos.h index 952b4b6d596..675416c4299 100644 --- a/include/MidiTime.h +++ b/include/TimePos.h @@ -1,6 +1,6 @@ /* - * MidiTime.h - declaration of class MidiTime which provides data type for - * position- and length-variables + * TimePos.h - declaration of class TimePos which provides data type for + * position- and length-variables * * Copyright (c) 2004-2014 Tobias Doerffel @@ -40,13 +40,15 @@ const int DefaultBeatsPerBar = DefaultTicksPerBar / DefaultStepsPerBar; class MeterModel; +/** + Represents a time signature, in which the numerator is the number of beats + in a bar, while the denominator is the type of note representing a beat. + + Example: 6/8 means 6 beats in a bar with each beat having a duration of one 8th-note. +*/ class LMMS_EXPORT TimeSig { public: - // in a time signature, - // the numerator represents the number of beats in a measure. - // the denominator indicates which type of note represents a beat. - // example: 6/8 means 6 beats in a measure, where each beat has duration equal to one 8th-note. TimeSig( int num, int denom ); TimeSig( const MeterModel &model ); int numerator() const; @@ -57,17 +59,20 @@ class LMMS_EXPORT TimeSig }; -class LMMS_EXPORT MidiTime +/** + Represents a position in time or length of a note or event, in ticks, beats, and bars +*/ +class LMMS_EXPORT TimePos { public: - MidiTime( const bar_t bar, const tick_t ticks ); - MidiTime( const tick_t ticks = 0 ); + TimePos( const bar_t bar, const tick_t ticks ); + TimePos( const tick_t ticks = 0 ); - MidiTime quantize(float) const; - MidiTime toAbsoluteBar() const; + TimePos quantize(float) const; + TimePos toAbsoluteBar() const; - MidiTime& operator+=( const MidiTime& time ); - MidiTime& operator-=( const MidiTime& time ); + TimePos& operator+=( const TimePos& time ); + TimePos& operator-=( const TimePos& time ); // return the bar, rounded down and 0-based bar_t getBar() const; @@ -92,12 +97,12 @@ class LMMS_EXPORT MidiTime double getTimeInMilliseconds( bpm_t beatsPerMinute ) const; - static MidiTime fromFrames( const f_cnt_t frames, const float framesPerTick ); + static TimePos fromFrames( const f_cnt_t frames, const float framesPerTick ); static tick_t ticksPerBar(); static tick_t ticksPerBar( const TimeSig &sig ); static int stepsPerBar(); static void setTicksPerBar( tick_t tpt ); - static MidiTime stepPosition( int step ); + static TimePos stepPosition( int step ); static double ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute ); static double ticksToMilliseconds( double ticks, bpm_t beatsPerMinute ); diff --git a/include/Track.h b/include/Track.h index dd6066986a2..9f609758c2c 100644 --- a/include/Track.h +++ b/include/Track.h @@ -34,7 +34,7 @@ #include #include "lmms_basics.h" -#include "MidiTime.h" +#include "TimePos.h" #include "Rubberband.h" #include "JournallingObject.h" #include "AutomatableModel.h" @@ -108,18 +108,18 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject } - inline const MidiTime & startPosition() const + inline const TimePos & startPosition() const { return m_startPosition; } - inline MidiTime endPosition() const + inline TimePos endPosition() const { const int sp = m_startPosition; return sp + m_length; } - inline const MidiTime & length() const + inline const TimePos & length() const { return m_length; } @@ -153,8 +153,8 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject return m_useCustomClipColor; } - virtual void movePosition( const MidiTime & pos ); - virtual void changeLength( const MidiTime & length ); + virtual void movePosition( const TimePos & pos ); + virtual void changeLength( const TimePos & length ); virtual TrackContentObjectView * createView( TrackView * tv ) = 0; @@ -171,8 +171,8 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject /// Returns true if and only if a->startPosition() < b->startPosition() static bool comparePosition(const TrackContentObject* a, const TrackContentObject* b); - MidiTime startTimeOffset() const; - void setStartTimeOffset( const MidiTime &startTimeOffset ); + TimePos startTimeOffset() const; + void setStartTimeOffset( const TimePos &startTimeOffset ); void updateColor(); @@ -201,9 +201,9 @@ public slots: Track * m_track; QString m_name; - MidiTime m_startPosition; - MidiTime m_length; - MidiTime m_startTimeOffset; + TimePos m_startPosition; + TimePos m_length; + TimePos m_startTimeOffset; BoolModel m_mutedModel; BoolModel m_soloModel; @@ -360,9 +360,9 @@ protected slots: Actions m_action; QPoint m_initialMousePos; QPoint m_initialMouseGlobalPos; - MidiTime m_initialTCOPos; - MidiTime m_initialTCOEnd; - QVector m_initialOffsets; + TimePos m_initialTCOPos; + TimePos m_initialTCOEnd; + QVector m_initialOffsets; TextFloat * m_hint; @@ -389,7 +389,7 @@ protected slots: void setInitialOffsets(); bool mouseMovedDistance( QMouseEvent * me, int distance ); - MidiTime draggedTCOPos( QMouseEvent * me ); + TimePos draggedTCOPos( QMouseEvent * me ); } ; @@ -423,12 +423,12 @@ class TrackContentWidget : public QWidget, public JournallingObject } } - bool canPasteSelection( MidiTime tcoPos, const QDropEvent *de ); - bool canPasteSelection( MidiTime tcoPos, const QMimeData *md, bool allowSameBar = false ); - bool pasteSelection( MidiTime tcoPos, QDropEvent * de ); - bool pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck = false ); + bool canPasteSelection( TimePos tcoPos, const QDropEvent *de ); + bool canPasteSelection( TimePos tcoPos, const QMimeData *md, bool allowSameBar = false ); + bool pasteSelection( TimePos tcoPos, QDropEvent * de ); + bool pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck = false ); - MidiTime endPosition( const MidiTime & posStart ); + TimePos endPosition( const TimePos & posStart ); // qproperty access methods @@ -444,7 +444,7 @@ class TrackContentWidget : public QWidget, public JournallingObject public slots: void update(); - void changePosition( const MidiTime & newPos = MidiTime( -1 ) ); + void changePosition( const TimePos & newPos = TimePos( -1 ) ); protected: enum ContextMenuAction @@ -479,7 +479,7 @@ public slots: private: Track * getTrack(); - MidiTime getPosition( int mouseX ); + TimePos getPosition( int mouseX ); TrackView * m_trackView; @@ -584,12 +584,12 @@ class LMMS_EXPORT Track : public Model, public JournallingObject return m_type; } - virtual bool play( const MidiTime & start, const fpp_t frames, + virtual bool play( const TimePos & start, const fpp_t frames, const f_cnt_t frameBase, int tcoNum = -1 ) = 0; virtual TrackView * createView( TrackContainerView * view ) = 0; - virtual TrackContentObject * createTCO( const MidiTime & pos ) = 0; + virtual TrackContentObject * createTCO( const TimePos & pos ) = 0; virtual void saveTrackSpecificSettings( QDomDocument & doc, QDomElement & parent ) = 0; @@ -618,15 +618,15 @@ class LMMS_EXPORT Track : public Model, public JournallingObject { return m_trackContentObjects; } - void getTCOsInRange( tcoVector & tcoV, const MidiTime & start, - const MidiTime & end ); + void getTCOsInRange( tcoVector & tcoV, const TimePos & start, + const TimePos & end ); void swapPositionOfTCOs( int tcoNum1, int tcoNum2 ); void createTCOsForBB( int bb ); - void insertBar( const MidiTime & pos ); - void removeBar( const MidiTime & pos ); + void insertBar( const TimePos & pos ); + void removeBar( const TimePos & pos ); bar_t length() const; diff --git a/include/TrackContainer.h b/include/TrackContainer.h index af1e8187d49..00b391e102d 100644 --- a/include/TrackContainer.h +++ b/include/TrackContainer.h @@ -93,13 +93,13 @@ class LMMS_EXPORT TrackContainer : public Model, public JournallingObject return m_TrackContainerType; } - virtual AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const; + virtual AutomatedValueMap automatedValuesAt(TimePos time, int tcoNum = -1) const; signals: void trackAdded( Track * _track ); protected: - static AutomatedValueMap automatedValuesFromTracks(const TrackList &tracks, MidiTime timeStart, int tcoNum = -1); + static AutomatedValueMap automatedValuesFromTracks(const TrackList &tracks, TimePos timeStart, int tcoNum = -1); mutable QReadWriteLock m_tracksMutex; diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 6e952189b01..30e7ec0f6aa 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -57,7 +57,7 @@ class TrackContainerView : public QWidget, public ModelView, return m_scrollArea; } - inline const MidiTime & currentPosition() const + inline const TimePos & currentPosition() const { return m_currentPosition; } @@ -143,7 +143,7 @@ public slots: void resizeEvent( QResizeEvent * ) override; - MidiTime m_currentPosition; + TimePos m_currentPosition; private: @@ -182,7 +182,7 @@ public slots: signals: - void positionChanged( const MidiTime & _pos ); + void positionChanged( const TimePos & _pos ); } ; diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index d67e2549290..3eba7cdcc00 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -134,7 +134,7 @@ void Lv2Instrument::loadFile(const QString &file) #ifdef LV2_INSTRUMENT_USE_MIDI bool Lv2Instrument::handleMidiEvent( - const MidiEvent &event, const MidiTime &time, f_cnt_t offset) + const MidiEvent &event, const TimePos &time, f_cnt_t offset) { // this function can be called from GUI threads while the plugin is running // handleMidiInputEvent will use a thread-safe ringbuffer diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index a35830952f9..60575d42689 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -66,7 +66,7 @@ class Lv2Instrument : public Instrument, public Lv2ControlBase bool hasNoteInput() const override { return Lv2ControlBase::hasNoteInput(); } #ifdef LV2_INSTRUMENT_USE_MIDI bool handleMidiEvent(const MidiEvent &event, - const MidiTime &time = MidiTime(), f_cnt_t offset = 0) override; + const TimePos &time = TimePos(), f_cnt_t offset = 0) override; #else void playNote(NotePlayHandle *nph, sampleFrame *) override; #endif diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index a93e1e41e8d..97c81460424 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -43,7 +43,7 @@ #include "Instrument.h" #include "GuiApplication.h" #include "MainWindow.h" -#include "MidiTime.h" +#include "TimePos.h" #include "debug.h" #include "Song.h" @@ -160,7 +160,7 @@ class smfMidiCC AutomationTrack * at; AutomationPattern * ap; - MidiTime lastPos; + TimePos lastPos; smfMidiCC & create( TrackContainer* tc, QString tn ) { @@ -187,11 +187,11 @@ class smfMidiCC } - smfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value ) + smfMidiCC & putValue( TimePos time, AutomatableModel * objModel, float value ) { if( !ap || time > lastPos + DefaultTicksPerBar ) { - MidiTime pPos = MidiTime( time.getBar(), 0 ); + TimePos pPos = TimePos( time.getBar(), 0 ); ap = dynamic_cast( at->createTCO(pPos)); ap->addObject( objModel ); @@ -200,7 +200,7 @@ class smfMidiCC lastPos = time; time = time - ap->startPosition(); ap->putValue( time, value, false ); - ap->changeLength( MidiTime( time.getBar() + 1, 0 ) ); + ap->changeLength( TimePos( time.getBar() + 1, 0 ) ); return *this; } @@ -278,14 +278,14 @@ class smfMidiChannel void splitPatterns() { Pattern * newPattern = nullptr; - MidiTime lastEnd(0); + TimePos lastEnd(0); p->rearrangeAllNotes(); for (auto n : p->notes()) { if (!newPattern || n->pos() > lastEnd + DefaultTicksPerBar) { - MidiTime pPos = MidiTime(n->pos().getBar(), 0); + TimePos pPos = TimePos(n->pos().getBar(), 0); newPattern = dynamic_cast(it->createTCO(pPos)); } lastEnd = n->pos() + n->length(); diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 8ab635b4f92..de3cce9ce56 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -293,7 +293,7 @@ int OpulenzInstrument::pushVoice(int v) { return i; } -bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) +bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { emulatorMutex.lock(); int key, vel, voice, tmp_pb; diff --git a/plugins/OpulenZ/OpulenZ.h b/plugins/OpulenZ/OpulenZ.h index 2273b355aff..1776df93fa9 100644 --- a/plugins/OpulenZ/OpulenZ.h +++ b/plugins/OpulenZ/OpulenZ.h @@ -56,7 +56,7 @@ class OpulenzInstrument : public Instrument return IsSingleStreamed | IsMidiBased; } - virtual bool handleMidiEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 ); + virtual bool handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ); virtual void play( sampleFrame * _working_buffer ); void saveSettings( QDomDocument & _doc, QDomElement & _this ); diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index a01ee625afe..728a6d804af 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -355,7 +355,7 @@ void CarlaInstrument::play(sampleFrame* workingBuffer) instrumentTrack()->processAudioBuffer(workingBuffer, bufsize, NULL); } -bool CarlaInstrument::handleMidiEvent(const MidiEvent& event, const MidiTime&, f_cnt_t offset) +bool CarlaInstrument::handleMidiEvent(const MidiEvent& event, const TimePos&, f_cnt_t offset) { const QMutexLocker ml(&fMutex); diff --git a/plugins/carlabase/carla.h b/plugins/carlabase/carla.h index 2c683add935..7efecf681d6 100644 --- a/plugins/carlabase/carla.h +++ b/plugins/carlabase/carla.h @@ -72,7 +72,7 @@ class CARLABASE_EXPORT CarlaInstrument : public Instrument virtual void saveSettings(QDomDocument& doc, QDomElement& parent); virtual void loadSettings(const QDomElement& elem); virtual void play(sampleFrame* workingBuffer); - virtual bool handleMidiEvent(const MidiEvent& event, const MidiTime& time, f_cnt_t offset); + virtual bool handleMidiEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset); virtual PluginView* instantiateView(QWidget* parent); signals: diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 08d418836c2..1cd58eef405 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -48,7 +48,6 @@ float frnd(float range) #include "ToolTip.h" #include "Song.h" #include "MidiEvent.h" -#include "MidiTime.h" #include "Mixer.h" #include "embed.h" diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index b2c15859464..d76ac37f6e5 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -411,7 +411,7 @@ void vestigeInstrument::play( sampleFrame * _buf ) -bool vestigeInstrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) +bool vestigeInstrument::handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { m_pluginMutex.lock(); if( m_plugin != NULL ) diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index ae4f803cf14..48c89f14d4c 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -67,7 +67,7 @@ class vestigeInstrument : public Instrument return IsSingleStreamed | IsMidiBased; } - virtual bool handleMidiEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 ); + virtual bool handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ); virtual PluginView * instantiateView( QWidget * _parent ); diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 8446d36f65b..94f480ce0c5 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -351,7 +351,7 @@ void ZynAddSubFxInstrument::play( sampleFrame * _buf ) -bool ZynAddSubFxInstrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) +bool ZynAddSubFxInstrument::handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { // do not forward external MIDI Control Change events if the according // LED is not checked diff --git a/plugins/zynaddsubfx/ZynAddSubFx.h b/plugins/zynaddsubfx/ZynAddSubFx.h index 6f5bc754d1c..10243e3731e 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.h +++ b/plugins/zynaddsubfx/ZynAddSubFx.h @@ -70,7 +70,7 @@ class ZynAddSubFxInstrument : public Instrument virtual void play( sampleFrame * _working_buffer ); - virtual bool handleMidiEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ); + virtual bool handleMidiEvent( const MidiEvent& event, const TimePos& time = TimePos(), f_cnt_t offset = 0 ); virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); virtual void loadSettings( const QDomElement & _this ); diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 8328e5cb9ea..f66706a3b94 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -708,7 +708,7 @@ void AutomatableModel::reset() -float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) +float AutomatableModel::globalAutomationValueAt( const TimePos& time ) { // get patterns that connect to this model QVector patterns = AutomationPattern::patternsForModel( this ); @@ -720,7 +720,7 @@ float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) else { // of those patterns: - // find the patterns which overlap with the miditime position + // find the patterns which overlap with the time position QVector patternsInRange; for( QVector::ConstIterator it = patterns.begin(); it != patterns.end(); it++ ) { @@ -738,7 +738,7 @@ float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) latestPattern = patternsInRange[0]; } else - // if we find no patterns at the exact miditime, we need to search for the last pattern before time and use that + // if we find no patterns at the exact time, we need to search for the last pattern before time and use that { int latestPosition = 0; diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 8886d7ea5e6..3c6aa3e9c01 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -51,7 +51,7 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : m_isRecording( false ), m_lastRecordedValue( 0 ) { - changeLength( MidiTime( 1, 0 ) ); + changeLength( TimePos( 1, 0 ) ); if( getTrack() ) { switch( getTrack()->trackContainer()->type() ) @@ -110,7 +110,7 @@ bool AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) if( m_objects.isEmpty() && hasAutomation() == false ) { // then initialize first value - putValue( MidiTime(0), _obj->inverseScaledValue( _obj->value() ), false ); + putValue( TimePos(0), _obj->inverseScaledValue( _obj->value() ), false ); } m_objects += _obj; @@ -176,9 +176,9 @@ const AutomationPattern::objectVector& AutomationPattern::objects() const -MidiTime AutomationPattern::timeMapLength() const +TimePos AutomationPattern::timeMapLength() const { - MidiTime one_bar = MidiTime(1, 0); + TimePos one_bar = TimePos(1, 0); if (m_timeMap.isEmpty()) { return one_bar; } timeMap::const_iterator it = m_timeMap.end(); @@ -187,7 +187,7 @@ MidiTime AutomationPattern::timeMapLength() const // return length as a whole bar to prevent disappearing TCO if (last_tick == 0) { return one_bar; } - return MidiTime(last_tick); + return TimePos(last_tick); } @@ -202,14 +202,14 @@ void AutomationPattern::updateLength() -MidiTime AutomationPattern::putValue( const MidiTime & time, +TimePos AutomationPattern::putValue( const TimePos & time, const float value, const bool quantPos, const bool ignoreSurroundingPoints ) { cleanObjects(); - MidiTime newTime = quantPos ? + TimePos newTime = quantPos ? Note::quantized( time, quantization() ) : time; @@ -241,7 +241,7 @@ MidiTime AutomationPattern::putValue( const MidiTime & time, -void AutomationPattern::removeValue( const MidiTime & time ) +void AutomationPattern::removeValue( const TimePos & time ) { cleanObjects(); @@ -261,7 +261,7 @@ void AutomationPattern::removeValue( const MidiTime & time ) -void AutomationPattern::recordValue(MidiTime time, float value) +void AutomationPattern::recordValue(TimePos time, float value) { if( value != m_lastRecordedValue ) { @@ -286,14 +286,14 @@ void AutomationPattern::recordValue(MidiTime time, float value) * @param true to snip x position * @return */ -MidiTime AutomationPattern::setDragValue( const MidiTime & time, +TimePos AutomationPattern::setDragValue( const TimePos & time, const float value, const bool quantPos, const bool controlKey ) { if( m_dragging == false ) { - MidiTime newTime = quantPos ? + TimePos newTime = quantPos ? Note::quantized( time, quantization() ) : time; this->removeValue( newTime ); @@ -327,7 +327,7 @@ void AutomationPattern::applyDragValue() -float AutomationPattern::valueAt( const MidiTime & _time ) const +float AutomationPattern::valueAt( const TimePos & _time ) const { if( m_timeMap.isEmpty() ) { @@ -395,7 +395,7 @@ float AutomationPattern::valueAt( timeMap::const_iterator v, int offset ) const -float *AutomationPattern::valuesAfter( const MidiTime & _time ) const +float *AutomationPattern::valuesAfter( const TimePos & _time ) const { timeMap::ConstIterator v = m_timeMap.lowerBound( _time ); if( v == m_timeMap.end() || (v+1) == m_timeMap.end() ) @@ -436,12 +436,12 @@ void AutomationPattern::flipY( int min, int max ) if ( min < 0 ) { tempValue = valueAt( ( iterate + i ).key() ) * -1; - putValue( MidiTime( (iterate + i).key() ) , tempValue, false); + putValue( TimePos( (iterate + i).key() ) , tempValue, false); } else { tempValue = max - valueAt( ( iterate + i ).key() ); - putValue( MidiTime( (iterate + i).key() ) , tempValue, false); + putValue( TimePos( (iterate + i).key() ) , tempValue, false); } } @@ -480,12 +480,12 @@ void AutomationPattern::flipX( int length ) if ( realLength < length ) { tempValue = valueAt( ( iterate + numPoints ).key() ); - putValue( MidiTime( length ) , tempValue, false); + putValue( TimePos( length ) , tempValue, false); numPoints++; for( int i = 0; i <= numPoints; i++ ) { tempValue = valueAt( ( iterate + i ).key() ); - MidiTime newTime = MidiTime( length - ( iterate + i ).key() ); + TimePos newTime = TimePos( length - ( iterate + i ).key() ); tempMap[newTime] = tempValue; } } @@ -494,15 +494,15 @@ void AutomationPattern::flipX( int length ) for( int i = 0; i <= numPoints; i++ ) { tempValue = valueAt( ( iterate + i ).key() ); - MidiTime newTime; + TimePos newTime; if ( ( iterate + i ).key() <= length ) { - newTime = MidiTime( length - ( iterate + i ).key() ); + newTime = TimePos( length - ( iterate + i ).key() ); } else { - newTime = MidiTime( ( iterate + i ).key() ); + newTime = TimePos( ( iterate + i ).key() ); } tempMap[newTime] = tempValue; } @@ -514,7 +514,7 @@ void AutomationPattern::flipX( int length ) { tempValue = valueAt( ( iterate + i ).key() ); cleanObjects(); - MidiTime newTime = MidiTime( realLength - ( iterate + i ).key() ); + TimePos newTime = TimePos( realLength - ( iterate + i ).key() ); tempMap[newTime] = tempValue; } } diff --git a/src/core/BBTrackContainer.cpp b/src/core/BBTrackContainer.cpp index ac4b6cb1a2f..a374ad29af6 100644 --- a/src/core/BBTrackContainer.cpp +++ b/src/core/BBTrackContainer.cpp @@ -53,7 +53,7 @@ BBTrackContainer::~BBTrackContainer() -bool BBTrackContainer::play( MidiTime _start, fpp_t _frames, +bool BBTrackContainer::play( TimePos _start, fpp_t _frames, f_cnt_t _offset, int _tco_num ) { bool played_a_note = false; @@ -62,7 +62,7 @@ bool BBTrackContainer::play( MidiTime _start, fpp_t _frames, return false; } - _start = _start % ( lengthOfBB( _tco_num ) * MidiTime::ticksPerBar() ); + _start = _start % ( lengthOfBB( _tco_num ) * TimePos::ticksPerBar() ); TrackList tl = tracks(); for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) @@ -92,7 +92,7 @@ void BBTrackContainer::updateAfterTrackAdd() bar_t BBTrackContainer::lengthOfBB( int _bb ) const { - MidiTime max_length = MidiTime::ticksPerBar(); + TimePos max_length = TimePos::ticksPerBar(); const TrackList & tl = tracks(); for (Track* t : tl) @@ -168,7 +168,7 @@ void BBTrackContainer::fixIncorrectPositions() { for( int i = 0; i < numOfBBs(); ++i ) { - ( *it )->getTCO( i )->movePosition( MidiTime( i, 0 ) ); + ( *it )->getTCO( i )->movePosition( TimePos( i, 0 ) ); } } } @@ -242,18 +242,18 @@ void BBTrackContainer::createTCOsForBB( int _bb ) } } -AutomatedValueMap BBTrackContainer::automatedValuesAt(MidiTime time, int tcoNum) const +AutomatedValueMap BBTrackContainer::automatedValuesAt(TimePos time, int tcoNum) const { Q_ASSERT(tcoNum >= 0); Q_ASSERT(time.getTicks() >= 0); auto length_bars = lengthOfBB(tcoNum); - auto length_ticks = length_bars * MidiTime::ticksPerBar(); + auto length_ticks = length_bars * TimePos::ticksPerBar(); if (time > length_ticks) { time = length_ticks; } - return TrackContainer::automatedValuesAt(time + (MidiTime::ticksPerBar() * tcoNum), tcoNum); + return TrackContainer::automatedValuesAt(time + (TimePos::ticksPerBar() * tcoNum), tcoNum); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 22bb1195103..7b38c6c39da 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -67,6 +67,7 @@ set(LMMS_SRCS core/SerializingObject.cpp core/Song.cpp core/TempoSyncKnobModel.cpp + core/TimePos.cpp core/ToolPlugin.cpp core/Track.cpp core/TrackContainer.cpp @@ -112,7 +113,6 @@ set(LMMS_SRCS core/midi/MidiSndio.cpp core/midi/MidiApple.cpp core/midi/MidiPort.cpp - core/midi/MidiTime.cpp core/midi/MidiWinMM.cpp PARENT_SCOPE diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 42ad3f0a3e0..09854220d7b 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -506,7 +506,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) NotePlayHandleManager::acquire( _n->instrumentTrack(), frames_processed, gated_frames, - Note( MidiTime( 0 ), MidiTime( 0 ), sub_note_key, _n->getVolume(), + Note( TimePos( 0 ), TimePos( 0 ), sub_note_key, _n->getVolume(), _n->getPanning(), _n->detuning() ), _n, -1, NotePlayHandle::OriginArpeggio ) ); diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 1a317e21f08..573951ea966 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -361,7 +361,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer() // Stop crash with metronome if empty project Engine::getSong()->countTracks() ) { - tick_t ticksPerBar = MidiTime::ticksPerBar(); + tick_t ticksPerBar = TimePos::ticksPerBar(); if ( p.getTicks() % ( ticksPerBar / 1 ) == 0 ) { addPlayHandle( new SamplePlayHandle( "misc/metronome02.ogg" ) ); diff --git a/src/core/Note.cpp b/src/core/Note.cpp index 389526bb9e0..080a555b5d0 100644 --- a/src/core/Note.cpp +++ b/src/core/Note.cpp @@ -31,7 +31,7 @@ #include "DetuningHelper.h" -Note::Note( const MidiTime & length, const MidiTime & pos, +Note::Note( const TimePos & length, const TimePos & pos, int key, volume_t volume, panning_t panning, DetuningHelper * detuning ) : m_selected( false ), @@ -93,7 +93,7 @@ Note::~Note() -void Note::setLength( const MidiTime & length ) +void Note::setLength( const TimePos & length ) { m_length = length; } @@ -101,7 +101,7 @@ void Note::setLength( const MidiTime & length ) -void Note::setPos( const MidiTime & pos ) +void Note::setPos( const TimePos & pos ) { m_pos = pos; } @@ -136,7 +136,7 @@ void Note::setPanning( panning_t panning ) -MidiTime Note::quantized( const MidiTime & m, const int qGrid ) +TimePos Note::quantized( const TimePos & m, const int qGrid ) { float p = ( (float) m / qGrid ); if( p - floorf( p ) < 0.5f ) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index d540792c048..a80b8f0c822 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -211,7 +211,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) // send MidiNoteOn event m_instrumentTrack->processOutEvent( MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ), - MidiTime::fromFrames( offset(), Engine::framesPerTick() ), + TimePos::fromFrames( offset(), Engine::framesPerTick() ), offset() ); } @@ -374,7 +374,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) // send MidiNoteOff event m_instrumentTrack->processOutEvent( MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ), - MidiTime::fromFrames( _s, Engine::framesPerTick() ), + TimePos::fromFrames( _s, Engine::framesPerTick() ), _s ); } @@ -383,7 +383,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) { if( m_origin == OriginMidiInput ) { - setLength( MidiTime( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); + setLength( TimePos( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); m_instrumentTrack->midiNoteOff( *this ); } } @@ -519,7 +519,7 @@ void NotePlayHandle::updateFrequency() -void NotePlayHandle::processMidiTime( const MidiTime& time ) +void NotePlayHandle::processTimePos( const TimePos& time ) { if( detuning() && time >= songGlobalParentOffset()+pos() ) { diff --git a/src/core/SampleRecordHandle.cpp b/src/core/SampleRecordHandle.cpp index cf9342b9e08..c8f688a969a 100644 --- a/src/core/SampleRecordHandle.cpp +++ b/src/core/SampleRecordHandle.cpp @@ -73,7 +73,7 @@ void SampleRecordHandle::play( sampleFrame * /*_working_buffer*/ ) writeBuffer( recbuf, frames ); m_framesRecorded += frames; - MidiTime len = (tick_t)( m_framesRecorded / Engine::framesPerTick() ); + TimePos len = (tick_t)( m_framesRecorded / Engine::framesPerTick() ); if( len > m_minLength ) { // m_tco->changeLength( len ); diff --git a/src/core/Song.cpp b/src/core/Song.cpp index bb2e937d086..0ae0cc887a8 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -57,7 +57,7 @@ #include "PeakController.h" -tick_t MidiTime::s_ticksPerBar = DefaultTicksPerBar; +tick_t TimePos::s_ticksPerBar = DefaultTicksPerBar; @@ -166,7 +166,7 @@ void Song::setTempo() void Song::setTimeSignature() { - MidiTime::setTicksPerBar( ticksPerBar() ); + TimePos::setTicksPerBar( ticksPerBar() ); emit timeSignatureChanged( m_oldTicksPerBar, ticksPerBar() ); emit dataChanged(); m_oldTicksPerBar = ticksPerBar(); @@ -240,7 +240,7 @@ void Song::processNextBuffer() // If the playback position is outside of the range [begin, end), move it to // begin and inform interested parties. // Returns true if the playback position was moved, else false. - const auto enforceLoop = [this](const MidiTime& begin, const MidiTime& end) + const auto enforceLoop = [this](const TimePos& begin, const TimePos& end) { if (getPlayPos() < begin || getPlayPos() >= end) { @@ -287,11 +287,11 @@ void Song::processNextBuffer() // loop back to the beginning when we reach the end if (m_playMode == Mode_PlayBB) { - enforceLoop(MidiTime{0}, MidiTime{Engine::getBBTrackContainer()->lengthOfCurrentBB(), 0}); + enforceLoop(TimePos{0}, TimePos{Engine::getBBTrackContainer()->lengthOfCurrentBB(), 0}); } else if (m_playMode == Mode_PlayPattern && m_loopPattern && !loopEnabled) { - enforceLoop(MidiTime{0}, m_patternToPlay->length()); + enforceLoop(TimePos{0}, m_patternToPlay->length()); } // Handle loop points, and inform VST plugins of the loop status @@ -343,14 +343,14 @@ void Song::processNextBuffer() frameOffsetInPeriod += framesToPlay; frameOffsetInTick += framesToPlay; getPlayPos().setCurrentFrame(frameOffsetInTick); - m_elapsedMilliSeconds[m_playMode] += MidiTime::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); + m_elapsedMilliSeconds[m_playMode] += TimePos::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); m_elapsedBars = m_playPos[Mode_PlaySong].getBar(); m_elapsedTicks = (m_playPos[Mode_PlaySong].getTicks() % ticksPerBar()) / 48; } } -void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fpp_t) +void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp_t) { AutomatedValueMap values; @@ -392,7 +392,7 @@ void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fp for (TrackContentObject* tco : tcos) { auto p = dynamic_cast(tco); - MidiTime relTime = timeStart - p->startPosition(); + TimePos relTime = timeStart - p->startPosition(); if (p->isRecording() && relTime >= 0 && relTime < p->length()) { const AutomatableModel* recordedModel = p->firstObject(); @@ -428,7 +428,7 @@ bool Song::isExportDone() const int Song::getExportProgress() const { - MidiTime pos = m_playPos[m_playMode]; + TimePos pos = m_playPos[m_playMode]; if (pos >= m_exportSongEnd) { @@ -572,7 +572,7 @@ void Song::setPlayPos( tick_t ticks, PlayModes playMode ) { tick_t ticksFromPlayMode = m_playPos[playMode].getTicks(); m_elapsedTicks += ticksFromPlayMode - ticks; - m_elapsedMilliSeconds[playMode] += MidiTime::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() ); + m_elapsedMilliSeconds[playMode] += TimePos::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() ); m_playPos[playMode].setTicks( ticks ); m_playPos[playMode].setCurrentFrame( 0.0f ); m_playPos[playMode].setJumped( true ); @@ -690,7 +690,7 @@ void Song::startExport() } else { - m_exportSongEnd = MidiTime(m_length, 0); + m_exportSongEnd = TimePos(m_length, 0); // Handle potentially ridiculous loop points gracefully. if (m_loopRenderCount > 1 && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() > m_exportSongEnd) @@ -699,18 +699,18 @@ void Song::startExport() } if (!m_exportLoop) - m_exportSongEnd += MidiTime(1,0); + m_exportSongEnd += TimePos(1,0); - m_exportSongBegin = MidiTime(0,0); + m_exportSongBegin = TimePos(0,0); // FIXME: remove this check once we load timeline in headless mode if (m_playPos[Mode_PlaySong].m_timeLine) { m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? - m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : MidiTime(0,0); + m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : TimePos(0,0); m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : MidiTime(0,0); + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : TimePos(0,0); } m_playPos[Mode_PlaySong].setTicks( 0 ); @@ -806,7 +806,7 @@ AutomationPattern * Song::tempoAutomationPattern() } -AutomatedValueMap Song::automatedValuesAt(MidiTime time, int tcoNum) const +AutomatedValueMap Song::automatedValuesAt(TimePos time, int tcoNum) const { return TrackContainer::automatedValuesFromTracks(TrackList{m_globalAutomationTrack} << tracks(), time, tcoNum); } diff --git a/src/core/StepRecorder.cpp b/src/core/StepRecorder.cpp index e107c58181b..1413d505014 100644 --- a/src/core/StepRecorder.cpp +++ b/src/core/StepRecorder.cpp @@ -43,7 +43,7 @@ void StepRecorder::initialize() connect(&m_updateReleasedTimer, SIGNAL(timeout()), this, SLOT(removeNotesReleasedForTooLong())); } -void StepRecorder::start(const MidiTime& currentPosition, const MidiTime& stepLength) +void StepRecorder::start(const TimePos& currentPosition, const TimePos& stepLength) { m_isRecording = true; @@ -53,7 +53,7 @@ void StepRecorder::start(const MidiTime& currentPosition, const MidiTime& stepLe const int q = m_pianoRoll.quantization(); const int curPosTicks = currentPosition.getTicks(); const int QuantizedPosTicks = (curPosTicks / q) * q; - const MidiTime& QuantizedPos = MidiTime(QuantizedPosTicks); + const TimePos& QuantizedPos = TimePos(QuantizedPosTicks); m_curStepStartPos = QuantizedPos; m_curStepLength = 0; @@ -154,7 +154,7 @@ bool StepRecorder::keyPressEvent(QKeyEvent* ke) return event_handled; } -void StepRecorder::setStepsLength(const MidiTime& newLength) +void StepRecorder::setStepsLength(const TimePos& newLength) { if(m_isStepInProgress) { @@ -319,7 +319,7 @@ void StepRecorder::removeNotesReleasedForTooLong() } } -MidiTime StepRecorder::getCurStepEndPos() +TimePos StepRecorder::getCurStepEndPos() { return m_curStepStartPos + m_curStepLength; } diff --git a/src/core/midi/MidiTime.cpp b/src/core/TimePos.cpp similarity index 64% rename from src/core/midi/MidiTime.cpp rename to src/core/TimePos.cpp index 4e718a1d880..9d25b9ce136 100644 --- a/src/core/midi/MidiTime.cpp +++ b/src/core/TimePos.cpp @@ -1,5 +1,5 @@ /* - * MidiTime.cpp - Class that encapsulates the position of a note/event in terms of + * TimePos.cpp - Class that encapsulates the position of a note/event in terms of * its bar, beat and tick. * * Copyright (c) 2004-2014 Tobias Doerffel = 0 ) { @@ -160,53 +160,53 @@ f_cnt_t MidiTime::frames( const float framesPerTick ) const return 0; } -double MidiTime::getTimeInMilliseconds( bpm_t beatsPerMinute ) const +double TimePos::getTimeInMilliseconds( bpm_t beatsPerMinute ) const { return ticksToMilliseconds( getTicks(), beatsPerMinute ); } -MidiTime MidiTime::fromFrames( const f_cnt_t frames, const float framesPerTick ) +TimePos TimePos::fromFrames( const f_cnt_t frames, const float framesPerTick ) { - return MidiTime( static_cast( frames / framesPerTick ) ); + return TimePos( static_cast( frames / framesPerTick ) ); } -tick_t MidiTime::ticksPerBar() +tick_t TimePos::ticksPerBar() { return s_ticksPerBar; } -tick_t MidiTime::ticksPerBar( const TimeSig &sig ) +tick_t TimePos::ticksPerBar( const TimeSig &sig ) { return DefaultTicksPerBar * sig.numerator() / sig.denominator(); } -int MidiTime::stepsPerBar() +int TimePos::stepsPerBar() { int steps = ticksPerBar() / DefaultBeatsPerBar; return qMax( 1, steps ); } -void MidiTime::setTicksPerBar( tick_t tpb ) +void TimePos::setTicksPerBar( tick_t tpb ) { s_ticksPerBar = tpb; } -MidiTime MidiTime::stepPosition( int step ) +TimePos TimePos::stepPosition( int step ) { return step * ticksPerBar() / stepsPerBar(); } -double MidiTime::ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute ) +double TimePos::ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute ) { - return MidiTime::ticksToMilliseconds( static_cast(ticks), beatsPerMinute ); + return TimePos::ticksToMilliseconds( static_cast(ticks), beatsPerMinute ); } -double MidiTime::ticksToMilliseconds(double ticks, bpm_t beatsPerMinute) +double TimePos::ticksToMilliseconds(double ticks, bpm_t beatsPerMinute) { // 60 * 1000 / 48 = 1250 return ( ticks * 1250 ) / beatsPerMinute; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index c63e863bf9c..c2178d3be2c 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -149,9 +149,9 @@ TrackContentObject::~TrackContentObject() * * \param _pos The new position of the track content object. */ -void TrackContentObject::movePosition( const MidiTime & pos ) +void TrackContentObject::movePosition( const TimePos & pos ) { - MidiTime newPos = qMax(0, pos.getTicks()); + TimePos newPos = qMax(0, pos.getTicks()); if (m_startPosition != newPos) { Engine::mixer()->requestChangeInModel(); @@ -172,7 +172,7 @@ void TrackContentObject::movePosition( const MidiTime & pos ) * * \param _length The new length of the track content object. */ -void TrackContentObject::changeLength( const MidiTime & length ) +void TrackContentObject::changeLength( const TimePos & length ) { m_length = length; Engine::getSong()->updateLength(); @@ -202,7 +202,7 @@ void TrackContentObject::copyStateTo( TrackContentObject *src, TrackContentObjec QDomElement parent = doc.createElement( "StateCopy" ); src->saveState( doc, parent ); - const MidiTime pos = dst->startPosition(); + const TimePos pos = dst->startPosition(); dst->restoreState( parent.firstChild().toElement() ); dst->movePosition( pos ); @@ -231,7 +231,7 @@ void TrackContentObject::toggleMute() -MidiTime TrackContentObject::startTimeOffset() const +TimePos TrackContentObject::startTimeOffset() const { return m_startTimeOffset; } @@ -239,7 +239,7 @@ MidiTime TrackContentObject::startTimeOffset() const -void TrackContentObject::setStartTimeOffset( const MidiTime &startTimeOffset ) +void TrackContentObject::setStartTimeOffset( const TimePos &startTimeOffset ) { m_startTimeOffset = startTimeOffset; } @@ -290,9 +290,9 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, m_action( NoAction ), m_initialMousePos( QPoint( 0, 0 ) ), m_initialMouseGlobalPos( QPoint( 0, 0 ) ), - m_initialTCOPos( MidiTime(0) ), - m_initialTCOEnd( MidiTime(0) ), - m_initialOffsets( QVector() ), + m_initialTCOPos( TimePos(0) ), + m_initialTCOEnd( TimePos(0) ), + m_initialOffsets( QVector() ), m_hint( NULL ), m_mutedColor( 0, 0, 0 ), m_mutedBackgroundColor( 0, 0, 0 ), @@ -514,7 +514,7 @@ void TrackContentObjectView::updateLength() { setFixedWidth( static_cast( m_tco->length() * pixelsPerBar() / - MidiTime::ticksPerBar() ) + 1 /*+ + TimePos::ticksPerBar() ) + 1 /*+ TCO_BORDER_WIDTH * 2-1*/ ); } m_trackView->trackContainerView()->update(); @@ -577,7 +577,7 @@ void TrackContentObjectView::useTrackColor() void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee ) { TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); - MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + TimePos tcoPos = TimePos( m_tco->startPosition() ); if( tcw->canPasteSelection( tcoPos, dee ) == false ) { @@ -617,7 +617,7 @@ void TrackContentObjectView::dropEvent( QDropEvent * de ) if( m_trackView->trackContainerView()->allowRubberband() == true ) { TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); - MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + TimePos tcoPos = TimePos( m_tco->startPosition() ); if( tcw->pasteSelection( tcoPos, de ) == true ) { @@ -636,7 +636,7 @@ void TrackContentObjectView::dropEvent( QDropEvent * de ) // Copy state into existing tco DataFile dataFile( value.toUtf8() ); - MidiTime pos = m_tco->startPosition(); + TimePos pos = m_tco->startPosition(); QDomElement tcos = dataFile.content().firstChildElement( "tcos" ); m_tco->restoreState( tcos.firstChildElement().firstChildElement() ); m_tco->movePosition( pos ); @@ -830,7 +830,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) s_textFloat->setText( QString( "%1:%2" ). arg( m_tco->startPosition().getBar() + 1 ). arg( m_tco->startPosition().getTicks() % - MidiTime::ticksPerBar() ) ); + TimePos::ticksPerBar() ) ); } else if( m_action == Resize || m_action == ResizeLeft ) { @@ -838,13 +838,13 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). arg( m_tco->length().getBar() ). arg( m_tco->length().getTicks() % - MidiTime::ticksPerBar() ). + TimePos::ticksPerBar() ). arg( m_tco->startPosition().getBar() + 1 ). arg( m_tco->startPosition().getTicks() % - MidiTime::ticksPerBar() ). + TimePos::ticksPerBar() ). arg( m_tco->endPosition().getBar() + 1 ). arg( m_tco->endPosition().getTicks() % - MidiTime::ticksPerBar() ) ); + TimePos::ticksPerBar() ) ); } // s_textFloat->reparent( this ); // setup text-float as if TCO was already moved/resized @@ -954,7 +954,7 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); if( m_action == Move ) { - MidiTime newPos = draggedTCOPos( me ); + TimePos newPos = draggedTCOPos( me ); m_tco->movePosition(newPos); newPos = m_tco->startPosition(); // Get the real position the TCO was dragged to for the label @@ -962,13 +962,13 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) s_textFloat->setText( QString( "%1:%2" ). arg( newPos.getBar() + 1 ). arg( newPos.getTicks() % - MidiTime::ticksPerBar() ) ); + TimePos::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) ); } else if( m_action == MoveSelection ) { // 1: Find the position we want to move the grabbed TCO to - MidiTime newPos = draggedTCOPos( me ); + TimePos newPos = draggedTCOPos( me ); // 2: Handle moving the other selected TCOs the same distance QVector so = @@ -1002,12 +1002,12 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier); const float snapSize = gui->songEditor()->m_editor->getSnapSize(); // Length in ticks of one snap increment - const MidiTime snapLength = MidiTime( (int)(snapSize * MidiTime::ticksPerBar()) ); + const TimePos snapLength = TimePos( (int)(snapSize * TimePos::ticksPerBar()) ); if( m_action == Resize ) { // The clip's new length - MidiTime l = static_cast( me->x() * MidiTime::ticksPerBar() / ppb ); + TimePos l = static_cast( me->x() * TimePos::ticksPerBar() / ppb ); if ( unquantized ) { // We want to preserve this adjusted offset, @@ -1018,18 +1018,18 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) } else if ( me->modifiers() & Qt::ShiftModifier ) { // If shift is held, quantize clip's end position - MidiTime end = MidiTime( m_initialTCOPos + l ).quantize( snapSize ); + TimePos end = TimePos( m_initialTCOPos + l ).quantize( snapSize ); // The end position has to be after the clip's start - MidiTime min = m_initialTCOPos.quantize( snapSize ); + TimePos min = m_initialTCOPos.quantize( snapSize ); if ( min <= m_initialTCOPos ) min += snapLength; m_tco->changeLength( qMax(min - m_initialTCOPos, end - m_initialTCOPos) ); } else { // Otherwise, resize in fixed increments - MidiTime initialLength = m_initialTCOEnd - m_initialTCOPos; - MidiTime offset = MidiTime( l - initialLength ).quantize( snapSize ); + TimePos initialLength = m_initialTCOEnd - m_initialTCOPos; + TimePos offset = TimePos( l - initialLength ).quantize( snapSize ); // Don't resize to less than 1 tick - MidiTime min = MidiTime( initialLength % snapLength ); + TimePos min = TimePos( initialLength % snapLength ); if (min < 1) min += snapLength; m_tco->changeLength( qMax( min, initialLength + offset) ); } @@ -1041,9 +1041,9 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) { const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); - MidiTime t = qMax( 0, (int) + TimePos t = qMax( 0, (int) m_trackView->trackContainerView()->currentPosition() + - static_cast( x * MidiTime::ticksPerBar() / ppb ) ); + static_cast( x * TimePos::ticksPerBar() / ppb ) ); if( unquantized ) { // We want to preserve this adjusted offset, @@ -1055,21 +1055,21 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) else if( me->modifiers() & Qt::ShiftModifier ) { // If shift is held, quantize clip's start position // Don't let the start position move past the end position - MidiTime max = m_initialTCOEnd.quantize( snapSize ); + TimePos max = m_initialTCOEnd.quantize( snapSize ); if ( max >= m_initialTCOEnd ) max -= snapLength; t = qMin( max, t.quantize( snapSize ) ); } else { // Otherwise, resize in fixed increments // Don't resize to less than 1 tick - MidiTime initialLength = m_initialTCOEnd - m_initialTCOPos; - MidiTime minLength = MidiTime( initialLength % snapLength ); + TimePos initialLength = m_initialTCOEnd - m_initialTCOPos; + TimePos minLength = TimePos( initialLength % snapLength ); if (minLength < 1) minLength += snapLength; - MidiTime offset = MidiTime(t - m_initialTCOPos).quantize( snapSize ); + TimePos offset = TimePos(t - m_initialTCOPos).quantize( snapSize ); t = qMin( m_initialTCOEnd - minLength, m_initialTCOPos + offset ); } - MidiTime oldPos = m_tco->startPosition(); + TimePos oldPos = m_tco->startPosition(); if( m_tco->length() + ( oldPos - t ) >= 1 ) { m_tco->movePosition( t ); @@ -1081,13 +1081,13 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). arg( m_tco->length().getBar() ). arg( m_tco->length().getTicks() % - MidiTime::ticksPerBar() ). + TimePos::ticksPerBar() ). arg( m_tco->startPosition().getBar() + 1 ). arg( m_tco->startPosition().getTicks() % - MidiTime::ticksPerBar() ). + TimePos::ticksPerBar() ). arg( m_tco->endPosition().getBar() + 1 ). arg( m_tco->endPosition().getTicks() % - MidiTime::ticksPerBar() ) ); + TimePos::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); } else @@ -1300,8 +1300,8 @@ void TrackContentObjectView::paste() // For getMimeData() using namespace Clipboard; - // If possible, paste the selection on the MidiTime of the selected Track and remove it - MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + // If possible, paste the selection on the TimePos of the selected Track and remove it + TimePos tcoPos = TimePos( m_tco->startPosition() ); TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); @@ -1338,7 +1338,7 @@ float TrackContentObjectView::pixelsPerBar() void TrackContentObjectView::setInitialOffsets() { QVector so = m_trackView->trackContainerView()->selectedObjects(); - QVector offsets; + QVector offsets; for( QVector::iterator it = so.begin(); it != so.end(); ++it ) { @@ -1376,14 +1376,14 @@ bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance * * \param me The QMouseEvent */ -MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) +TimePos TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) { //Pixels per bar const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); // The pixel distance that the mouse has moved const int mouseOff = mapToGlobal(me->pos()).x() - m_initialMouseGlobalPos.x(); - MidiTime newPos = m_initialTCOPos + mouseOff * MidiTime::ticksPerBar() / ppb; - MidiTime offset = newPos - m_initialTCOPos; + TimePos newPos = m_initialTCOPos + mouseOff * TimePos::ticksPerBar() / ppb; + TimePos offset = newPos - m_initialTCOPos; // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize if ( me->button() != Qt::NoButton || (me->modifiers() & Qt::ControlModifier) @@ -1396,9 +1396,9 @@ MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) else if ( me->modifiers() & Qt::ShiftModifier ) { // If shift is held, quantize position (Default in 1.2.0 and earlier) // or end position, whichever is closest to the actual position - MidiTime startQ = newPos.quantize( gui->songEditor()->m_editor->getSnapSize() ); + TimePos startQ = newPos.quantize( gui->songEditor()->m_editor->getSnapSize() ); // Find start position that gives snapped clip end position - MidiTime endQ = ( newPos + m_tco->length() ); + TimePos endQ = ( newPos + m_tco->length() ); endQ = endQ.quantize( gui->songEditor()->m_editor->getSnapSize() ); endQ = endQ - m_tco->length(); // Select the position closest to actual position @@ -1482,8 +1482,8 @@ TrackContentWidget::TrackContentWidget( TrackView * parent ) : setAcceptDrops( true ); connect( parent->trackContainerView(), - SIGNAL( positionChanged( const MidiTime & ) ), - this, SLOT( changePosition( const MidiTime & ) ) ); + SIGNAL( positionChanged( const TimePos & ) ), + this, SLOT( changePosition( const TimePos & ) ) ); setStyle( QApplication::style() ); @@ -1611,7 +1611,7 @@ void TrackContentWidget::update() * * \param newPos The MIDI time to move to. */ -void TrackContentWidget::changePosition( const MidiTime & newPos ) +void TrackContentWidget::changePosition( const TimePos & newPos ) { if( m_trackView->trackContainerView() == gui->getBBEditor()->trackContainerView() ) { @@ -1648,7 +1648,7 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) return; } - MidiTime pos = newPos; + TimePos pos = newPos; if( pos < 0 ) { pos = m_trackView->trackContainerView()->currentPosition(); @@ -1674,7 +1674,7 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) ( ts <= begin && te >= end ) ) { tcov->move( static_cast( ( ts - begin ) * ppb / - MidiTime::ticksPerBar() ), + TimePos::ticksPerBar() ), tcov->y() ); if( !tcov->isVisible() ) { @@ -1699,12 +1699,12 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) * * \param mouseX the mouse's current X position in pixels. */ -MidiTime TrackContentWidget::getPosition( int mouseX ) +TimePos TrackContentWidget::getPosition( int mouseX ) { TrackContainerView * tv = m_trackView->trackContainerView(); - return MidiTime( tv->currentPosition() + + return TimePos( tv->currentPosition() + mouseX * - MidiTime::ticksPerBar() / + TimePos::ticksPerBar() / static_cast( tv->pixelsPerBar() ) ); } @@ -1717,7 +1717,7 @@ MidiTime TrackContentWidget::getPosition( int mouseX ) */ void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) { - MidiTime tcoPos = getPosition( dee->pos().x() ); + TimePos tcoPos = getPosition( dee->pos().x() ); if( canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); @@ -1737,7 +1737,7 @@ void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) * \param tcoPos the position of the TCO slot being pasted on * \param de the DropEvent generated */ -bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* de ) +bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QDropEvent* de ) { const QMimeData * mimeData = de->mimeData(); @@ -1749,7 +1749,7 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d } // Overloaded method to make it possible to call this method without a Drag&Drop event -bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData* md , bool allowSameBar ) +bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QMimeData* md , bool allowSameBar ) { // For decodeKey() and decodeValue() using namespace Clipboard; @@ -1771,8 +1771,8 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData* md // Extract the metadata and which TCO was grabbed QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" ); QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); - MidiTime grabbedTCOPos = tcoPosAttr.value().toInt(); - MidiTime grabbedTCOBar = MidiTime( grabbedTCOPos.getBar(), 0 ); + TimePos grabbedTCOPos = tcoPosAttr.value().toInt(); + TimePos grabbedTCOBar = TimePos( grabbedTCOPos.getBar(), 0 ); // Extract the track index that was originally clicked QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); @@ -1824,7 +1824,7 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData* md * \param tcoPos the position of the TCO slot being pasted on * \param de the DropEvent generated */ -bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) +bool TrackContentWidget::pasteSelection( TimePos tcoPos, QDropEvent * de ) { const QMimeData * mimeData = de->mimeData(); @@ -1838,7 +1838,7 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) } // Overloaded method so we can call it without a Drag&Drop event -bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, bool skipSafetyCheck ) +bool TrackContentWidget::pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck ) { // For decodeKey() and decodeValue() using namespace Clipboard; @@ -1866,7 +1866,7 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); int initialTrackIndex = tiAttr.value().toInt(); QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); - MidiTime grabbedTCOPos = tcoPosAttr.value().toInt(); + TimePos grabbedTCOPos = tcoPosAttr.value().toInt(); // Snap the mouse position to the beginning of the dropped bar, in ticks const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks(); @@ -1889,20 +1889,20 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, float snapSize = gui->songEditor()->m_editor->getSnapSize(); // All patterns should be offset the same amount as the grabbed pattern - MidiTime offset = MidiTime(tcoPos - grabbedTCOPos); + TimePos offset = TimePos(tcoPos - grabbedTCOPos); // Users expect clips to "fall" backwards, so bias the offset - offset = offset - MidiTime::ticksPerBar() * snapSize / 2; + offset = offset - TimePos::ticksPerBar() * snapSize / 2; // The offset is quantized (rather than the positions) to preserve fine adjustments offset = offset.quantize(snapSize); // Get the leftmost TCO and fix the offset if it reaches below bar 0 - MidiTime leftmostPos = grabbedTCOPos; + TimePos leftmostPos = grabbedTCOPos; for(int i = 0; i < tcoNodes.length(); ++i) { QDomElement outerTCOElement = tcoNodes.item(i).toElement(); QDomElement tcoElement = outerTCOElement.firstChildElement(); - MidiTime pos = tcoElement.attributeNode("pos").value().toInt(); + TimePos pos = tcoElement.attributeNode("pos").value().toInt(); if(pos < leftmostPos) { leftmostPos = pos; } } @@ -1919,9 +1919,9 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, Track * t = tracks.at( finalTrackIndex ); // The new position is the old position plus the offset. - MidiTime pos = tcoElement.attributeNode( "pos" ).value().toInt() + offset; + TimePos pos = tcoElement.attributeNode( "pos" ).value().toInt() + offset; // If we land on ourselves, offset by one snap - MidiTime shift = MidiTime::ticksPerBar() * gui->songEditor()->m_editor->getSnapSize(); + TimePos shift = TimePos::ticksPerBar() * gui->songEditor()->m_editor->getSnapSize(); if (offset == 0) { pos += shift; } TrackContentObject * tco = t->createTCO( pos ); @@ -1945,7 +1945,7 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, */ void TrackContentWidget::dropEvent( QDropEvent * de ) { - MidiTime tcoPos = MidiTime( getPosition( de->pos().x() ) ); + TimePos tcoPos = TimePos( getPosition( de->pos().x() ) ); if( pasteSelection( tcoPos, de ) == true ) { de->accept(); @@ -1978,8 +1978,8 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) so.at( i )->setSelected( false); } getTrack()->addJournalCheckPoint(); - const MidiTime pos = getPosition( me->x() ).getBar() * - MidiTime::ticksPerBar(); + const TimePos pos = getPosition( me->x() ).getBar() * + TimePos::ticksPerBar(); getTrack()->createTCO(pos); } } @@ -2038,11 +2038,11 @@ Track * TrackContentWidget::getTrack() * * \param posStart the starting position of the Widget (from getPosition()) */ -MidiTime TrackContentWidget::endPosition( const MidiTime & posStart ) +TimePos TrackContentWidget::endPosition( const TimePos & posStart ) { const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); const int w = width(); - return posStart + static_cast( w * MidiTime::ticksPerBar() / ppb ); + return posStart + static_cast( w * TimePos::ticksPerBar() / ppb ); } void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) @@ -2079,8 +2079,8 @@ void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenu switch( action ) { case Paste: - // Paste the selection on the MidiTime of the context menu event - MidiTime tcoPos = getPosition( cme->x() ); + // Paste the selection on the TimePos of the context menu event + TimePos tcoPos = getPosition( cme->x() ); pasteSelection( tcoPos, getMimeData() ); break; @@ -2707,7 +2707,7 @@ void Track::loadSettings( const QDomElement & element ) && !node.toElement().attribute( "metadata" ).toInt() ) { TrackContentObject * tco = createTCO( - MidiTime( 0 ) ); + TimePos( 0 ) ); tco->restoreState( node.toElement() ); } } @@ -2803,7 +2803,7 @@ TrackContentObject * Track::getTCO( int tcoNum ) } printf( "called Track::getTCO( %d ), " "but TCO %d doesn't exist\n", tcoNum, tcoNum ); - return createTCO( tcoNum * MidiTime::ticksPerBar() ); + return createTCO( tcoNum * TimePos::ticksPerBar() ); } @@ -2847,8 +2847,8 @@ int Track::getTCONum( const TrackContentObject * tco ) * \param start The MIDI start time of the range. * \param end The MIDI endi time of the range. */ -void Track::getTCOsInRange( tcoVector & tcoV, const MidiTime & start, - const MidiTime & end ) +void Track::getTCOsInRange( tcoVector & tcoV, const TimePos & start, + const TimePos & end ) { for( TrackContentObject* tco : m_trackContentObjects ) { @@ -2880,7 +2880,7 @@ void Track::swapPositionOfTCOs( int tcoNum1, int tcoNum2 ) qSwap( m_trackContentObjects[tcoNum1], m_trackContentObjects[tcoNum2] ); - const MidiTime pos = m_trackContentObjects[tcoNum1]->startPosition(); + const TimePos pos = m_trackContentObjects[tcoNum1]->startPosition(); m_trackContentObjects[tcoNum1]->movePosition( m_trackContentObjects[tcoNum2]->startPosition() ); @@ -2894,9 +2894,9 @@ void Track::createTCOsForBB( int bb ) { while( numOfTCOs() < bb + 1 ) { - MidiTime position = MidiTime( numOfTCOs(), 0 ); + TimePos position = TimePos( numOfTCOs(), 0 ); TrackContentObject * tco = createTCO( position ); - tco->changeLength( MidiTime( 1, 0 ) ); + tco->changeLength( TimePos( 1, 0 ) ); } } @@ -2910,7 +2910,7 @@ void Track::createTCOsForBB( int bb ) * in ascending order by TCO time, once we hit a TCO that was earlier * than the insert time, we could fall out of the loop early. */ -void Track::insertBar( const MidiTime & pos ) +void Track::insertBar( const TimePos & pos ) { // we'll increase the position of every TCO, positioned behind pos, by // one bar @@ -2920,7 +2920,7 @@ void Track::insertBar( const MidiTime & pos ) if( ( *it )->startPosition() >= pos ) { ( *it )->movePosition( (*it)->startPosition() + - MidiTime::ticksPerBar() ); + TimePos::ticksPerBar() ); } } } @@ -2932,7 +2932,7 @@ void Track::insertBar( const MidiTime & pos ) * * \param pos The time at which we want to remove the bar. */ -void Track::removeBar( const MidiTime & pos ) +void Track::removeBar( const TimePos & pos ) { // we'll decrease the position of every TCO, positioned behind pos, by // one bar @@ -2941,7 +2941,7 @@ void Track::removeBar( const MidiTime & pos ) { if( ( *it )->startPosition() >= pos ) { - (*it)->movePosition((*it)->startPosition() - MidiTime::ticksPerBar()); + (*it)->movePosition((*it)->startPosition() - TimePos::ticksPerBar()); } } } @@ -2975,7 +2975,7 @@ bar_t Track::length() const } } - return last / MidiTime::ticksPerBar(); + return last / TimePos::ticksPerBar(); } diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index eac40ea6f84..84846a77ca3 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -248,13 +248,13 @@ bool TrackContainer::isEmpty() const -AutomatedValueMap TrackContainer::automatedValuesAt(MidiTime time, int tcoNum) const +AutomatedValueMap TrackContainer::automatedValuesAt(TimePos time, int tcoNum) const { return automatedValuesFromTracks(tracks(), time, tcoNum); } -AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tracks, MidiTime time, int tcoNum) +AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tracks, TimePos time, int tcoNum) { Track::tcoVector tcos; @@ -295,7 +295,7 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra if (! p->hasAutomation()) { continue; } - MidiTime relTime = time - p->startPosition(); + TimePos relTime = time - p->startPosition(); if (! p->getAutoResize()) { relTime = qMin(relTime, p->length()); } @@ -311,9 +311,9 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra auto bbIndex = dynamic_cast(bb->getTrack())->index(); auto bbContainer = Engine::getBBTrackContainer(); - MidiTime bbTime = time - tco->startPosition(); + TimePos bbTime = time - tco->startPosition(); bbTime = std::min(bbTime, tco->length()); - bbTime = bbTime % (bbContainer->lengthOfBB(bbIndex) * MidiTime::ticksPerBar()); + bbTime = bbTime % (bbContainer->lengthOfBB(bbIndex) * TimePos::ticksPerBar()); auto bbValues = bbContainer->automatedValuesAt(bbTime, bbIndex); for (auto it=bbValues.begin(); it != bbValues.end(); it++) diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index e7ede9dd60d..98fe7b13b20 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -206,7 +206,7 @@ bool Lv2ControlBase::hasNoteInput() const void Lv2ControlBase::handleMidiInputEvent(const MidiEvent &event, - const MidiTime &time, f_cnt_t offset) + const TimePos &time, f_cnt_t offset) { for (auto& c : m_procs) { c->handleMidiInputEvent(event, time, offset); } } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 0ab204ae971..2190a6f1d09 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -50,7 +50,7 @@ struct MidiInputEvent { MidiEvent ev; - MidiTime time; + TimePos time; f_cnt_t offset; }; @@ -320,7 +320,7 @@ void Lv2Proc::run(fpp_t frames) // in case there will be a PR which removes this callback and instead adds a // `ringbuffer_t` to `class Instrument`, this // function (and the ringbuffer and its reader in `Lv2Proc`) will simply vanish -void Lv2Proc::handleMidiInputEvent(const MidiEvent &event, const MidiTime &time, f_cnt_t offset) +void Lv2Proc::handleMidiInputEvent(const MidiEvent &event, const TimePos &time, f_cnt_t offset) { if(m_midiIn) { diff --git a/src/core/midi/MidiAlsaSeq.cpp b/src/core/midi/MidiAlsaSeq.cpp index 56fd956d46d..70c5a40c80a 100644 --- a/src/core/midi/MidiAlsaSeq.cpp +++ b/src/core/midi/MidiAlsaSeq.cpp @@ -157,7 +157,7 @@ QString MidiAlsaSeq::probeDevice() -void MidiAlsaSeq::processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ) +void MidiAlsaSeq::processOutEvent( const MidiEvent& event, const TimePos& time, const MidiPort* port ) { // HACK!!! - need a better solution which isn't that easy since we // cannot store const-ptrs in our map because we need to call non-const @@ -536,7 +536,7 @@ void MidiAlsaSeq::run() ev->data.note.velocity, source ), - MidiTime( ev->time.tick ) ); + TimePos( ev->time.tick ) ); break; case SND_SEQ_EVENT_NOTEOFF: @@ -547,7 +547,7 @@ void MidiAlsaSeq::run() ev->data.note.velocity, source ), - MidiTime( ev->time.tick) ); + TimePos( ev->time.tick) ); break; case SND_SEQ_EVENT_KEYPRESS: @@ -558,7 +558,7 @@ void MidiAlsaSeq::run() KeysPerOctave, ev->data.note.velocity, source - ), MidiTime() ); + ), TimePos() ); break; case SND_SEQ_EVENT_CONTROLLER: @@ -567,7 +567,7 @@ void MidiAlsaSeq::run() ev->data.control.channel, ev->data.control.param, ev->data.control.value, source ), - MidiTime() ); + TimePos() ); break; case SND_SEQ_EVENT_PGMCHANGE: @@ -576,7 +576,7 @@ void MidiAlsaSeq::run() ev->data.control.channel, ev->data.control.value, 0, source ), - MidiTime() ); + TimePos() ); break; case SND_SEQ_EVENT_CHANPRESS: @@ -585,14 +585,14 @@ void MidiAlsaSeq::run() ev->data.control.channel, ev->data.control.param, ev->data.control.value, source ), - MidiTime() ); + TimePos() ); break; case SND_SEQ_EVENT_PITCHBEND: dest->processInEvent( MidiEvent( MidiPitchBend, ev->data.control.channel, ev->data.control.value + 8192, 0, source ), - MidiTime() ); + TimePos() ); break; case SND_SEQ_EVENT_SENSING: diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp index 0575b76ae51..67e2084cfde 100644 --- a/src/core/midi/MidiApple.cpp +++ b/src/core/midi/MidiApple.cpp @@ -57,7 +57,7 @@ MidiApple::~MidiApple() -void MidiApple::processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ) +void MidiApple::processOutEvent( const MidiEvent& event, const TimePos& time, const MidiPort* port ) { qDebug("MidiApple:processOutEvent displayName:'%s'",port->displayName().toLatin1().constData()); diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index e37f59c06ba..b3d7e63635d 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -266,7 +266,7 @@ void MidiClientRaw::processParsedEvent() -void MidiClientRaw::processOutEvent( const MidiEvent& event, const MidiTime & , const MidiPort* port ) +void MidiClientRaw::processOutEvent( const MidiEvent& event, const TimePos & , const MidiPort* port ) { // TODO: also evaluate _time and queue event if necessary switch( event.type() ) diff --git a/src/core/midi/MidiController.cpp b/src/core/midi/MidiController.cpp index 72128e18436..93cea4a00f5 100644 --- a/src/core/midi/MidiController.cpp +++ b/src/core/midi/MidiController.cpp @@ -80,7 +80,7 @@ void MidiController::updateName() -void MidiController::processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) +void MidiController::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { unsigned char controllerNum; switch( event.type() ) diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index 4e97a6713cb..84d9cc8d1ce 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -120,7 +120,7 @@ void MidiPort::setMode( Mode mode ) -void MidiPort::processInEvent( const MidiEvent& event, const MidiTime& time ) +void MidiPort::processInEvent( const MidiEvent& event, const TimePos& time ) { // mask event if( isInputEnabled() && @@ -149,7 +149,7 @@ void MidiPort::processInEvent( const MidiEvent& event, const MidiTime& time ) -void MidiPort::processOutEvent( const MidiEvent& event, const MidiTime& time ) +void MidiPort::processOutEvent( const MidiEvent& event, const TimePos& time ) { // When output is enabled, route midi events if the selected channel matches // the event channel or if there's no selected channel (value 0, represented by "--") diff --git a/src/core/midi/MidiWinMM.cpp b/src/core/midi/MidiWinMM.cpp index ef60f30153b..dbbc06ae1c8 100644 --- a/src/core/midi/MidiWinMM.cpp +++ b/src/core/midi/MidiWinMM.cpp @@ -49,7 +49,7 @@ MidiWinMM::~MidiWinMM() -void MidiWinMM::processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ) +void MidiWinMM::processOutEvent( const MidiEvent& event, const TimePos& time, const MidiPort* port ) { const DWORD shortMsg = ( event.type() + event.channel() ) + ( ( event.param( 0 ) & 0xff ) << 8 ) + diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 1c9ac03a418..45412199400 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -285,7 +285,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) const float y_scale = max - min; const float h = ( height() - 2 * TCO_BORDER_WIDTH ) / y_scale; - const float ppTick = ppb / MidiTime::ticksPerBar(); + const float ppTick = ppb / TimePos::ticksPerBar(); p.translate( 0.0f, max * height() / y_scale - TCO_BORDER_WIDTH ); p.scale( 1.0f, -h ); diff --git a/src/gui/ControllerConnectionDialog.cpp b/src/gui/ControllerConnectionDialog.cpp index 6b6527b3342..e15b5aeada6 100644 --- a/src/gui/ControllerConnectionDialog.cpp +++ b/src/gui/ControllerConnectionDialog.cpp @@ -63,7 +63,7 @@ class AutoDetectMidiController : public MidiController } - void processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 ) override + void processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ) override { if( event.type() == MidiControlChange && ( m_midiPort.inputChannel() == 0 || m_midiPort.inputChannel() == event.channel() + 1 ) ) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 7828dcfbc92..b1de8e1a47a 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1671,7 +1671,7 @@ void MainWindow::onSongStopped() { if(songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) ) { - songEditor->m_editor->updatePosition( MidiTime(tl->savedPos().getTicks() ) ); + songEditor->m_editor->updatePosition( TimePos(tl->savedPos().getTicks() ) ); } tl->savePos( -1 ); } diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index 070ac0a40d1..e7e7ca113e0 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -43,7 +43,7 @@ QPixmap * TimeLineWidget::s_posMarkerPixmap = NULL; TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb, - Song::PlayPos & pos, const MidiTime & begin, Song::PlayModes mode, + Song::PlayPos & pos, const TimePos & begin, Song::PlayModes mode, QWidget * parent ) : QWidget( parent ), m_inactiveLoopColor( 52, 63, 53, 64 ), @@ -189,7 +189,7 @@ void TimeLineWidget::loadSettings( const QDomElement & _this ) -void TimeLineWidget::updatePosition( const MidiTime & ) +void TimeLineWidget::updatePosition( const TimePos & ) { const int new_x = markerX( m_pos ); @@ -266,14 +266,14 @@ void TimeLineWidget::paintEvent( QPaintEvent * ) bar_t barNumber = m_begin.getBar(); int const x = m_xOffset + s_posMarkerPixmap->width() / 2 - - ( ( static_cast( m_begin * m_ppb ) / MidiTime::ticksPerBar() ) % static_cast( m_ppb ) ); + ( ( static_cast( m_begin * m_ppb ) / TimePos::ticksPerBar() ) % static_cast( m_ppb ) ); for( int i = 0; x + i * m_ppb < width(); ++i ) { ++barNumber; if( ( barNumber - 1 ) % qMax( 1, qRound( 1.0f / 3.0f * - MidiTime::ticksPerBar() / m_ppb ) ) == 0 ) + TimePos::ticksPerBar() / m_ppb ) ) == 0 ) { const int cx = x + qRound( i * m_ppb ); p.setPen( barLineColor ); @@ -330,8 +330,8 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) else if( event->button() == Qt::RightButton ) { m_moveXOff = s_posMarkerPixmap->width() / 2; - const MidiTime t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerBar() / m_ppb ); - const MidiTime loopMid = ( m_loopPos[0] + m_loopPos[1] ) / 2; + const TimePos t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb ); + const TimePos loopMid = ( m_loopPos[0] + m_loopPos[1] ) / 2; if( t < loopMid ) { @@ -366,7 +366,7 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) { parentWidget()->update(); // essential for widgets that this timeline had taken their mouse move event from. - const MidiTime t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerBar() / m_ppb ); + const TimePos t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb ); switch( m_action ) { @@ -405,13 +405,13 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) // Note, swap 1 and 0 below and the behavior "skips" the other // marking instead of pushing it. if( m_action == MoveLoopBegin ) - { - m_loopPos[0] -= MidiTime::ticksPerBar(); - } + { + m_loopPos[0] -= TimePos::ticksPerBar(); + } else - { - m_loopPos[1] += MidiTime::ticksPerBar(); - } + { + m_loopPos[1] += TimePos::ticksPerBar(); + } } update(); break; diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index 9b51c76f272..fe73de1b1d1 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -122,9 +122,9 @@ TrackView * TrackContainerView::addTrackView( TrackView * _tv ) { m_trackViews.push_back( _tv ); m_scrollLayout->addWidget( _tv ); - connect( this, SIGNAL( positionChanged( const MidiTime & ) ), + connect( this, SIGNAL( positionChanged( const TimePos & ) ), _tv->getTrackContentWidget(), - SLOT( changePosition( const MidiTime & ) ) ); + SLOT( changePosition( const TimePos & ) ) ); realignTracks(); return( _tv ); } diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index b64cea0f5f2..c5116b9281a 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -150,10 +150,10 @@ AutomationEditor::AutomationEditor() : Song::Mode_PlayAutomationPattern ), m_currentPosition, Song::Mode_PlayAutomationPattern, this ); - connect( this, SIGNAL( positionChanged( const MidiTime & ) ), - m_timeLine, SLOT( updatePosition( const MidiTime & ) ) ); - connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ), - this, SLOT( updatePosition( const MidiTime & ) ) ); + connect( this, SIGNAL( positionChanged( const TimePos & ) ), + m_timeLine, SLOT( updatePosition( const TimePos & ) ) ); + connect( m_timeLine, SIGNAL( positionChanged( const TimePos & ) ), + this, SLOT( updatePosition( const TimePos & ) ) ); removeSelection(); @@ -482,8 +482,8 @@ void AutomationEditor::drawLine( int x0In, float y0, int x1In, float y1 ) x += xstep; i += 1; - m_pattern->removeValue( MidiTime( x ) ); - m_pattern->putValue( MidiTime( x ), y ); + m_pattern->removeValue( TimePos( x ) ); + m_pattern->putValue( TimePos( x ), y ); } } @@ -510,7 +510,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) x -= VALUES_WIDTH; // get tick in which the user clicked - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; // get time map of current pattern @@ -527,7 +527,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) if( pos_ticks >= it.key() && ( it+1==time_map.end() || pos_ticks <= (it+1).key() ) && - ( pos_ticks<= it.key() + MidiTime::ticksPerBar() *4 / m_ppb ) && + ( pos_ticks<= it.key() + TimePos::ticksPerBar() *4 / m_ppb ) && ( level == it.value() || mouseEvent->button() == Qt::RightButton ) ) { break; @@ -565,9 +565,9 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) if( it == time_map.end() ) { // then set new value - MidiTime value_pos( pos_ticks ); + TimePos value_pos( pos_ticks ); - MidiTime new_time = + TimePos new_time = m_pattern->setDragValue( value_pos, level, true, mouseEvent->modifiers() & @@ -584,7 +584,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) int aligned_x = (int)( (float)( ( it.key() - m_currentPosition ) * - m_ppb ) / MidiTime::ticksPerBar() ); + m_ppb ) / TimePos::ticksPerBar() ); m_moveXOffset = x - aligned_x - 1; // set move-cursor QCursor c( Qt::SizeAllCursor ); @@ -717,7 +717,7 @@ void AutomationEditor::removePoints( int x0, int x1 ) int i = 0; while( i <= deltax ) { - m_pattern->removeValue( MidiTime( x ) ); + m_pattern->removeValue( TimePos( x ) ); x += xstep; i += 1; } @@ -746,7 +746,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) x -= m_moveXOffset; } - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; // m_mouseDownLeft used to prevent drag when drawing line if (m_mouseDownLeft && m_editMode == DRAW) @@ -768,7 +768,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) // we moved the value so the value has to be // moved properly according to new starting- // time in the time map of pattern - m_pattern->setDragValue( MidiTime( pos_ticks ), + m_pattern->setDragValue( TimePos( pos_ticks ), level, true, mouseEvent->modifiers() & Qt::ControlModifier ); @@ -879,7 +879,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; m_selectedTick = pos_ticks - m_selectStartTick; @@ -900,7 +900,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) // move selection + selected values // do horizontal move-stuff - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; int ticks_diff = pos_ticks - m_moveStartTick; @@ -925,8 +925,8 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } m_selectStartTick += ticks_diff; - int bar_diff = ticks_diff / MidiTime::ticksPerBar(); - ticks_diff = ticks_diff % MidiTime::ticksPerBar(); + int bar_diff = ticks_diff / TimePos::ticksPerBar(); + ticks_diff = ticks_diff % TimePos::ticksPerBar(); // do vertical move-stuff @@ -971,27 +971,27 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) for( timeMap::iterator it = m_selValuesForMove.begin(); it != m_selValuesForMove.end(); ++it ) { - MidiTime new_value_pos; + TimePos new_value_pos; if( it.key() ) { int value_bar = ( it.key() / - MidiTime::ticksPerBar() ) + TimePos::ticksPerBar() ) + bar_diff; int value_ticks = ( it.key() % - MidiTime::ticksPerBar() ) + TimePos::ticksPerBar() ) + ticks_diff; // ensure value_ticks range - if( value_ticks / MidiTime::ticksPerBar() ) + if( value_ticks / TimePos::ticksPerBar() ) { value_bar += value_ticks - / MidiTime::ticksPerBar(); + / TimePos::ticksPerBar(); value_ticks %= - MidiTime::ticksPerBar(); + TimePos::ticksPerBar(); } m_pattern->removeValue( it.key() ); - new_value_pos = MidiTime( value_bar, + new_value_pos = TimePos( value_bar, value_ticks ); } new_selValuesForMove[ @@ -1039,7 +1039,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; m_selectedTick = pos_ticks - @@ -1295,7 +1295,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); float zoomFactor = m_zoomXLevels[m_zoomingXModel.value()]; //the bars which disappears at the left side by scrolling - int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); + int leftBars = m_currentPosition * zoomFactor / TimePos::ticksPerBar(); //iterates the visible bars and draw the shading on uneven bars for( int x = VALUES_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppb, ++barCount ) @@ -1321,10 +1321,10 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } // and finally bars - for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), + for( tick = m_currentPosition - m_currentPosition % TimePos::ticksPerBar(), x = xCoordOfTick( tick ); x<=width(); - tick += MidiTime::ticksPerBar(), x = xCoordOfTick( tick ) ) + tick += TimePos::ticksPerBar(), x = xCoordOfTick( tick ) ) { p.setPen( barLineColor() ); p.drawLine( x, grid_bottom, x, x_line_end ); @@ -1460,8 +1460,8 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) // now draw selection-frame int x = ( sel_pos_start - m_currentPosition ) * m_ppb / - MidiTime::ticksPerBar(); - int w = ( sel_pos_end - sel_pos_start ) * m_ppb / MidiTime::ticksPerBar(); + TimePos::ticksPerBar(); + int w = ( sel_pos_end - sel_pos_start ) * m_ppb / TimePos::ticksPerBar(); int y, h; if( m_y_auto ) { @@ -1533,7 +1533,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) int AutomationEditor::xCoordOfTick(int tick ) { return VALUES_WIDTH + ( ( tick - m_currentPosition ) - * m_ppb / MidiTime::ticksPerBar() ); + * m_ppb / TimePos::ticksPerBar() ); } @@ -1717,7 +1717,7 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) } x = qBound( 0, x, m_zoomingXModel.size() - 1 ); - int mouseX = (position( we ).x() - VALUES_WIDTH)* MidiTime::ticksPerBar(); + int mouseX = (position( we ).x() - VALUES_WIDTH)* TimePos::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = mouseX / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -1994,7 +1994,7 @@ void AutomationEditor::getSelectedValues( timeMap & selected_values ) ++it ) { //TODO: Add constant - tick_t len_ticks = MidiTime::ticksPerBar() / 16; + tick_t len_ticks = TimePos::ticksPerBar() / 16; float level = it.value(); tick_t pos_ticks = it.key(); @@ -2123,7 +2123,7 @@ void AutomationEditor::deleteSelectedValues() -void AutomationEditor::updatePosition(const MidiTime & t ) +void AutomationEditor::updatePosition(const TimePos & t ) { if( ( Engine::getSong()->isPlaying() && Engine::getSong()->playMode() == @@ -2131,17 +2131,17 @@ void AutomationEditor::updatePosition(const MidiTime & t ) m_scrollBack == true ) { const int w = width() - VALUES_WIDTH; - if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) + if( t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb ) { m_leftRightScroll->setValue( t.getBar() * - MidiTime::ticksPerBar() ); + TimePos::ticksPerBar() ); } else if( t < m_currentPosition ) { - MidiTime t_ = qMax( t - w * MidiTime::ticksPerBar() * - MidiTime::ticksPerBar() / m_ppb, 0 ); + TimePos t_ = qMax( t - w * TimePos::ticksPerBar() * + TimePos::ticksPerBar() / m_ppb, 0 ); m_leftRightScroll->setValue( t_.getBar() * - MidiTime::ticksPerBar() ); + TimePos::ticksPerBar() ); } m_scrollBack = false; } diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index ffafc4577d8..1406e683dac 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -260,7 +260,7 @@ void BBTrackContainerView::dropEvent(QDropEvent* de) hasValidBBTCOs = true; for (int i = 0; i < t->getTCOs().size(); ++i) { - if (t->getTCOs()[i]->startPosition() != MidiTime(i, 0)) + if (t->getTCOs()[i]->startPosition() != TimePos(i, 0)) { hasValidBBTCOs = false; break; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 194867ce463..c99176cc7fa 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -174,7 +174,7 @@ PianoRoll::PianoRoll() : m_whiteKeySmallHeight(qFloor(m_keyLineHeight * 1.5)), m_whiteKeyBigHeight(m_keyLineHeight * 2), m_blackKeyHeight(m_keyLineHeight), - m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), + m_lenOfNewNotes( TimePos( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), m_minResizeLen( 0 ), @@ -285,28 +285,28 @@ PianoRoll::PianoRoll() : Song::Mode_PlayPattern ), m_currentPosition, Song::Mode_PlayPattern, this ); - connect( this, SIGNAL( positionChanged( const MidiTime & ) ), - m_timeLine, SLOT( updatePosition( const MidiTime & ) ) ); - connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ), - this, SLOT( updatePosition( const MidiTime & ) ) ); + connect( this, SIGNAL( positionChanged( const TimePos & ) ), + m_timeLine, SLOT( updatePosition( const TimePos & ) ) ); + connect( m_timeLine, SIGNAL( positionChanged( const TimePos & ) ), + this, SLOT( updatePosition( const TimePos & ) ) ); // white position line follows timeline marker m_positionLine = new PositionLine(this); //update timeline when in step-recording mode - connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const MidiTime & ) ), - this, SLOT( updatePositionStepRecording( const MidiTime & ) ) ); + connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const TimePos & ) ), + this, SLOT( updatePositionStepRecording( const TimePos & ) ) ); // update timeline when in record-accompany mode connect( Engine::getSong()->getPlayPos( Song::Mode_PlaySong ).m_timeLine, - SIGNAL( positionChanged( const MidiTime & ) ), + SIGNAL( positionChanged( const TimePos & ) ), this, - SLOT( updatePositionAccompany( const MidiTime & ) ) ); + SLOT( updatePositionAccompany( const TimePos & ) ) ); // TODO /* connect( engine::getSong()->getPlayPos( Song::Mode_PlayBB ).m_timeLine, - SIGNAL( positionChanged( const MidiTime & ) ), + SIGNAL( positionChanged( const TimePos & ) ), this, - SLOT( updatePositionAccompany( const MidiTime & ) ) );*/ + SLOT( updatePositionAccompany( const TimePos & ) ) );*/ removeSelection(); @@ -686,10 +686,10 @@ void PianoRoll::glueNotes() // position or next in sequence. if ((*note)->key() == (*nextNote)->key() && (*nextNote)->pos() <= (*note)->pos() - + qMax(MidiTime(0), (*note)->length())) + + qMax(TimePos(0), (*note)->length())) { (*note)->setLength(qMax((*note)->length(), - MidiTime((*nextNote)->endPos() - (*note)->pos()))); + TimePos((*nextNote)->endPos() - (*note)->pos()))); noteToRemove.push_back(*nextNote); ++nextNote; } @@ -827,7 +827,7 @@ void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) xEnd -= m_whiteKeyWidth; // select an area of notes - int posTicks = xStart * MidiTime::ticksPerBar() / m_ppb + + int posTicks = xStart * TimePos::ticksPerBar() / m_ppb + m_currentPosition; int keyNum = 0; m_selectStartTick = posTicks; @@ -837,7 +837,7 @@ void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) // change size of selection // get tick in which the cursor is posated - posTicks = xEnd * MidiTime::ticksPerBar() / m_ppb + + posTicks = xEnd * TimePos::ticksPerBar() / m_ppb + m_currentPosition; keyNum = 120; @@ -987,7 +987,7 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, for( timeMap::ConstIterator it = map.begin(); it != map.end(); ++it ) { int pos_ticks = it.key(); - int pos_x = _x + pos_ticks * m_ppb / MidiTime::ticksPerBar(); + int pos_x = _x + pos_ticks * m_ppb / TimePos::ticksPerBar(); const float level = it.value(); @@ -1200,7 +1200,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) // Move selected notes by one bar to the left if (hasValidPattern()) { - shiftPos( direction * MidiTime::ticksPerBar() ); + shiftPos( direction * TimePos::ticksPerBar() ); } } else if( ke->modifiers() & Qt::ShiftModifier && m_action == ActionNone) @@ -1498,7 +1498,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) x -= m_whiteKeyWidth; // get tick in which the user clicked - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; @@ -1512,7 +1512,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) for( int i = 0; i < notes.size(); ++i ) { Note *note = *it; - MidiTime len = note->length(); + TimePos len = note->length(); if( len < 0 ) { len = 4; @@ -1528,7 +1528,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) || ( edit_note && pos_ticks <= note->pos() + - NOTE_EDIT_LINE_WIDTH * MidiTime::ticksPerBar() / m_ppb ) + NOTE_EDIT_LINE_WIDTH * TimePos::ticksPerBar() / m_ppb ) ) ) { @@ -1569,8 +1569,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // +32 to quanitize the note correctly when placing notes with // the mouse. We do this here instead of in note.quantized // because live notes should still be quantized at the half. - MidiTime note_pos( pos_ticks - ( quantization() / 2 ) ); - MidiTime note_len( newNoteLen() ); + TimePos note_pos( pos_ticks - ( quantization() / 2 ) ); + TimePos note_len( newNoteLen() ); Note new_note( note_len, note_pos, key_num ); new_note.setSelected( true ); @@ -1649,8 +1649,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) } // clicked at the "tail" of the note? - if( pos_ticks * m_ppb / MidiTime::ticksPerBar() > - m_currentNote->endPos() * m_ppb / MidiTime::ticksPerBar() - RESIZE_AREA_WIDTH + if( pos_ticks * m_ppb / TimePos::ticksPerBar() > + m_currentNote->endPos() * m_ppb / TimePos::ticksPerBar() - RESIZE_AREA_WIDTH && m_currentNote->length() > 0 ) { m_pattern->addJournalCheckPoint(); @@ -1807,10 +1807,10 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) int pixel_range = 4; int x = me->x() - m_whiteKeyWidth; const int ticks_start = ( x-pixel_range/2 ) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; const int ticks_end = ( x+pixel_range/2 ) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; - const int ticks_middle = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; + const int ticks_middle = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; // go through notes to figure out which one we want to change bool altPressed = me->modifiers() & Qt::AltModifier; @@ -2218,9 +2218,9 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // convert to ticks so that we can check which notes // are in the range int ticks_start = ( x-pixel_range/2 ) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x+pixel_range/2 ) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; // get note-vector of current pattern const NoteVector & notes = m_pattern->notes(); @@ -2316,7 +2316,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // set move- or resize-cursor // get tick in which the cursor is posated - int pos_ticks = ( x * MidiTime::ticksPerBar() ) / + int pos_ticks = ( x * TimePos::ticksPerBar() ) / m_ppb + m_currentPosition; // get note-vector of current pattern @@ -2349,7 +2349,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) Note *note = *it; // x coordinate of the right edge of the note int noteRightX = ( note->pos() + note->length() - - m_currentPosition) * m_ppb/MidiTime::ticksPerBar(); + m_currentPosition) * m_ppb/TimePos::ticksPerBar(); // cursor at the "tail" of the note? bool atTail = note->length() > 0 && x > noteRightX - RESIZE_AREA_WIDTH; @@ -2370,7 +2370,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // change size of selection // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; m_selectedTick = pos_ticks - m_selectStartTick; @@ -2392,7 +2392,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // any key if in erase mode // get tick in which the user clicked - int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + + int pos_ticks = x * TimePos::ticksPerBar() / m_ppb + m_currentPosition; @@ -2406,7 +2406,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) while( it != notes.end() ) { Note *note = *it; - MidiTime len = note->length(); + TimePos len = note->length(); if( len < 0 ) { len = 4; @@ -2423,7 +2423,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) ( edit_note && pos_ticks <= note->pos() + NOTE_EDIT_LINE_WIDTH * - MidiTime::ticksPerBar() / + TimePos::ticksPerBar() / m_ppb ) ) ) @@ -2479,7 +2479,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerBar()/ m_ppb + + int pos_ticks = x * TimePos::ticksPerBar()/ m_ppb + m_currentPosition; m_selectedTick = pos_ticks - @@ -2540,7 +2540,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // convert pixels to ticks and keys int off_x = x - m_moveStartX; - int off_ticks = off_x * MidiTime::ticksPerBar() / m_ppb; + int off_ticks = off_x * TimePos::ticksPerBar() / m_ppb; int off_key = getKey( y ) - getKey( m_moveStartY ); // handle scroll changes while dragging @@ -2588,7 +2588,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) { ticks_new = 1; } - note->setLength( MidiTime( ticks_new ) ); + note->setLength( TimePos( ticks_new ) ); m_lenOfNewNotes = note->length(); } else @@ -2603,7 +2603,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) key_num = qMax(0, key_num); key_num = qMin(key_num, NumKeys); - note->setPos( MidiTime( pos_ticks ) ); + note->setPos( TimePos( pos_ticks ) ); note->setKey( key_num ); } } @@ -2680,8 +2680,8 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) posteriorDeltaThisFrame = (newStart+newLength) - (note->pos().getTicks() + note->length().getTicks()); } - note->setLength( MidiTime(newLength) ); - note->setPos( MidiTime(newStart) ); + note->setLength( TimePos(newLength) ); + note->setPos( TimePos(newStart) ); m_lenOfNewNotes = note->length(); } @@ -2693,7 +2693,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) if (!note->selected() && note->pos().getTicks() >= posteriorEndTick) { int newStart = note->pos().getTicks() + posteriorDeltaThisFrame; - note->setPos( MidiTime(newStart) ); + note->setPos( TimePos(newStart) ); } } } @@ -2706,7 +2706,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) for (Note *note : selectedNotes) { int newLength = qMax(minLength, note->oldLength() + off_ticks); - note->setLength(MidiTime(newLength)); + note->setLength(TimePos(newLength)); m_lenOfNewNotes = note->length(); } @@ -2821,7 +2821,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } auto xCoordOfTick = [=](int tick) { return m_whiteKeyWidth + ( - (tick - m_currentPosition) * m_ppb / MidiTime::ticksPerBar() + (tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar() ); }; p.setPen(m_lineColor); @@ -2987,7 +2987,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) static_cast(Engine::getSong()->getTimeSigModel().getDenominator()); float zoomFactor = m_zoomLevels[m_zoomingModel.value()]; //the bars which disappears at the left side by scrolling - int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); + int leftBars = m_currentPosition * zoomFactor / TimePos::ticksPerBar(); //iterates the visible bars and draw the shading on uneven bars for (int x = m_whiteKeyWidth, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; @@ -3017,10 +3017,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw vertical bar lines p.setPen(m_barLineColor); - for(tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), + for(tick = m_currentPosition - m_currentPosition % TimePos::ticksPerBar(), x = xCoordOfTick( tick ); x <= width(); - tick += MidiTime::ticksPerBar(), x = xCoordOfTick(tick)) + tick += TimePos::ticksPerBar(), x = xCoordOfTick(tick)) { p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } @@ -3117,9 +3117,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int pos_ticks = note->pos(); - int note_width = len_ticks * m_ppb / MidiTime::ticksPerBar(); + int note_width = len_ticks * m_ppb / TimePos::ticksPerBar(); const int x = ( pos_ticks - m_currentPosition ) * - m_ppb / MidiTime::ticksPerBar(); + m_ppb / TimePos::ticksPerBar(); // skip this note if not in visible area at all if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { @@ -3158,9 +3158,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int pos_ticks = note->pos(); - int note_width = len_ticks * m_ppb / MidiTime::ticksPerBar(); + int note_width = len_ticks * m_ppb / TimePos::ticksPerBar(); const int x = ( pos_ticks - m_currentPosition ) * - m_ppb / MidiTime::ticksPerBar(); + m_ppb / TimePos::ticksPerBar(); // skip this note if not in visible area at all if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { @@ -3245,9 +3245,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int pos_ticks = note->pos(); - int note_width = len_ticks * m_ppb / MidiTime::ticksPerBar(); + int note_width = len_ticks * m_ppb / TimePos::ticksPerBar(); const int x = ( pos_ticks - m_currentPosition ) * - m_ppb / MidiTime::ticksPerBar(); + m_ppb / TimePos::ticksPerBar(); // skip this note if not in visible area at all if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { @@ -3290,9 +3290,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // now draw selection-frame int x = ( ( sel_pos_start - m_currentPosition ) * m_ppb ) / - MidiTime::ticksPerBar(); + TimePos::ticksPerBar(); int w = ( ( ( sel_pos_end - m_currentPosition ) * m_ppb ) / - MidiTime::ticksPerBar() ) - x; + TimePos::ticksPerBar() ) - x; int y = (int) y_base - sel_key_start * m_keyLineHeight; int h = (int) y_base - sel_key_end * m_keyLineHeight - y; p.setPen(m_selectedNoteColor); @@ -3414,9 +3414,9 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) int pixel_range = 8; int x = position(we).x() - m_whiteKeyWidth; int ticks_start = ( x - pixel_range / 2 ) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; // When alt is pressed we only edit the note under the cursor bool altPressed = we->modifiers() & Qt::AltModifier; @@ -3517,7 +3517,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (position(we).x() - m_whiteKeyWidth) * MidiTime::ticksPerBar(); + int x = (position(we).x() - m_whiteKeyWidth) * TimePos::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -3728,7 +3728,7 @@ void PianoRoll::startRecordNote(const Note & n ) (Engine::getSong()->playMode() == desiredPlayModeForAccompany() || Engine::getSong()->playMode() == Song::Mode_PlayPattern )) { - MidiTime sub; + TimePos sub; if( Engine::getSong()->playMode() == Song::Mode_PlaySong ) { sub = m_pattern->startPosition(); @@ -3977,7 +3977,7 @@ void PianoRoll::copyToClipboard( const NoteVector & notes ) const QDomElement note_list = dataFile.createElement( "note-list" ); dataFile.content().appendChild( note_list ); - MidiTime start_pos( notes.front()->pos().getBar(), 0 ); + TimePos start_pos( notes.front()->pos().getBar(), 0 ); for( const Note *note : notes ) { Note clip_note( *note ); @@ -4108,18 +4108,18 @@ bool PianoRoll::deleteSelectedNotes() -void PianoRoll::autoScroll( const MidiTime & t ) +void PianoRoll::autoScroll( const TimePos & t ) { const int w = width() - m_whiteKeyWidth; - if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) + if( t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb ) { - m_leftRightScroll->setValue( t.getBar() * MidiTime::ticksPerBar() ); + m_leftRightScroll->setValue( t.getBar() * TimePos::ticksPerBar() ); } else if( t < m_currentPosition ) { - MidiTime t2 = qMax( t - w * MidiTime::ticksPerBar() * - MidiTime::ticksPerBar() / m_ppb, (tick_t) 0 ); - m_leftRightScroll->setValue( t2.getBar() * MidiTime::ticksPerBar() ); + TimePos t2 = qMax( t - w * TimePos::ticksPerBar() * + TimePos::ticksPerBar() / m_ppb, (tick_t) 0 ); + m_leftRightScroll->setValue( t2.getBar() * TimePos::ticksPerBar() ); } m_scrollBack = false; } @@ -4127,7 +4127,7 @@ void PianoRoll::autoScroll( const MidiTime & t ) -void PianoRoll::updatePosition( const MidiTime & t ) +void PianoRoll::updatePosition( const TimePos & t ) { if( ( Engine::getSong()->isPlaying() && Engine::getSong()->playMode() == Song::Mode_PlayPattern @@ -4136,7 +4136,7 @@ void PianoRoll::updatePosition( const MidiTime & t ) { autoScroll( t ); } - const int pos = m_timeLine->pos() * m_ppb / MidiTime::ticksPerBar(); + const int pos = m_timeLine->pos() * m_ppb / TimePos::ticksPerBar(); if (pos >= m_currentPosition && pos <= m_currentPosition + width() - m_whiteKeyWidth) { m_positionLine->show(); @@ -4157,14 +4157,14 @@ void PianoRoll::updatePositionLineHeight() -void PianoRoll::updatePositionAccompany( const MidiTime & t ) +void PianoRoll::updatePositionAccompany( const TimePos & t ) { Song * s = Engine::getSong(); if( m_recording && hasValidPattern() && s->playMode() != Song::Mode_PlayPattern ) { - MidiTime pos = t; + TimePos pos = t; if( s->playMode() != Song::Mode_PlayBB ) { pos -= m_pattern->startPosition(); @@ -4178,7 +4178,7 @@ void PianoRoll::updatePositionAccompany( const MidiTime & t ) } -void PianoRoll::updatePositionStepRecording( const MidiTime & t ) +void PianoRoll::updatePositionStepRecording( const TimePos & t ) { if( m_stepRecorder.isRecording() ) { @@ -4269,7 +4269,7 @@ void PianoRoll::quantizeNotes() for( Note* n : notes ) { - if( n->length() == MidiTime( 0 ) ) + if( n->length() == TimePos( 0 ) ) { continue; } @@ -4304,7 +4304,7 @@ void PianoRoll::updateSemiToneMarkerMenu() -MidiTime PianoRoll::newNoteLen() const +TimePos PianoRoll::newNoteLen() const { if( m_noteLenModel.value() == 0 ) { @@ -4340,7 +4340,7 @@ Note * PianoRoll::noteUnderMouse() int key_num = getKey( pos.y() ); int pos_ticks = (pos.x() - m_whiteKeyWidth) * - MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + TimePos::ticksPerBar() / m_ppb + m_currentPosition; // loop through whole note-vector... for( Note* const& note : m_pattern->notes() ) diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 8829bedfd22..39fa1f6276a 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -69,7 +69,7 @@ SongEditor::SongEditor( Song * song ) : m_scrollPos(), m_mousePos(), m_rubberBandStartTrackview(0), - m_rubberbandStartMidipos(0), + m_rubberbandStartTimePos(0), m_currentZoomingValue(m_zoomingModel->value()), m_trackHeadWidth(ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt()==1 ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT @@ -83,11 +83,11 @@ SongEditor::SongEditor( Song * song ) : m_song->m_playPos[Song::Mode_PlaySong], m_currentPosition, Song::Mode_PlaySong, this ); - connect( this, SIGNAL( positionChanged( const MidiTime & ) ), + connect( this, SIGNAL( positionChanged( const TimePos & ) ), m_song->m_playPos[Song::Mode_PlaySong].m_timeLine, - SLOT( updatePosition( const MidiTime & ) ) ); - connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ), - this, SLOT( updatePosition( const MidiTime & ) ) ); + SLOT( updatePosition( const TimePos & ) ) ); + connect( m_timeLine, SIGNAL( positionChanged( const TimePos & ) ), + this, SLOT( updatePosition( const TimePos & ) ) ); connect( m_timeLine, SIGNAL( regionSelectedFromPixels( int, int ) ), this, SLOT( selectRegionFromPixels( int, int ) ) ); connect( m_timeLine, SIGNAL( selectionFinished() ), @@ -339,7 +339,7 @@ void SongEditor::setHighQuality( bool hq ) void SongEditor::scrolled( int new_pos ) { update(); - emit positionChanged( m_currentPosition = MidiTime( new_pos, 0 ) ); + emit positionChanged( m_currentPosition = TimePos( new_pos, 0 ) ); } @@ -363,8 +363,8 @@ void SongEditor::selectRegionFromPixels(int xStart, int xEnd) m_currentZoomingValue = zoomingModel()->value(); //calculate the song position where the mouse was clicked - m_rubberbandStartMidipos = MidiTime((xStart - m_trackHeadWidth) - / pixelsPerBar() * MidiTime::ticksPerBar()) + m_rubberbandStartTimePos = TimePos((xStart - m_trackHeadWidth) + / pixelsPerBar() * TimePos::ticksPerBar()) + m_currentPosition; m_rubberBandStartTrackview = 0; } @@ -413,9 +413,9 @@ void SongEditor::updateRubberband() //the index of the TrackView the mouse is hover int rubberBandTrackview = trackIndexFromSelectionPoint(m_mousePos.y()); - //the miditime the mouse is hover - MidiTime rubberbandMidipos = MidiTime((qMin(m_mousePos.x(), width()) - m_trackHeadWidth) - / pixelsPerBar() * MidiTime::ticksPerBar()) + //the time position the mouse is hover + TimePos rubberbandTimePos = TimePos((qMin(m_mousePos.x(), width()) - m_trackHeadWidth) + / pixelsPerBar() * TimePos::ticksPerBar()) + m_currentPosition; //are tcos in the rect of selection? @@ -427,9 +427,9 @@ void SongEditor::updateRubberband() auto indexOfTrackView = trackViews().indexOf(tco->getTrackView()); bool isBeetweenRubberbandViews = indexOfTrackView >= qMin(m_rubberBandStartTrackview, rubberBandTrackview) && indexOfTrackView <= qMax(m_rubberBandStartTrackview, rubberBandTrackview); - bool isBeetweenRubberbandMidiPos = tco->getTrackContentObject()->endPosition() >= qMin(m_rubberbandStartMidipos, rubberbandMidipos) - && tco->getTrackContentObject()->startPosition() <= qMax(m_rubberbandStartMidipos, rubberbandMidipos); - it->setSelected(isBeetweenRubberbandViews && isBeetweenRubberbandMidiPos); + bool isBeetweenRubberbandTimePos = tco->getTrackContentObject()->endPosition() >= qMin(m_rubberbandStartTimePos, rubberbandTimePos) + && tco->getTrackContentObject()->startPosition() <= qMax(m_rubberbandStartTimePos, rubberbandTimePos); + it->setSelected(isBeetweenRubberbandViews && isBeetweenRubberbandTimePos); } } } @@ -476,7 +476,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) } else if( ke->key() == Qt::Key_Left ) { - tick_t t = m_song->currentTick() - MidiTime::ticksPerBar(); + tick_t t = m_song->currentTick() - TimePos::ticksPerBar(); if( t >= 0 ) { m_song->setPlayPos( t, Song::Mode_PlaySong ); @@ -484,7 +484,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) } else if( ke->key() == Qt::Key_Right ) { - tick_t t = m_song->currentTick() + MidiTime::ticksPerBar(); + tick_t t = m_song->currentTick() + TimePos::ticksPerBar(); if( t < MaxSongLength ) { m_song->setPlayPos( t, Song::Mode_PlaySong ); @@ -608,10 +608,10 @@ void SongEditor::mousePressEvent(QMouseEvent *me) rubberBand()->setGeometry(QRect(m_origin, QSize())); rubberBand()->show(); - //the trackView(index) and the miditime where the mouse was clicked + //the trackView(index) and the time position where the mouse was clicked m_rubberBandStartTrackview = trackIndexFromSelectionPoint(me->y()); - m_rubberbandStartMidipos = MidiTime((me->x() - m_trackHeadWidth) - / pixelsPerBar() * MidiTime::ticksPerBar()) + m_rubberbandStartTimePos = TimePos((me->x() - m_trackHeadWidth) + / pixelsPerBar() * TimePos::ticksPerBar()) + m_currentPosition; } QWidget::mousePressEvent(me); @@ -766,7 +766,7 @@ static inline void animateScroll( QScrollBar *scrollBar, int newVal, bool smooth -void SongEditor::updatePosition( const MidiTime & t ) +void SongEditor::updatePosition( const TimePos & t ) { int widgetWidth, trackOpWidth; if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() ) @@ -788,7 +788,7 @@ void SongEditor::updatePosition( const MidiTime & t ) const int w = width() - widgetWidth - trackOpWidth - contentWidget()->verticalScrollBar()->width(); // width of right scrollbar - if( t > m_currentPosition + w * MidiTime::ticksPerBar() / + if( t > m_currentPosition + w * TimePos::ticksPerBar() / pixelsPerBar() ) { animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); diff --git a/src/gui/widgets/StepRecorderWidget.cpp b/src/gui/widgets/StepRecorderWidget.cpp index d2157ef66bc..65deb0d0a8b 100644 --- a/src/gui/widgets/StepRecorderWidget.cpp +++ b/src/gui/widgets/StepRecorderWidget.cpp @@ -53,7 +53,7 @@ void StepRecorderWidget::setPixelsPerBar(int ppb) m_ppb = ppb; } -void StepRecorderWidget::setCurrentPosition(MidiTime currentPosition) +void StepRecorderWidget::setCurrentPosition(TimePos currentPosition) { m_currentPosition = currentPosition; } @@ -76,12 +76,12 @@ void StepRecorderWidget::setBottomMargin(const int marginBottom) m_marginBottom = marginBottom; } -void StepRecorderWidget::setStartPosition(MidiTime pos) +void StepRecorderWidget::setStartPosition(TimePos pos) { m_curStepStartPos = pos; } -void StepRecorderWidget::setEndPosition(MidiTime pos) +void StepRecorderWidget::setEndPosition(TimePos pos) { m_curStepEndPos = pos; emit positionChanged(m_curStepEndPos); @@ -93,7 +93,7 @@ void StepRecorderWidget::showHint() embed::getIconPixmap("hint")); } -void StepRecorderWidget::setStepsLength(MidiTime stepsLength) +void StepRecorderWidget::setStepsLength(TimePos stepsLength) { m_stepsLength = stepsLength; } @@ -109,7 +109,7 @@ void StepRecorderWidget::paintEvent(QPaintEvent * pe) //draw steps ruler painter.setPen(m_colorLineEnd); - MidiTime curPos = m_curStepEndPos; + TimePos curPos = m_curStepEndPos; int x = xCoordOfTick(curPos); while(x <= m_right) { @@ -138,7 +138,7 @@ void StepRecorderWidget::paintEvent(QPaintEvent * pe) int StepRecorderWidget::xCoordOfTick(int tick) { - return m_marginLeft + ((tick - m_currentPosition) * m_ppb / MidiTime::ticksPerBar()); + return m_marginLeft + ((tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar()); } @@ -151,7 +151,7 @@ void StepRecorderWidget::drawVerLine(QPainter* painter, int x, const QColor& col } } -void StepRecorderWidget::drawVerLine(QPainter* painter, const MidiTime& pos, const QColor& color, int top, int bottom) +void StepRecorderWidget::drawVerLine(QPainter* painter, const TimePos& pos, const QColor& color, int top, int bottom) { drawVerLine(painter, xCoordOfTick(pos), color, top, bottom); } diff --git a/src/tracks/AutomationTrack.cpp b/src/tracks/AutomationTrack.cpp index fe3622921d9..dfb0df480fb 100644 --- a/src/tracks/AutomationTrack.cpp +++ b/src/tracks/AutomationTrack.cpp @@ -40,7 +40,7 @@ AutomationTrack::AutomationTrack( TrackContainer* tc, bool _hidden ) : setName( tr( "Automation track" ) ); } -bool AutomationTrack::play( const MidiTime & time_start, const fpp_t _frames, +bool AutomationTrack::play( const TimePos & time_start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num ) { return false; @@ -57,7 +57,7 @@ TrackView * AutomationTrack::createView( TrackContainerView* tcv ) -TrackContentObject* AutomationTrack::createTCO(const MidiTime & pos) +TrackContentObject* AutomationTrack::createTCO(const TimePos & pos) { AutomationPattern* p = new AutomationPattern(this); p->movePosition(pos); @@ -119,11 +119,11 @@ void AutomationTrackView::dropEvent( QDropEvent * _de ) journallingObject( val.toInt() ) ); if( mod != NULL ) { - MidiTime pos = MidiTime( trackContainerView()-> + TimePos pos = TimePos( trackContainerView()-> currentPosition() + ( _de->pos().x() - getTrackContentWidget()->x() ) * - MidiTime::ticksPerBar() / + TimePos::ticksPerBar() / static_cast( trackContainerView()->pixelsPerBar() ) ) .toAbsoluteBar(); diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 5ec03db30b4..8de8437abd3 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -52,7 +52,7 @@ BBTCO::BBTCO( Track * _track ) : if( t > 0 ) { saveJournallingState( false ); - changeLength( MidiTime( t, 0 ) ); + changeLength( TimePos( t, 0 ) ); restoreJournallingState(); } setAutoResize( false ); @@ -208,7 +208,7 @@ void BBTCOView::paintEvent( QPaintEvent * ) p.setPen( c.darker( 200 ) ); bar_t t = Engine::getBBTrackContainer()->lengthOfBB( m_bbTCO->bbTrackIndex() ); - if( m_bbTCO->length() > MidiTime::ticksPerBar() && t > 0 ) + if( m_bbTCO->length() > TimePos::ticksPerBar() && t > 0 ) { for( int x = static_cast( t * pixelsPerBar() ); x < width() - 2; @@ -331,7 +331,7 @@ BBTrack::~BBTrack() // play _frames frames of given TCO within starting with _start -bool BBTrack::play( const MidiTime & _start, const fpp_t _frames, +bool BBTrack::play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _offset, int _tco_num ) { if( isMuted() ) @@ -352,8 +352,8 @@ bool BBTrack::play( const MidiTime & _start, const fpp_t _frames, return false; } - MidiTime lastPosition; - MidiTime lastLen; + TimePos lastPosition; + TimePos lastLen; for( tcoVector::iterator it = tcos.begin(); it != tcos.end(); ++it ) { if( !( *it )->isMuted() && @@ -382,7 +382,7 @@ TrackView * BBTrack::createView( TrackContainerView* tcv ) -TrackContentObject* BBTrack::createTCO(const MidiTime & pos) +TrackContentObject* BBTrack::createTCO(const TimePos & pos) { BBTCO* bbtco = new BBTCO(this); bbtco->movePosition(pos); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 4333627e807..a8c1343aae9 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -244,7 +244,7 @@ MidiEvent InstrumentTrack::applyMasterKey( const MidiEvent& event ) -void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) +void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { if( Engine::getSong()->isExporting() ) { @@ -267,7 +267,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti NotePlayHandleManager::acquire( this, offset, typeInfo::max() / 2, - Note( MidiTime(), MidiTime(), event.key(), event.volume( midiPort()->baseVelocity() ) ), + Note( TimePos(), TimePos(), event.key(), event.volume( midiPort()->baseVelocity() ) ), NULL, event.channel(), NotePlayHandle::OriginMidiInput ); m_notes[event.key()] = nph; @@ -332,7 +332,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti nph->OriginMidiInput) { nph->setLength( - MidiTime( static_cast( + TimePos( static_cast( nph->totalFramesPlayed() / Engine::framesPerTick() ) ) ); midiNoteOff( *nph ); @@ -385,7 +385,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti -void InstrumentTrack::processOutEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset ) +void InstrumentTrack::processOutEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { // do nothing if we do not have an instrument instance (e.g. when loading settings) if( m_instrument == NULL ) @@ -608,7 +608,7 @@ void InstrumentTrack::removeMidiPortNode( DataFile & _dataFile ) -bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, +bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _offset, int _tco_num ) { if( ! m_instrument || ! tryLock() ) @@ -638,7 +638,7 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, for( NotePlayHandleList::Iterator it = m_processHandles.begin(); it != m_processHandles.end(); ++it ) { - ( *it )->processMidiTime( _start ); + ( *it )->processTimePos( _start ); } if ( tcos.size() == 0 ) @@ -660,7 +660,7 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, { continue; } - MidiTime cur_start = _start; + TimePos cur_start = _start; if( _tco_num < 0 ) { cur_start -= p->startPosition(); @@ -713,7 +713,7 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, -TrackContentObject* InstrumentTrack::createTCO(const MidiTime & pos) +TrackContentObject* InstrumentTrack::createTCO(const TimePos & pos) { Pattern* p = new Pattern(this); p->movePosition(pos); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 69d575813b6..ded78ffa8c6 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -56,7 +56,7 @@ Pattern::Pattern( InstrumentTrack * _instrument_track ) : TrackContentObject( _instrument_track ), m_instrumentTrack( _instrument_track ), m_patternType( BeatPattern ), - m_steps( MidiTime::stepsPerBar() ) + m_steps( TimePos::stepsPerBar() ) { if( _instrument_track->trackContainer() == Engine::getBBTrackContainer() ) @@ -160,7 +160,7 @@ void Pattern::updateLength() return; } - tick_t max_length = MidiTime::ticksPerBar(); + tick_t max_length = TimePos::ticksPerBar(); for( NoteVector::ConstIterator it = m_notes.begin(); it != m_notes.end(); ++it ) @@ -171,17 +171,17 @@ void Pattern::updateLength() ( *it )->endPos() ); } } - changeLength( MidiTime( max_length ).nextFullBar() * - MidiTime::ticksPerBar() ); + changeLength( TimePos( max_length ).nextFullBar() * + TimePos::ticksPerBar() ); updateBBTrack(); } -MidiTime Pattern::beatPatternLength() const +TimePos Pattern::beatPatternLength() const { - tick_t max_length = MidiTime::ticksPerBar(); + tick_t max_length = TimePos::ticksPerBar(); for( NoteVector::ConstIterator it = m_notes.begin(); it != m_notes.end(); ++it ) @@ -193,13 +193,13 @@ MidiTime Pattern::beatPatternLength() const } } - if( m_steps != MidiTime::stepsPerBar() ) + if( m_steps != TimePos::stepsPerBar() ) { - max_length = m_steps * MidiTime::ticksPerBar() / - MidiTime::stepsPerBar(); + max_length = m_steps * TimePos::ticksPerBar() / + TimePos::stepsPerBar(); } - return MidiTime( max_length ).nextFullBar() * MidiTime::ticksPerBar(); + return TimePos( max_length ).nextFullBar() * TimePos::ticksPerBar(); } @@ -258,7 +258,7 @@ Note * Pattern::noteAtStep( int _step ) for( NoteVector::Iterator it = m_notes.begin(); it != m_notes.end(); ++it ) { - if( ( *it )->pos() == MidiTime::stepPosition( _step ) + if( ( *it )->pos() == TimePos::stepPosition( _step ) && ( *it )->length() < 0 ) { return *it; @@ -297,8 +297,8 @@ void Pattern::clearNotes() Note * Pattern::addStepNote( int step ) { - return addNote( Note( MidiTime( -DefaultTicksPerBar ), - MidiTime::stepPosition( step ) ), false ); + return addNote( Note( TimePos( -DefaultTicksPerBar ), + TimePos::stepPosition( step ) ), false ); } @@ -428,7 +428,7 @@ void Pattern::loadSettings( const QDomElement & _this ) m_steps = _this.attribute( "steps" ).toInt(); if( m_steps == 0 ) { - m_steps = MidiTime::stepsPerBar(); + m_steps = TimePos::stepsPerBar(); } checkType(); @@ -477,7 +477,7 @@ void Pattern::clear() void Pattern::addSteps() { - m_steps += MidiTime::stepsPerBar(); + m_steps += TimePos::stepsPerBar(); updateLength(); emit dataChanged(); } @@ -508,7 +508,7 @@ void Pattern::cloneSteps() void Pattern::removeSteps() { - int n = MidiTime::stepsPerBar(); + int n = TimePos::stepsPerBar(); if( n < m_steps ) { for( int i = m_steps - n; i < m_steps; ++i ) @@ -566,19 +566,19 @@ bool Pattern::empty() void Pattern::changeTimeSignature() { - MidiTime last_pos = MidiTime::ticksPerBar() - 1; + TimePos last_pos = TimePos::ticksPerBar() - 1; for( NoteVector::ConstIterator cit = m_notes.begin(); cit != m_notes.end(); ++cit ) { if( ( *cit )->length() < 0 && ( *cit )->pos() > last_pos ) { - last_pos = ( *cit )->pos()+MidiTime::ticksPerBar() / - MidiTime::stepsPerBar(); + last_pos = ( *cit )->pos()+TimePos::ticksPerBar() / + TimePos::stepsPerBar(); } } - last_pos = last_pos.nextFullBar() * MidiTime::ticksPerBar(); - m_steps = qMax( MidiTime::stepsPerBar(), - last_pos.getBar() * MidiTime::stepsPerBar() ); + last_pos = last_pos.nextFullBar() * TimePos::ticksPerBar(); + m_steps = qMax( TimePos::stepsPerBar(), + last_pos.getBar() * TimePos::stepsPerBar() ); updateLength(); } @@ -923,7 +923,7 @@ void PatternView::paintEvent( QPaintEvent * ) // Length of one bar/beat in the [0,1] x [0,1] coordinate system const float barLength = 1. / m_pat->length().getBar(); - const float tickLength = barLength / MidiTime::ticksPerBar(); + const float tickLength = barLength / TimePos::ticksPerBar(); const int x_base = TCO_BORDER_WIDTH; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 197e3fcf1a9..31768de75da 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -123,7 +123,7 @@ SampleTCO::~SampleTCO() -void SampleTCO::changeLength( const MidiTime & _length ) +void SampleTCO::changeLength( const TimePos & _length ) { TrackContentObject::changeLength( qMax( static_cast( _length ), 1 ) ); } @@ -231,7 +231,7 @@ void SampleTCO::updateLength() -MidiTime SampleTCO::sampleLength() const +TimePos SampleTCO::sampleLength() const { return (int)( m_sampleBuffer->frames() / Engine::framesPerTick() ); } @@ -681,7 +681,7 @@ SampleTrack::~SampleTrack() -bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, +bool SampleTrack::play( const TimePos & _start, const fpp_t _frames, const f_cnt_t _offset, int _tco_num ) { m_audioPort.effects()->startRunning(); @@ -788,7 +788,7 @@ TrackView * SampleTrack::createView( TrackContainerView* tcv ) -TrackContentObject * SampleTrack::createTCO(const MidiTime & pos) +TrackContentObject * SampleTrack::createTCO(const TimePos & pos) { SampleTCO * sTco = new SampleTCO(this); sTco->movePosition(pos); @@ -1023,10 +1023,10 @@ void SampleTrackView::dropEvent(QDropEvent *de) ? trackHeadWidth : de->pos().x(); - MidiTime tcoPos = trackContainerView()->fixedTCOs() - ? MidiTime(0) - : MidiTime(((xPos - trackHeadWidth) / trackContainerView()->pixelsPerBar() - * MidiTime::ticksPerBar()) + trackContainerView()->currentPosition() + TimePos tcoPos = trackContainerView()->fixedTCOs() + ? TimePos(0) + : TimePos(((xPos - trackHeadWidth) / trackContainerView()->pixelsPerBar() + * TimePos::ticksPerBar()) + trackContainerView()->currentPosition() ).quantize(1.0); SampleTCO * sTco = static_cast(getTrack()->createTCO(tcoPos)); diff --git a/tests/src/tracks/AutomationTrackTest.cpp b/tests/src/tracks/AutomationTrackTest.cpp index 291ae293ef1..f86bfe44d2b 100644 --- a/tests/src/tracks/AutomationTrackTest.cpp +++ b/tests/src/tracks/AutomationTrackTest.cpp @@ -141,20 +141,20 @@ private slots: dynamic_cast(Track::create(Track::InstrumentTrack, song)); Pattern* notePattern = dynamic_cast(instrumentTrack->createTCO(0)); - notePattern->changeLength(MidiTime(4, 0)); - Note* note = notePattern->addNote(Note(MidiTime(4, 0)), false); + notePattern->changeLength(TimePos(4, 0)); + Note* note = notePattern->addNote(Note(TimePos(4, 0)), false); note->createDetuning(); DetuningHelper* dh = note->detuning(); auto pattern = dh->automationPattern(); pattern->setProgressionType( AutomationPattern::LinearProgression ); - pattern->putValue(MidiTime(0, 0), 0.0); - pattern->putValue(MidiTime(4, 0), 1.0); + pattern->putValue(TimePos(0, 0), 0.0); + pattern->putValue(TimePos(4, 0), 1.0); - QCOMPARE(pattern->valueAt(MidiTime(0, 0)), 0.0f); - QCOMPARE(pattern->valueAt(MidiTime(1, 0)), 0.25f); - QCOMPARE(pattern->valueAt(MidiTime(2, 0)), 0.5f); - QCOMPARE(pattern->valueAt(MidiTime(4, 0)), 1.0f); + QCOMPARE(pattern->valueAt(TimePos(0, 0)), 0.0f); + QCOMPARE(pattern->valueAt(TimePos(1, 0)), 0.25f); + QCOMPARE(pattern->valueAt(TimePos(2, 0)), 0.5f); + QCOMPARE(pattern->valueAt(TimePos(4, 0)), 1.0f); } void testBBTrack() @@ -186,12 +186,12 @@ private slots: QVERIFY(! bbContainer->automatedValuesAt(5, bbTrack2.index()).size()); BBTCO tco(&bbTrack); - tco.changeLength(MidiTime::ticksPerBar() * 2); + tco.changeLength(TimePos::ticksPerBar() * 2); tco.movePosition(0); QCOMPARE(song->automatedValuesAt(0)[&model], 0.0f); QCOMPARE(song->automatedValuesAt(5)[&model], 0.5f); - QCOMPARE(song->automatedValuesAt(MidiTime::ticksPerBar() + 5)[&model], 0.5f); + QCOMPARE(song->automatedValuesAt(TimePos::ticksPerBar() + 5)[&model], 0.5f); } void testGlobalAutomation() From c49ca376bf2554ba71d43c1aa52ddb88bda967ff Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 30 Nov 2020 16:48:26 +0900 Subject: [PATCH 162/180] Fix crash on OGG export with Qt >= 5.10 (#5813) --- src/core/audio/AudioFileOgg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index ce506f2e17c..7e1cb48b4a8 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -139,7 +139,7 @@ bool AudioFileOgg::startEncoding() // We give our ogg file a random serial number and avoid // 0 and UINT32_MAX which can get you into trouble. #if (QT_VERSION >= QT_VERSION_CHECK(5,10,0)) - QRandomGenerator::global()->seed(time(0)); + // QRandomGenerator::global() is already initialized, and we can't seed() it. m_serialNo = 0xD0000000 + QRandomGenerator::global()->generate() % 0x0FFFFFFF; #else qsrand(time(0)); From aff2ebcce0abee8d2d795ce1d374f6650af93e7d Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 1 Dec 2020 11:01:39 +0900 Subject: [PATCH 163/180] Update CIs to macOS 10.14 and XCode 10.3 --- .circleci/config.yml | 2 +- .travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 408fef721a1..2d15efd1e10 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -170,7 +170,7 @@ jobs: environment: <<: *common_environment macos: - xcode: "9.4.1" + xcode: "10.3.0" steps: - checkout - *init diff --git a/.travis.yml b/.travis.yml index 765fdb14cdd..d2d92d897ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ matrix: git: depth: false - os: osx - osx_image: xcode9.4 + osx_image: xcode10.3 before_install: # appdmg doesn't work with old Node.js - if [ "$TRAVIS_OS_NAME" = osx ]; then nvm install 10; fi From d73ede58a3109db8d3dd87587e3c01649cea27ac Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 1 Dec 2020 11:03:58 +0900 Subject: [PATCH 164/180] Remove instrument track window caching to fix related bugs (#5808) --- include/InstrumentTrack.h | 10 +-- src/core/Song.cpp | 2 - src/gui/GuiApplication.cpp | 1 - src/tracks/InstrumentTrack.cpp | 107 +++++++-------------------------- 4 files changed, 25 insertions(+), 95 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index b4bac9136a7..2ec5c287ae8 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -42,7 +42,6 @@ class QLineEdit; -template class QQueue; class InstrumentFunctionArpeggioView; class InstrumentFunctionNoteStackingView; class EffectRackView; @@ -324,10 +323,6 @@ class InstrumentTrackView : public TrackView return m_midiMenu; } - void freeInstrumentTrackWindow(); - - static void cleanupWindowCache(); - // Create a menu for assigning/creating channels for this track QMenu * createFxMenu( QString title, QString newFxLabel ) override; @@ -349,12 +344,12 @@ private slots: void assignFxLine( int channelIndex ); void createFxLine(); + void handleConfigChange(QString cls, QString attr, QString value); + private: InstrumentTrackWindow * m_window; - static QQueue s_windowCache; - // widgets in track-settings-widget TrackLabelButton * m_tlb; Knob * m_volumeKnob; @@ -482,6 +477,7 @@ protected slots: PianoView * m_pianoView; friend class InstrumentView; + friend class InstrumentTrackView; } ; diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 0ae0cc887a8..78fb7d44aa4 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -884,8 +884,6 @@ void Song::clearProject() Engine::projectJournal()->clearJournal(); Engine::projectJournal()->setJournalling( true ); - - InstrumentTrackView::cleanupWindowCache(); } diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index dbd95cdfc25..5170ffb8c44 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -163,7 +163,6 @@ GuiApplication::GuiApplication() GuiApplication::~GuiApplication() { - InstrumentTrackView::cleanupWindowCache(); s_instance = nullptr; } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a8c1343aae9..2f4a3bc985c 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -25,7 +25,6 @@ #include "InstrumentTrack.h" #include -#include #include #include #include @@ -79,7 +78,6 @@ const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; const int PIANO_HEIGHT = 80; -const int INSTRUMENT_WINDOW_CACHE_SIZE = 8; // #### IT: @@ -951,9 +949,6 @@ void InstrumentTrack::autoAssignMidiDevice(bool assign) // #### ITV: -QQueue InstrumentTrackView::s_windowCache; - - InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerView* tcv ) : TrackView( _it, tcv ), @@ -975,6 +970,9 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV connect( _it, SIGNAL( nameChanged() ), m_tlb, SLOT( update() ) ); + connect(ConfigManager::inst(), SIGNAL(valueChanged(QString, QString, QString)), + this, SLOT(handleConfigChange(QString, QString, QString))); + // creation of widgets for track-settings-widget int widgetWidth; if( ConfigManager::inst()->value( "ui", @@ -1066,7 +1064,8 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV InstrumentTrackView::~InstrumentTrackView() { - freeInstrumentTrackWindow(); + delete m_window; + m_window = nullptr; delete model()->m_midiPort.m_readablePortsMenu; delete model()->m_midiPort.m_writablePortsMenu; @@ -1119,89 +1118,26 @@ void InstrumentTrackView::assignFxLine(int channelIndex) -// TODO: Add windows to free list on freeInstrumentTrackWindow. -// But, don't NULL m_window or disconnect signals. This will allow windows -// that are being show/hidden frequently to stay connected. -void InstrumentTrackView::freeInstrumentTrackWindow() +InstrumentTrackWindow * InstrumentTrackView::getInstrumentTrackWindow() { - if( m_window != NULL ) + if (!m_window) { - m_lastPos = m_window->parentWidget()->pos(); - - if( ConfigManager::inst()->value( "ui", - "oneinstrumenttrackwindow" ).toInt() || - s_windowCache.count() < INSTRUMENT_WINDOW_CACHE_SIZE ) - { - model()->setHook( NULL ); - m_window->setInstrumentTrackView( NULL ); - m_window->parentWidget()->hide(); - m_window->updateInstrumentView(); - s_windowCache << m_window; - } - else - { - delete m_window; - } - - m_window = NULL; + m_window = new InstrumentTrackWindow(this); } -} - - - -void InstrumentTrackView::cleanupWindowCache() -{ - while( !s_windowCache.isEmpty() ) - { - delete s_windowCache.dequeue(); - } + return m_window; } - - - -InstrumentTrackWindow * InstrumentTrackView::getInstrumentTrackWindow() +void InstrumentTrackView::handleConfigChange(QString cls, QString attr, QString value) { - if( m_window != NULL ) - { - } - else if( !s_windowCache.isEmpty() ) + // When one instrument track window mode is turned on, + // close windows except last opened one. + if (cls == "ui" && attr == "oneinstrumenttrackwindow" && value.toInt()) { - m_window = s_windowCache.dequeue(); - - m_window->setInstrumentTrackView( this ); - m_window->setModel( model() ); - m_window->updateInstrumentView(); - model()->setHook( m_window ); - - if( ConfigManager::inst()-> - value( "ui", "oneinstrumenttrackwindow" ).toInt() ) - { - s_windowCache << m_window; - } - else if( m_lastPos.x() > 0 || m_lastPos.y() > 0 ) - { - m_window->parentWidget()->move( m_lastPos ); - } + m_tlb->setChecked(m_window && m_window == topLevelInstrumentTrackWindow()); } - else - { - m_window = new InstrumentTrackWindow( this ); - if( ConfigManager::inst()-> - value( "ui", "oneinstrumenttrackwindow" ).toInt() ) - { - // first time, an InstrumentTrackWindow is opened - s_windowCache << m_window; - } - } - - return m_window; } - - - void InstrumentTrackView::dragEnterEvent( QDragEnterEvent * _dee ) { InstrumentTrackWindow::dragEnterEventGeneric( _dee ); @@ -1225,12 +1161,15 @@ void InstrumentTrackView::dropEvent( QDropEvent * _de ) void InstrumentTrackView::toggleInstrumentWindow( bool _on ) { - getInstrumentTrackWindow()->toggleVisibility( _on ); - - if( !_on ) + if (_on && ConfigManager::inst()->value("ui", "oneinstrumenttrackwindow").toInt()) { - freeInstrumentTrackWindow(); + if (topLevelInstrumentTrackWindow()) + { + topLevelInstrumentTrackWindow()->m_itv->m_tlb->setChecked(false); + } } + + getInstrumentTrackWindow()->toggleVisibility( _on ); } @@ -1547,11 +1486,9 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : InstrumentTrackWindow::~InstrumentTrackWindow() { - InstrumentTrackView::s_windowCache.removeAll( this ); - delete m_instrumentView; - if( gui->mainWindow()->workspace() ) + if (parentWidget()) { parentWidget()->hide(); parentWidget()->deleteLater(); From d6b9853426e866e1fe903461fd49ec4b8438c2bb Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 1 Dec 2020 19:34:33 +0900 Subject: [PATCH 165/180] Tests: use C++14 as well as LMMS --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ddebe116c6e..643c7958518 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") INCLUDE_DIRECTORIES("${CMAKE_BINARY_DIR}") INCLUDE_DIRECTORIES("${CMAKE_BINARY_DIR}/src") -SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD 14) SET(CMAKE_AUTOMOC ON) From 9f0dc0fb1bbdcf24dd5648a0e1db8f6c6180cba6 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 1 Dec 2020 19:36:17 +0900 Subject: [PATCH 166/180] Work around build failures for tests on macOS >= 10.14 This one should be removed once we export the include directory for lmms to tests properly. --- tests/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 643c7958518..74ebba940f2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,11 @@ SET(CMAKE_CXX_STANDARD 14) SET(CMAKE_AUTOMOC ON) +# FIXME: remove this once we export include directories for LMMS +IF(LMMS_BUILD_APPLE) +INCLUDE_DIRECTORIES("/usr/local/include") +ENDIF() + ADD_EXECUTABLE(tests EXCLUDE_FROM_ALL main.cpp From 4f74151f006c95d5bed933a9c421521545c1391b Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Tue, 1 Dec 2020 10:38:04 +0000 Subject: [PATCH 167/180] Fix export when rendering looped section multiple times (#5814) Co-authored-by: Hyunjin Song --- src/core/Song.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 78fb7d44aa4..30d0bea23fb 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -295,7 +295,7 @@ void Song::processNextBuffer() } // Handle loop points, and inform VST plugins of the loop status - if (loopEnabled || m_loopRenderRemaining > 1) + if (loopEnabled || (m_loopRenderRemaining > 1 && getPlayPos() >= timeline->loopBegin())) { m_vstSyncController.startCycle( timeline->loopBegin().getTicks(), timeline->loopEnd().getTicks()); From 3c36365afa87128184e020d8b628e1933401f247 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Tue, 1 Dec 2020 22:27:37 -0300 Subject: [PATCH 168/180] Adds support for MIDI CC events inside LMMS (#5581) --- data/themes/classic/midi_cc_rack.png | Bin 0 -> 554 bytes data/themes/default/midi_cc_rack.png | Bin 0 -> 554 bytes include/AutomatableModelView.h | 1 + include/InstrumentTrack.h | 11 ++- include/MidiCCRackView.h | 40 ++++++++ include/MidiEvent.h | 29 ++++-- include/ModelView.h | 1 + src/gui/AutomatableModelView.cpp | 25 +++++ src/gui/CMakeLists.txt | 1 + src/gui/MidiCCRackView.cpp | 133 +++++++++++++++++++++++++++ src/gui/ModelView.cpp | 10 ++ src/tracks/InstrumentTrack.cpp | 96 ++++++++++++++++++- 12 files changed, 336 insertions(+), 11 deletions(-) create mode 100644 data/themes/classic/midi_cc_rack.png create mode 100644 data/themes/default/midi_cc_rack.png create mode 100644 include/MidiCCRackView.h create mode 100644 src/gui/MidiCCRackView.cpp diff --git a/data/themes/classic/midi_cc_rack.png b/data/themes/classic/midi_cc_rack.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe8bb9cb2479575cc5ecbcc0e32fc2bfca58ad4 GIT binary patch literal 554 zcmV+_0@eMAP)ZX-A6Rldc2v;E`1WB&jL~cdg zL{LH!F5I*&NTdj&&(K$Bmqs)ZYD6f7e;~+OiIb9#i@DD37Cfw;knbtv=GETFNTIkJcmlsdw?8`y<|xvc@u)%(CH zV6p|^sQMLH0w#dPX!%yKvY|dx=bG4^SD&gYA$BtY%lYV4&*xa1`lbPFS?$O*6?H2= z2cJz!X$^P+>;NBgY!|2ko4^z>4r~A)QcAU4vkLqG9;cMn4-G`^RIBPewO#E~zpHmc z&2V;dMNR66`c^&JaLUX7;bN2dn2&5sHP!a{vGU07*qoM6N<$g3q||Hvj+t literal 0 HcmV?d00001 diff --git a/data/themes/default/midi_cc_rack.png b/data/themes/default/midi_cc_rack.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe8bb9cb2479575cc5ecbcc0e32fc2bfca58ad4 GIT binary patch literal 554 zcmV+_0@eMAP)ZX-A6Rldc2v;E`1WB&jL~cdg zL{LH!F5I*&NTdj&&(K$Bmqs)ZYD6f7e;~+OiIb9#i@DD37Cfw;knbtv=GETFNTIkJcmlsdw?8`y<|xvc@u)%(CH zV6p|^sQMLH0w#dPX!%yKvY|dx=bG4^SD&gYA$BtY%lYV4&*xa1`lbPFS?$O*6?H2= z2cJz!X$^P+>;NBgY!|2ko4^z>4r~A)QcAU4vkLqG9;cMn4-G`^RIBPewO#E~zpHmc z&2V;dMNR66`c^&JaLUX7;bN2dn2&5sHP!a{vGU07*qoM6N<$g3q||Hvj+t literal 0 HcmV?d00001 diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h index 029ea16fba7..a5996118595 100644 --- a/include/AutomatableModelView.h +++ b/include/AutomatableModelView.h @@ -50,6 +50,7 @@ class LMMS_EXPORT AutomatableModelView : public ModelView } void setModel( Model* model, bool isOldModelValid = true ) override; + void unsetModel() override; template inline T value() const diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 2ec5c287ae8..50e7c4a2554 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -30,6 +30,8 @@ #include "GroupBox.h" #include "InstrumentFunctions.h" #include "InstrumentSoundShaping.h" +#include "Midi.h" +#include "MidiCCRackView.h" #include "MidiEventProcessor.h" #include "MidiPort.h" #include "NotePlayHandle.h" @@ -228,7 +230,6 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor void newNote(); void endNote(); - protected: QString nodeName() const override { @@ -247,6 +248,8 @@ protected slots: private: + void processCCEvent(int controller); + MidiPort m_midiPort; NotePlayHandle* m_notes[NumKeys]; @@ -286,11 +289,14 @@ protected slots: Piano m_piano; + std::unique_ptr m_midiCCEnable; + std::unique_ptr m_midiCCModel[MidiControllerCount]; friend class InstrumentTrackView; friend class InstrumentTrackWindow; friend class NotePlayHandle; friend class InstrumentMiscView; + friend class MidiCCRackView; } ; @@ -334,6 +340,7 @@ class InstrumentTrackView : public TrackView private slots: void toggleInstrumentWindow( bool _on ); + void toggleMidiCCRack(); void activityIndicatorPressed(); void activityIndicatorReleased(); @@ -361,6 +368,8 @@ private slots: QAction * m_midiInputAction; QAction * m_midiOutputAction; + std::unique_ptr m_midiCCRackView; + QPoint m_lastPos; FadeButton * getActivityIndicator() override diff --git a/include/MidiCCRackView.h b/include/MidiCCRackView.h new file mode 100644 index 00000000000..9d015e4e8fb --- /dev/null +++ b/include/MidiCCRackView.h @@ -0,0 +1,40 @@ +#ifndef MIDI_CC_RACK_VIEW_H +#define MIDI_CC_RACK_VIEW_H + +#include + +#include "GroupBox.h" +#include "Knob.h" +#include "Midi.h" +#include "SerializingObject.h" + +class InstrumentTrack; + +class MidiCCRackView : public QWidget, public SerializingObject +{ + Q_OBJECT +public: + MidiCCRackView(InstrumentTrack * track); + ~MidiCCRackView() override; + + void saveSettings(QDomDocument & doc, QDomElement & parent) override; + void loadSettings(const QDomElement &) override; + + inline QString nodeName() const override + { + return "MidiCCRackView"; + } + +private slots: + void renameWindow(); + +private: + InstrumentTrack *m_track; + + GroupBox *m_midiCCGroupBox; // MIDI CC GroupBox (used to enable disable MIDI CC) + + Knob *m_controllerKnob[MidiControllerCount]; // Holds the knob widgets for each controller + +}; + +#endif diff --git a/include/MidiEvent.h b/include/MidiEvent.h index 907314b4866..177ef75ea94 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -33,27 +33,30 @@ class MidiEvent { public: - MidiEvent( MidiEventTypes type = MidiActiveSensing, + MidiEvent(MidiEventTypes type = MidiActiveSensing, int8_t channel = 0, int16_t param1 = 0, int16_t param2 = 0, - const void* sourcePort = NULL ) : + const void* sourcePort = nullptr, + bool ignoreOnExport = true) : m_type( type ), m_metaEvent( MidiMetaInvalid ), m_channel( channel ), m_sysExData( NULL ), - m_sourcePort( sourcePort ) + m_sourcePort(sourcePort), + m_ignoreOnExport(ignoreOnExport) { m_data.m_param[0] = param1; m_data.m_param[1] = param2; } - MidiEvent( MidiEventTypes type, const char* sysExData, int dataLen ) : + MidiEvent(MidiEventTypes type, const char* sysExData, int dataLen, bool ignoreOnExport = true) : m_type( type ), m_metaEvent( MidiMetaInvalid ), m_channel( 0 ), m_sysExData( sysExData ), - m_sourcePort( NULL ) + m_sourcePort(nullptr), + m_ignoreOnExport(ignoreOnExport) { m_data.m_sysExDataLen = dataLen; } @@ -64,7 +67,8 @@ class MidiEvent m_channel( other.m_channel ), m_data( other.m_data ), m_sysExData( other.m_sysExData ), - m_sourcePort( other.m_sourcePort ) + m_sourcePort(other.m_sourcePort), + m_ignoreOnExport(other.m_ignoreOnExport) { } @@ -190,6 +194,16 @@ class MidiEvent setParam( 0, pitchBend ); } + bool ignoreOnExport() const + { + return m_ignoreOnExport; + } + + void setIgnoreOnExport(bool value) + { + m_ignoreOnExport = value; + } + private: MidiEventTypes m_type; // MIDI event type @@ -205,6 +219,9 @@ class MidiEvent const char* m_sysExData; const void* m_sourcePort; + // This helps us ignore MIDI events that shouldn't be processed + // during a project export, like physical controller events. + bool m_ignoreOnExport; } ; #endif diff --git a/include/ModelView.h b/include/ModelView.h index 907cd14efdf..04229ed0d2f 100644 --- a/include/ModelView.h +++ b/include/ModelView.h @@ -36,6 +36,7 @@ class LMMS_EXPORT ModelView virtual ~ModelView(); virtual void setModel( Model* model, bool isOldModelValid = true ); + virtual void unsetModel(); Model* model() { diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index 6a74ada90fa..efd6967cf18 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -142,6 +142,31 @@ void AutomatableModelView::setModel( Model* model, bool isOldModelValid ) +// Unsets the current model by setting a dummy empty model. The dummy model is marked as +// "defaultConstructed", so the next call to setModel will delete it. +void AutomatableModelView::unsetModel() +{ + if (dynamic_cast(this)) + { + setModel(new FloatModel(0, 0, 0, 1, nullptr, QString(), true)); + } + else if (dynamic_cast(this)) + { + setModel(new IntModel(0, 0, 0, nullptr, QString(), true)); + } + else if (dynamic_cast(this)) + { + setModel(new BoolModel(false, nullptr, QString(), true)); + } + else + { + ModelView::unsetModel(); + } +} + + + + void AutomatableModelView::mousePressEvent( QMouseEvent* event ) { if( event->button() == Qt::LeftButton && event->modifiers() & Qt::ControlModifier ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e1be929aab1..193c0b9012c 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -22,6 +22,7 @@ SET(LMMS_SRCS gui/Lv2ViewBase.cpp gui/MainApplication.cpp gui/MainWindow.cpp + gui/MidiCCRackView.cpp gui/MidiSetupWidget.cpp gui/ModelView.cpp gui/PeakControllerDialog.cpp diff --git a/src/gui/MidiCCRackView.cpp b/src/gui/MidiCCRackView.cpp new file mode 100644 index 00000000000..4d932fd1c5d --- /dev/null +++ b/src/gui/MidiCCRackView.cpp @@ -0,0 +1,133 @@ +/* + * MidiCCRackView.cpp - implementation of the MIDI CC rack widget + * + * Copyright (c) 2020 Ian Caio + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "MidiCCRackView.h" + +#include +#include +#include +#include +#include + +#include "embed.h" +#include "GroupBox.h" +#include "GuiApplication.h" +#include "InstrumentTrack.h" +#include "Knob.h" +#include "MainWindow.h" +#include "Track.h" + + +MidiCCRackView::MidiCCRackView(InstrumentTrack * track) : + QWidget(), + m_track(track) +{ + setWindowIcon(embed::getIconPixmap("midi_cc_rack")); + setWindowTitle(tr("MIDI CC Rack - %1").arg(m_track->name())); + + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget(this); + + // Remove maximize button + Qt::WindowFlags flags = subWin->windowFlags(); + flags &= ~Qt::WindowMaximizeButtonHint; + subWin->setWindowFlags(flags); + + // Adjust window attributes, sizing and position + subWin->setAttribute(Qt::WA_DeleteOnClose, false); + subWin->resize(350, 300); + subWin->setFixedWidth(350); + subWin->setMinimumHeight(300); + subWin->hide(); + + // Main window layout + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + // Knobs GroupBox - Here we have the MIDI CC controller knobs for the selected track + m_midiCCGroupBox = new GroupBox(tr("MIDI CC Knobs:")); + + // Layout to keep scrollable area under the GroupBox header + QVBoxLayout *knobsGroupBoxLayout = new QVBoxLayout(); + knobsGroupBoxLayout->setContentsMargins(5, 16, 5, 5); + + m_midiCCGroupBox->setLayout(knobsGroupBoxLayout); + + // Scrollable area + widget + its layout that will have all the knobs + QScrollArea *knobsScrollArea = new QScrollArea(); + QWidget *knobsArea = new QWidget(); + QGridLayout *knobsAreaLayout = new QGridLayout(); + + knobsArea->setLayout(knobsAreaLayout); + knobsScrollArea->setWidget(knobsArea); + knobsScrollArea->setWidgetResizable(true); + + knobsGroupBoxLayout->addWidget(knobsScrollArea); + + // Adds the controller knobs + for (int i = 0; i < MidiControllerCount; ++i) + { + m_controllerKnob[i] = new Knob(knobBright_26); + m_controllerKnob[i]->setLabel(tr("CC %1").arg(i)); + knobsAreaLayout->addWidget(m_controllerKnob[i], i/4, i%4); + } + + // Set all the models + // Set the LED button to enable/disable the track midi cc + m_midiCCGroupBox->setModel(m_track->m_midiCCEnable.get()); + + // Set the model for each Knob + for (int i = 0; i < MidiControllerCount; ++i) + { + m_controllerKnob[i]->setModel(m_track->m_midiCCModel[i].get()); + } + + // Connection to update the name of the track on the label + connect(m_track, SIGNAL(nameChanged()), + this, SLOT(renameWindow())); + + // Adding everything to the main layout + mainLayout->addWidget(m_midiCCGroupBox); +} + +MidiCCRackView::~MidiCCRackView() +{ + if(parentWidget()) + { + parentWidget()->hide(); + parentWidget()->deleteLater(); + } +} + +void MidiCCRackView::renameWindow() +{ + setWindowTitle(tr("MIDI CC Rack - %1").arg(m_track->name())); +} + +void MidiCCRackView::saveSettings(QDomDocument & doc, QDomElement & parent) +{ +} + +void MidiCCRackView::loadSettings(const QDomElement &) +{ +} diff --git a/src/gui/ModelView.cpp b/src/gui/ModelView.cpp index 3b250fbcd3f..f9031ea9484 100644 --- a/src/gui/ModelView.cpp +++ b/src/gui/ModelView.cpp @@ -74,6 +74,16 @@ void ModelView::setModel( Model* model, bool isOldModelValid ) +// Unsets the current model by setting a dummy empty model. The dummy model is marked as +// "defaultConstructed", so the next call to setModel will delete it. +void ModelView::unsetModel() +{ + setModel(new Model(nullptr, QString(), true)); +} + + + + void ModelView::doConnections() { if( m_model != NULL ) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 2f4a3bc985c..702257f2959 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -73,6 +73,7 @@ #include "StringPairDrag.h" #include "TrackContainerView.h" #include "TrackLabelButton.h" +#include "MidiCCRackView.h" const int INSTRUMENT_WIDTH = 254; @@ -119,6 +120,21 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : } + // Initialize the m_midiCCEnabled variable, but it's actually going to be connected + // to a LedButton + m_midiCCEnable = std::make_unique(false, nullptr, tr("Enable/Disable MIDI CC")); + + // Initialize the MIDI CC controller models and connect them to the method that processes + // the midi cc events + for (int i = 0; i < MidiControllerCount; ++i) + { + m_midiCCModel[i] = std::make_unique(0.0f, 0.0f, 127.0f, 1.0f, + nullptr, tr("CC Controller %1").arg(i)); + + connect(m_midiCCModel[i].get(), &FloatModel::dataChanged, + this, [this, i]{ processCCEvent(i); }, Qt::DirectConnection); + } + setName( tr( "Default preset" ) ); connect( &m_baseNoteModel, SIGNAL( dataChanged() ), @@ -242,9 +258,27 @@ MidiEvent InstrumentTrack::applyMasterKey( const MidiEvent& event ) +void InstrumentTrack::processCCEvent(int controller) +{ + // Does nothing if the LED is disabled + if (!m_midiCCEnable->value()) { return; } + + uint8_t channel = static_cast(midiPort()->realOutputChannel()); + uint16_t cc = static_cast(controller); + uint16_t value = static_cast(m_midiCCModel[controller]->value()); + + // Process the MIDI CC event as an input event but with ignoreOnExport set to false + // so we can know LMMS generated the event, not a controller, and can process it during + // the project export + processInEvent(MidiEvent(MidiControlChange, channel, cc, value, NULL, false)); +} + + + + void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { - if( Engine::getSong()->isExporting() ) + if (Engine::getSong()->isExporting() && event.ignoreOnExport()) { return; } @@ -373,9 +407,11 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim break; } - if( eventHandled == false && instrument()->handleMidiEvent( event, time, offset ) == false ) + // If the event wasn't handled, check if there's a loaded instrument and if so send the + // event to it. If it returns false means the instrument didn't handle the event, so we trigger a warning. + if (eventHandled == false && !(instrument() && instrument()->handleMidiEvent(event, time, offset))) { - qWarning( "InstrumentTrack: unhandled MIDI event %d", event.type() ); + qWarning("InstrumentTrack: unhandled MIDI event %d", event.type()); } } @@ -740,6 +776,15 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement m_baseNoteModel.saveSettings( doc, thisElement, "basenote" ); m_useMasterPitchModel.saveSettings( doc, thisElement, "usemasterpitch"); + // Save MIDI CC stuff + m_midiCCEnable->saveSettings(doc, thisElement, "enablecc"); + QDomElement midiCC = doc.createElement("midicontrollers"); + thisElement.appendChild(midiCC); + for (int i = 0; i < MidiControllerCount; ++i) + { + m_midiCCModel[i]->saveSettings(doc, midiCC, "cc" + QString::number(i)); + } + if( m_instrument != NULL ) { QDomElement i = doc.createElement( "instrument" ); @@ -798,6 +843,10 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement // clear effect-chain just in case we load an old preset without FX-data m_audioPort.effects()->clear(); + // We set MIDI CC enable to false so the knobs don't trigger MIDI CC events while + // they are being loaded. After all knobs are loaded we load the right value of m_midiCCEnable. + m_midiCCEnable->setValue(false); + QDomNode node = thisElement.firstChild(); while( !node.isNull() ) { @@ -842,6 +891,13 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement emit instrumentChanged(); } } + else if (node.nodeName() == "midicontrollers") + { + for (int i = 0; i < MidiControllerCount; ++i) + { + m_midiCCModel[i]->loadSettings(node.toElement(), "cc" + QString::number(i)); + } + } // compat code - if node-name doesn't match any known // one, we assume that it is an instrument-plugin // which we'll try to load @@ -862,6 +918,10 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement } node = node.nextSibling(); } + + // Load the right value of m_midiCCEnable + m_midiCCEnable->loadSettings(thisElement, "enablecc"); + updatePitchRange(); unlock(); } @@ -997,7 +1057,7 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), tr( "Panning" ) ); m_panningKnob->setModel( &_it->m_panningModel ); - m_panningKnob->setHintText( tr( "Panning:" ), "%" ); + m_panningKnob->setHintText(tr("Panning:"), "%"); m_panningKnob->move( widgetWidth-24, 2 ); m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); @@ -1037,6 +1097,11 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV m_midiInputAction->setText( tr( "Input" ) ); m_midiOutputAction->setText( tr( "Output" ) ); + QAction *midiRackAction = m_midiMenu->addAction(tr("Open/Close MIDI CC Rack")); + midiRackAction->setIcon(embed::getIconPixmap("midi_cc_rack")); + connect(midiRackAction, SIGNAL(triggered()), + this, SLOT(toggleMidiCCRack())); + m_activityIndicator = new FadeButton( QApplication::palette().color( QPalette::Active, QPalette::Background), QApplication::palette().color( QPalette::Active, @@ -1074,6 +1139,29 @@ InstrumentTrackView::~InstrumentTrackView() +void InstrumentTrackView::toggleMidiCCRack() +{ + // Lazy creation: midiCCRackView is only created when accessed the first time. + // this->model() returns pointer to the InstrumentTrack who owns this InstrumentTrackView. + if (!m_midiCCRackView) + { + m_midiCCRackView = std::unique_ptr(new MidiCCRackView(this->model())); + } + + if (m_midiCCRackView->parentWidget()->isVisible()) + { + m_midiCCRackView->parentWidget()->hide(); + } + else + { + m_midiCCRackView->parentWidget()->show(); + m_midiCCRackView->show(); + } +} + + + + InstrumentTrackWindow * InstrumentTrackView::topLevelInstrumentTrackWindow() { InstrumentTrackWindow * w = NULL; From 827d44be321f7f371b7a4ae0e9f8446692ecda8b Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Thu, 3 Dec 2020 01:31:03 +0000 Subject: [PATCH 169/180] Ensure file opened successfully when loading sample (#5816) --- src/core/SampleBuffer.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 1872b920f29..d057b8a9112 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -196,11 +196,10 @@ void SampleBuffer::update( bool _keep_settings ) { // Use QFile to handle unicode file names on Windows QFile f(file); - f.open(QIODevice::ReadOnly); SNDFILE * snd_file; SF_INFO sf_info; sf_info.format = 0; - if( ( snd_file = sf_open_fd( f.handle(), SFM_READ, &sf_info, false ) ) != NULL ) + if (f.open(QIODevice::ReadOnly) && (snd_file = sf_open_fd(f.handle(), SFM_READ, &sf_info, false))) { f_cnt_t frames = sf_info.frames; int rate = sf_info.samplerate; @@ -396,8 +395,7 @@ f_cnt_t SampleBuffer::decodeSampleSF(QString _f, // Use QFile to handle unicode file names on Windows QFile f(_f); - f.open(QIODevice::ReadOnly); - if( ( snd_file = sf_open_fd( f.handle(), SFM_READ, &sf_info, false ) ) != NULL ) + if (f.open(QIODevice::ReadOnly) && (snd_file = sf_open_fd(f.handle(), SFM_READ, &sf_info, false))) { frames = sf_info.frames; From ddf69feebc2ee6754b897f61ae3bc9101e0df45c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 29 Nov 2020 22:20:08 +0100 Subject: [PATCH 170/180] Lv2: Fix overflow and enum visualization * Fix arithmetic overflow in `Lv2Ports::Meta::get()` in case min and max are not set * Fix combo boxes with >16 values being wrongly visualized as knobs * Rename `Lv2Ports::Vis` enum value `None` to `Generic` --- include/Lv2Ports.h | 10 +++++----- src/core/lv2/Lv2Ports.cpp | 11 +++++++---- src/core/lv2/Lv2Proc.cpp | 2 +- src/gui/Lv2ViewBase.cpp | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 1b37d518b3c..529200793ad 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -61,10 +61,10 @@ enum class Type { //! Port visualization //! @note All Lv2 audio ports are float, this is only the visualisation enum class Vis { - None, - Integer, - Enumeration, - Toggled + Generic, //!< nothing specific, a generic knob or slider shall be used + Integer, //!< counter + Enumeration, //!< selection from enumerated values + Toggled //!< boolean widget }; const char* toStr(Lv2Ports::Flow pf); @@ -106,7 +106,7 @@ struct Meta { Type m_type = Type::Unknown; Flow m_flow = Flow::Unknown; - Vis m_vis = Vis::None; + Vis m_vis = Vis::Generic; bool m_logarithmic = false; diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 4bd77f0bcbb..4db7f3e2a5a 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -77,7 +77,7 @@ const char *toStr(Vis pv) case Vis::Toggled: return "toggled"; case Vis::Enumeration: return "enumeration"; case Vis::Integer: return "integer"; - case Vis::None: return "none"; + case Vis::Generic: return "none"; } return ""; } @@ -118,7 +118,7 @@ std::vector Meta::get(const LilvPlugin *plugin, ? Vis::Enumeration : hasProperty(LV2_CORE__toggled) ? Vis::Toggled - : Vis::None; + : Vis::Generic; if (isA(LV2_CORE__InputPort)) { m_flow = Flow::Input; } else if (isA(LV2_CORE__OutputPort)) { m_flow = Flow::Output; } @@ -218,11 +218,14 @@ std::vector Meta::get(const LilvPlugin *plugin, } // visualization - if (m_max - m_min > 15.0f) + if (!m_min_set() || + !m_max_set() || + // if we get here, min and max are set, so max-min should not overflow: + (m_vis == Vis::Integer && m_max - m_min > 15.0f)) { // range too large for spinbox visualisation, use knobs // e.g. 0...15 would be OK - m_vis = Vis::None; + m_vis = Vis::Generic; } } } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 2190a6f1d09..bbfc7065ca0 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -471,7 +471,7 @@ void Lv2Proc::createPort(std::size_t portNum) } switch (meta.m_vis) { - case Lv2Ports::Vis::None: + case Lv2Ports::Vis::Generic: { // allow ~1000 steps float stepSize = (meta.max(sr) - meta.min(sr)) / 1000.0f; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index a3ef260581d..767adea5a48 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -67,7 +67,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : switch (port.m_vis) { - case PortVis::None: + case PortVis::Generic: m_control = new KnobControl(m_par); break; case PortVis::Integer: From b701e82e3b5981018353ee30905a8e6e9a98e0b5 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 3 Dec 2020 22:47:16 -0300 Subject: [PATCH 171/180] Split Track.cpp and Track.h (#5806) (Fixes #5592) --- include/AutomationPattern.h | 2 +- include/AutomationPatternView.h | 2 +- include/AutomationTrack.h | 1 + include/BBEditor.h | 1 + include/BBTrack.h | 2 + include/InstrumentTrack.h | 1 + include/Pattern.h | 8 +- include/PresetPreviewPlayHandle.h | 1 + include/SampleTrack.h | 2 + include/Song.h | 2 + include/Track.h | 627 +---- include/TrackContainerView.h | 7 +- include/TrackContentObject.h | 183 ++ include/TrackContentObjectView.h | 218 ++ include/TrackContentWidget.h | 142 + include/TrackOperationsWidget.h | 79 + include/TrackView.h | 173 ++ plugins/MidiExport/MidiExport.cpp | 1 + src/core/CMakeLists.txt | 1 + src/core/Song.cpp | 2 + src/core/Track.cpp | 2845 +-------------------- src/core/TrackContentObject.cpp | 206 ++ src/core/main.cpp | 1 + src/gui/CMakeLists.txt | 4 + src/gui/FileBrowser.cpp | 1 + src/gui/GuiApplication.cpp | 1 + src/gui/MainWindow.cpp | 3 + src/gui/TrackContainerView.cpp | 3 + src/gui/TrackContentObjectView.cpp | 1243 +++++++++ src/gui/TrackView.cpp | 458 ++++ src/gui/editors/BBEditor.cpp | 1 + src/gui/editors/PianoRoll.cpp | 1 + src/gui/editors/SongEditor.cpp | 5 +- src/gui/widgets/FxLineLcdSpinBox.cpp | 2 +- src/gui/widgets/TrackContentWidget.cpp | 710 +++++ src/gui/widgets/TrackOperationsWidget.cpp | 353 +++ src/tracks/BBTrack.cpp | 1 + src/tracks/InstrumentTrack.cpp | 1 + src/tracks/SampleTrack.cpp | 1 + 39 files changed, 3831 insertions(+), 3464 deletions(-) create mode 100644 include/TrackContentObject.h create mode 100644 include/TrackContentObjectView.h create mode 100644 include/TrackContentWidget.h create mode 100644 include/TrackOperationsWidget.h create mode 100644 include/TrackView.h create mode 100644 src/core/TrackContentObject.cpp create mode 100644 src/gui/TrackContentObjectView.cpp create mode 100644 src/gui/TrackView.cpp create mode 100644 src/gui/widgets/TrackContentWidget.cpp create mode 100644 src/gui/widgets/TrackOperationsWidget.cpp diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 0e98c48d39f..aff1f26bf86 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -30,7 +30,7 @@ #include #include -#include "Track.h" +#include "TrackContentObject.h" class AutomationTrack; diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index a6529b5d4a1..5e7b12977af 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -30,7 +30,7 @@ #include "AutomationPattern.h" #include "Song.h" #include "SongEditor.h" -#include "Track.h" +#include "TrackContentObjectView.h" class AutomationPatternView : public TrackContentObjectView diff --git a/include/AutomationTrack.h b/include/AutomationTrack.h index 0d53faaef19..b783c590559 100644 --- a/include/AutomationTrack.h +++ b/include/AutomationTrack.h @@ -28,6 +28,7 @@ #define AUTOMATION_TRACK_H #include "Track.h" +#include "TrackView.h" class AutomationTrack : public Track diff --git a/include/BBEditor.h b/include/BBEditor.h index 311ed570436..d02dc198b64 100644 --- a/include/BBEditor.h +++ b/include/BBEditor.h @@ -26,6 +26,7 @@ #ifndef BB_EDITOR_H #define BB_EDITOR_H + #include "Editor.h" #include "TrackContainerView.h" diff --git a/include/BBTrack.h b/include/BBTrack.h index 3c2f2ede518..4197a88daf0 100644 --- a/include/BBTrack.h +++ b/include/BBTrack.h @@ -31,7 +31,9 @@ #include #include +#include "TrackContentObjectView.h" #include "Track.h" +#include "TrackView.h" class TrackLabelButton; class TrackContainer; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 50e7c4a2554..ec278996fec 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -40,6 +40,7 @@ #include "Pitch.h" #include "Plugin.h" #include "Track.h" +#include "TrackView.h" diff --git a/include/Pattern.h b/include/Pattern.h index ab7c9423005..0bc1f61f37a 100644 --- a/include/Pattern.h +++ b/include/Pattern.h @@ -34,16 +34,10 @@ #include "Note.h" -#include "Track.h" +#include "TrackContentObjectView.h" -class QAction; -class QProgressBar; -class QPushButton; - class InstrumentTrack; -class SampleBuffer; - class LMMS_EXPORT Pattern : public TrackContentObject diff --git a/include/PresetPreviewPlayHandle.h b/include/PresetPreviewPlayHandle.h index a95b680abb1..fcdecf6b8bb 100644 --- a/include/PresetPreviewPlayHandle.h +++ b/include/PresetPreviewPlayHandle.h @@ -29,6 +29,7 @@ #include "NotePlayHandle.h" +class DataFile; class InstrumentTrack; class PreviewTrackContainer; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 7a9c25ab2c0..fab98e948d7 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -33,6 +33,8 @@ #include "FxMixer.h" #include "FxLineLcdSpinBox.h" #include "Track.h" +#include "TrackContentObjectView.h" +#include "TrackView.h" class EffectRackView; class Knob; diff --git a/include/Song.h b/include/Song.h index e06658f4ced..c15fed57cd2 100644 --- a/include/Song.h +++ b/include/Song.h @@ -29,6 +29,8 @@ #include #include +#include +#include #include "TrackContainer.h" #include "Controller.h" diff --git a/include/Track.h b/include/Track.h index 9f609758c2c..9ddd34f4fc6 100644 --- a/include/Track.h +++ b/include/Track.h @@ -1,6 +1,5 @@ /* - * Track.h - declaration of classes concerning tracks -> necessary for all - * track-like objects (beat/bassline, sample-track...) + * Track.h - declaration of Track class * * Copyright (c) 2004-2014 Tobias Doerffel * @@ -26,43 +25,22 @@ #ifndef TRACK_H #define TRACK_H + #include -#include -#include -#include #include -#include -#include "lmms_basics.h" -#include "TimePos.h" -#include "Rubberband.h" -#include "JournallingObject.h" #include "AutomatableModel.h" -#include "ModelView.h" -#include "DataFile.h" -#include "FadeButton.h" - +#include "JournallingObject.h" +#include "lmms_basics.h" -class QMenu; -class QPushButton; -class PixmapButton; -class TextFloat; -class Track; -class TrackContentObjectView; +class TimePos; class TrackContainer; class TrackContainerView; -class TrackContentWidget; +class TrackContentObject; class TrackView; -const int DEFAULT_SETTINGS_WIDGET_WIDTH = 224; -const int TRACK_OP_WIDTH = 78; -// This shaves 150-ish pixels off track buttons, -// ruled from config: ui.compacttrackbuttons -const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 96; -const int TRACK_OP_WIDTH_COMPACT = 62; - /*! The minimum track height in pixels * * Tracks can be resized by shift-dragging anywhere inside the track @@ -71,483 +49,10 @@ const int TRACK_OP_WIDTH_COMPACT = 62; const int MINIMAL_TRACK_HEIGHT = 32; const int DEFAULT_TRACK_HEIGHT = 32; -const int TCO_BORDER_WIDTH = 2; - char const *const FILENAME_FILTER = "[\\0000-\x1f\"*/:<>?\\\\|\x7f]"; -class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject -{ - Q_OBJECT - MM_OPERATORS - mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel); - mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel); -public: - TrackContentObject( Track * track ); - virtual ~TrackContentObject(); - - inline Track * getTrack() const - { - return m_track; - } - - inline const QString & name() const - { - return m_name; - } - - inline void setName( const QString & name ) - { - m_name = name; - emit dataChanged(); - } - - QString displayName() const override - { - return name(); - } - - - inline const TimePos & startPosition() const - { - return m_startPosition; - } - - inline TimePos endPosition() const - { - const int sp = m_startPosition; - return sp + m_length; - } - - inline const TimePos & length() const - { - return m_length; - } - - inline void setAutoResize( const bool r ) - { - m_autoResize = r; - } - - inline const bool getAutoResize() const - { - return m_autoResize; - } - - QColor color() const - { - return m_color; - } - - void setColor( const QColor & c ) - { - m_color = c; - } - - bool hasColor(); - - void useCustomClipColor( bool b ); - - bool usesCustomClipColor() - { - return m_useCustomClipColor; - } - - virtual void movePosition( const TimePos & pos ); - virtual void changeLength( const TimePos & length ); - - virtual TrackContentObjectView * createView( TrackView * tv ) = 0; - - inline void selectViewOnCreate( bool select ) - { - m_selectViewOnCreate = select; - } - - inline bool getSelectViewOnCreate() - { - return m_selectViewOnCreate; - } - - /// Returns true if and only if a->startPosition() < b->startPosition() - static bool comparePosition(const TrackContentObject* a, const TrackContentObject* b); - - TimePos startTimeOffset() const; - void setStartTimeOffset( const TimePos &startTimeOffset ); - - void updateColor(); - - // Will copy the state of a TCO to another TCO - static void copyStateTo( TrackContentObject *src, TrackContentObject *dst ); - -public slots: - void toggleMute(); - - -signals: - void lengthChanged(); - void positionChanged(); - void destroyedTCO(); - void trackColorChanged(); - - -private: - enum Actions - { - NoAction, - Move, - Resize - } ; - - Track * m_track; - QString m_name; - - TimePos m_startPosition; - TimePos m_length; - TimePos m_startTimeOffset; - - BoolModel m_mutedModel; - BoolModel m_soloModel; - bool m_autoResize; - - bool m_selectViewOnCreate; - - QColor m_color; - bool m_useCustomClipColor; - - friend class TrackContentObjectView; - -} ; - - - -class TrackContentObjectView : public selectableObject, public ModelView -{ - Q_OBJECT - -// theming qproperties - Q_PROPERTY( QColor mutedColor READ mutedColor WRITE setMutedColor ) - Q_PROPERTY( QColor mutedBackgroundColor READ mutedBackgroundColor WRITE setMutedBackgroundColor ) - Q_PROPERTY( QColor selectedColor READ selectedColor WRITE setSelectedColor ) - Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) - Q_PROPERTY( QColor textBackgroundColor READ textBackgroundColor WRITE setTextBackgroundColor ) - Q_PROPERTY( QColor textShadowColor READ textShadowColor WRITE setTextShadowColor ) - Q_PROPERTY( QColor BBPatternBackground READ BBPatternBackground WRITE setBBPatternBackground ) - Q_PROPERTY( bool gradient READ gradient WRITE setGradient ) - // We have to use a QSize here because using QPoint isn't supported. - // width -> x, height -> y - Q_PROPERTY( QSize mouseHotspotHand WRITE setMouseHotspotHand ) - -public: - TrackContentObjectView( TrackContentObject * tco, TrackView * tv ); - virtual ~TrackContentObjectView(); - - bool fixedTCOs(); - - inline TrackContentObject * getTrackContentObject() - { - return m_tco; - } - - inline TrackView * getTrackView() - { - return m_trackView; - } - - // qproperty access func - QColor mutedColor() const; - QColor mutedBackgroundColor() const; - QColor selectedColor() const; - QColor textColor() const; - QColor textBackgroundColor() const; - QColor textShadowColor() const; - QColor BBPatternBackground() const; - bool gradient() const; - void setMutedColor( const QColor & c ); - void setMutedBackgroundColor( const QColor & c ); - void setSelectedColor( const QColor & c ); - void setTextColor( const QColor & c ); - void setTextBackgroundColor( const QColor & c ); - void setTextShadowColor( const QColor & c ); - void setBBPatternBackground( const QColor & c ); - void setGradient( const bool & b ); - void setMouseHotspotHand(const QSize & s); - - // access needsUpdate member variable - bool needsUpdate(); - void setNeedsUpdate( bool b ); - - // Method to get a QVector of TCOs to be affected by a context menu action - QVector getClickedTCOs(); - - // Methods to remove, copy, cut, paste and mute a QVector of TCO views - void copy( QVector tcovs ); - void cut( QVector tcovs ); - void paste(); - // remove and toggleMute are static because they don't depend - // being called from a particular TCO view, but can be called anywhere as long - // as a valid TCO view list is given, while copy/cut require an instance for - // some metadata to be written to the clipboard. - static void remove( QVector tcovs ); - static void toggleMute( QVector tcovs ); - - QColor getColorForDisplay( QColor ); - -public slots: - virtual bool close(); - void remove(); - void update() override; - - void changeClipColor(); - void useTrackColor(); - -protected: - enum ContextMenuAction - { - Remove, - Cut, - Copy, - Paste, - Mute - }; - - virtual void constructContextMenu( QMenu * ) - { - } - - void contextMenuEvent( QContextMenuEvent * cme ) override; - void contextMenuAction( ContextMenuAction action ); - void dragEnterEvent( QDragEnterEvent * dee ) override; - void dropEvent( QDropEvent * de ) override; - void leaveEvent( QEvent * e ) override; - void mousePressEvent( QMouseEvent * me ) override; - void mouseMoveEvent( QMouseEvent * me ) override; - void mouseReleaseEvent( QMouseEvent * me ) override; - void resizeEvent( QResizeEvent * re ) override - { - m_needsUpdate = true; - selectableObject::resizeEvent( re ); - } - - float pixelsPerBar(); - - - DataFile createTCODataFiles(const QVector & tcos) const; - - virtual void paintTextLabel(QString const & text, QPainter & painter); - - -protected slots: - void updateLength(); - void updatePosition(); - - -private: - enum Actions - { - NoAction, - Move, - MoveSelection, - Resize, - ResizeLeft, - CopySelection, - ToggleSelected - } ; - - static TextFloat * s_textFloat; - - TrackContentObject * m_tco; - TrackView * m_trackView; - Actions m_action; - QPoint m_initialMousePos; - QPoint m_initialMouseGlobalPos; - TimePos m_initialTCOPos; - TimePos m_initialTCOEnd; - QVector m_initialOffsets; - - TextFloat * m_hint; - -// qproperty fields - QColor m_mutedColor; - QColor m_mutedBackgroundColor; - QColor m_selectedColor; - QColor m_textColor; - QColor m_textBackgroundColor; - QColor m_textShadowColor; - QColor m_BBPatternBackground; - bool m_gradient; - QSize m_mouseHotspotHand; // QSize must be used because QPoint isn't supported by property system - bool m_cursorSetYet; - - bool m_needsUpdate; - inline void setInitialPos( QPoint pos ) - { - m_initialMousePos = pos; - m_initialMouseGlobalPos = mapToGlobal( pos ); - m_initialTCOPos = m_tco->startPosition(); - m_initialTCOEnd = m_initialTCOPos + m_tco->length(); - } - void setInitialOffsets(); - - bool mouseMovedDistance( QMouseEvent * me, int distance ); - TimePos draggedTCOPos( QMouseEvent * me ); -} ; - - - - - -class TrackContentWidget : public QWidget, public JournallingObject -{ - Q_OBJECT - - // qproperties for track background gradients - Q_PROPERTY( QBrush darkerColor READ darkerColor WRITE setDarkerColor ) - Q_PROPERTY( QBrush lighterColor READ lighterColor WRITE setLighterColor ) - Q_PROPERTY( QBrush gridColor READ gridColor WRITE setGridColor ) - Q_PROPERTY( QBrush embossColor READ embossColor WRITE setEmbossColor ) - -public: - TrackContentWidget( TrackView * parent ); - virtual ~TrackContentWidget(); - - /*! \brief Updates the background tile pixmap. */ - void updateBackground(); - - void addTCOView( TrackContentObjectView * tcov ); - void removeTCOView( TrackContentObjectView * tcov ); - void removeTCOView( int tcoNum ) - { - if( tcoNum >= 0 && tcoNum < m_tcoViews.size() ) - { - removeTCOView( m_tcoViews[tcoNum] ); - } - } - - bool canPasteSelection( TimePos tcoPos, const QDropEvent *de ); - bool canPasteSelection( TimePos tcoPos, const QMimeData *md, bool allowSameBar = false ); - bool pasteSelection( TimePos tcoPos, QDropEvent * de ); - bool pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck = false ); - - TimePos endPosition( const TimePos & posStart ); - - // qproperty access methods - - QBrush darkerColor() const; - QBrush lighterColor() const; - QBrush gridColor() const; - QBrush embossColor() const; - - void setDarkerColor( const QBrush & c ); - void setLighterColor( const QBrush & c ); - void setGridColor( const QBrush & c ); - void setEmbossColor( const QBrush & c); - -public slots: - void update(); - void changePosition( const TimePos & newPos = TimePos( -1 ) ); - -protected: - enum ContextMenuAction - { - Paste - }; - - void contextMenuEvent( QContextMenuEvent * cme ) override; - void contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ); - void dragEnterEvent( QDragEnterEvent * dee ) override; - void dropEvent( QDropEvent * de ) override; - void mousePressEvent( QMouseEvent * me ) override; - void paintEvent( QPaintEvent * pe ) override; - void resizeEvent( QResizeEvent * re ) override; - - QString nodeName() const override - { - return "trackcontentwidget"; - } - - void saveSettings( QDomDocument& doc, QDomElement& element ) override - { - Q_UNUSED(doc) - Q_UNUSED(element) - } - - void loadSettings( const QDomElement& element ) override - { - Q_UNUSED(element) - } - - -private: - Track * getTrack(); - TimePos getPosition( int mouseX ); - - TrackView * m_trackView; - - typedef QVector tcoViewVector; - tcoViewVector m_tcoViews; - - QPixmap m_background; - - // qproperty fields - QBrush m_darkerColor; - QBrush m_lighterColor; - QBrush m_gridColor; - QBrush m_embossColor; -} ; - - - - - -class TrackOperationsWidget : public QWidget -{ - Q_OBJECT -public: - TrackOperationsWidget( TrackView * parent ); - ~TrackOperationsWidget(); - - -protected: - void mousePressEvent( QMouseEvent * me ) override; - void paintEvent( QPaintEvent * pe ) override; - - -private slots: - void cloneTrack(); - void removeTrack(); - void updateMenu(); - void changeTrackColor(); - void randomTrackColor(); - void resetTrackColor(); - void useTrackColor(); - void toggleRecording(bool on); - void recordingOn(); - void recordingOff(); - void clearTrack(); - -private: - TrackView * m_trackView; - - QPushButton * m_trackOps; - PixmapButton * m_muteBtn; - PixmapButton * m_soloBtn; - - - friend class TrackView; - -signals: - void trackRemovalScheduled( TrackView * t ); - void colorChanged( QColor & c ); - void colorParented(); - void colorReset(); - -} ; - - - - - -// base-class for all tracks +//! Base-class for all tracks class LMMS_EXPORT Track : public Model, public JournallingObject { Q_OBJECT @@ -729,122 +234,4 @@ public slots: - -class TrackView : public QWidget, public ModelView, public JournallingObject -{ - Q_OBJECT -public: - TrackView( Track * _track, TrackContainerView* tcv ); - virtual ~TrackView(); - - inline const Track * getTrack() const - { - return m_track; - } - - inline Track * getTrack() - { - return m_track; - } - - inline TrackContainerView* trackContainerView() - { - return m_trackContainerView; - } - - inline TrackOperationsWidget * getTrackOperationsWidget() - { - return &m_trackOperationsWidget; - } - - inline QWidget * getTrackSettingsWidget() - { - return &m_trackSettingsWidget; - } - - inline TrackContentWidget * getTrackContentWidget() - { - return &m_trackContentWidget; - } - - bool isMovingTrack() const - { - return m_action == MoveTrack; - } - - virtual void update(); - - // Create a menu for assigning/creating channels for this track - // Currently instrument track and sample track supports it - virtual QMenu * createFxMenu(QString title, QString newFxLabel); - - -public slots: - virtual bool close(); - - -protected: - void modelChanged() override; - - void saveSettings( QDomDocument& doc, QDomElement& element ) override - { - Q_UNUSED(doc) - Q_UNUSED(element) - } - - void loadSettings( const QDomElement& element ) override - { - Q_UNUSED(element) - } - - QString nodeName() const override - { - return "trackview"; - } - - - void dragEnterEvent( QDragEnterEvent * dee ) override; - void dropEvent( QDropEvent * de ) override; - void mousePressEvent( QMouseEvent * me ) override; - void mouseMoveEvent( QMouseEvent * me ) override; - void mouseReleaseEvent( QMouseEvent * me ) override; - void paintEvent( QPaintEvent * pe ) override; - void resizeEvent( QResizeEvent * re ) override; - - -private: - enum Actions - { - NoAction, - MoveTrack, - ResizeTrack - } ; - - Track * m_track; - TrackContainerView * m_trackContainerView; - - TrackOperationsWidget m_trackOperationsWidget; - QWidget m_trackSettingsWidget; - TrackContentWidget m_trackContentWidget; - - Actions m_action; - - virtual FadeButton * getActivityIndicator() - { - return nullptr; - } - - void setIndicatorMute(FadeButton* indicator, bool muted); - - friend class TrackLabelButton; - - -private slots: - void createTCOView( TrackContentObject * tco ); - void muteChanged(); - -} ; - - - #endif diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 30e7ec0f6aa..0edd63865f6 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -31,12 +31,15 @@ #include #include -#include "Track.h" #include "JournallingObject.h" -#include "InstrumentTrack.h" +#include "ModelView.h" +#include "Rubberband.h" +#include "TrackView.h" class QVBoxLayout; + +class InstrumentTrack; class TrackContainer; diff --git a/include/TrackContentObject.h b/include/TrackContentObject.h new file mode 100644 index 00000000000..dddd2b75c39 --- /dev/null +++ b/include/TrackContentObject.h @@ -0,0 +1,183 @@ +/* + * TrackConteintObject.h - declaration of TrackContentObject class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef TRACK_CONTENT_OBJECT_H +#define TRACK_CONTENT_OBJECT_H + +#include + +#include "AutomatableModel.h" +#include "lmms_basics.h" + + +class Track; +class TrackContentObjectView; +class TrackContainer; +class TrackView; + + +class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject +{ + Q_OBJECT + MM_OPERATORS + mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel); + mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel); +public: + TrackContentObject( Track * track ); + virtual ~TrackContentObject(); + + inline Track * getTrack() const + { + return m_track; + } + + inline const QString & name() const + { + return m_name; + } + + inline void setName( const QString & name ) + { + m_name = name; + emit dataChanged(); + } + + QString displayName() const override + { + return name(); + } + + + inline const TimePos & startPosition() const + { + return m_startPosition; + } + + inline TimePos endPosition() const + { + const int sp = m_startPosition; + return sp + m_length; + } + + inline const TimePos & length() const + { + return m_length; + } + + inline void setAutoResize( const bool r ) + { + m_autoResize = r; + } + + inline const bool getAutoResize() const + { + return m_autoResize; + } + + QColor color() const + { + return m_color; + } + + void setColor( const QColor & c ) + { + m_color = c; + } + + bool hasColor(); + + void useCustomClipColor( bool b ); + + bool usesCustomClipColor() + { + return m_useCustomClipColor; + } + + virtual void movePosition( const TimePos & pos ); + virtual void changeLength( const TimePos & length ); + + virtual TrackContentObjectView * createView( TrackView * tv ) = 0; + + inline void selectViewOnCreate( bool select ) + { + m_selectViewOnCreate = select; + } + + inline bool getSelectViewOnCreate() + { + return m_selectViewOnCreate; + } + + /// Returns true if and only if a->startPosition() < b->startPosition() + static bool comparePosition(const TrackContentObject* a, const TrackContentObject* b); + + TimePos startTimeOffset() const; + void setStartTimeOffset( const TimePos &startTimeOffset ); + + void updateColor(); + + // Will copy the state of a TCO to another TCO + static void copyStateTo( TrackContentObject *src, TrackContentObject *dst ); + +public slots: + void toggleMute(); + + +signals: + void lengthChanged(); + void positionChanged(); + void destroyedTCO(); + void trackColorChanged(); + + +private: + enum Actions + { + NoAction, + Move, + Resize + } ; + + Track * m_track; + QString m_name; + + TimePos m_startPosition; + TimePos m_length; + TimePos m_startTimeOffset; + + BoolModel m_mutedModel; + BoolModel m_soloModel; + bool m_autoResize; + + bool m_selectViewOnCreate; + + QColor m_color; + bool m_useCustomClipColor; + + friend class TrackContentObjectView; + +} ; + + +#endif diff --git a/include/TrackContentObjectView.h b/include/TrackContentObjectView.h new file mode 100644 index 00000000000..0f99af96479 --- /dev/null +++ b/include/TrackContentObjectView.h @@ -0,0 +1,218 @@ +/* + * TrackContentObjectView.h - declaration of TrackContentObjectView class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef TRACK_CONTENT_OBJECT_VIEW_H +#define TRACK_CONTENT_OBJECT_VIEW_H + + +#include + +#include "ModelView.h" +#include "Rubberband.h" +#include "TrackContentObject.h" + + +class QMenu; +class QContextMenuEvent; + +class DataFile; +class TextFloat; +class TrackContentObject; +class TrackView; + + +class TrackContentObjectView : public selectableObject, public ModelView +{ + Q_OBJECT + +// theming qproperties + Q_PROPERTY( QColor mutedColor READ mutedColor WRITE setMutedColor ) + Q_PROPERTY( QColor mutedBackgroundColor READ mutedBackgroundColor WRITE setMutedBackgroundColor ) + Q_PROPERTY( QColor selectedColor READ selectedColor WRITE setSelectedColor ) + Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) + Q_PROPERTY( QColor textBackgroundColor READ textBackgroundColor WRITE setTextBackgroundColor ) + Q_PROPERTY( QColor textShadowColor READ textShadowColor WRITE setTextShadowColor ) + Q_PROPERTY( QColor BBPatternBackground READ BBPatternBackground WRITE setBBPatternBackground ) + Q_PROPERTY( bool gradient READ gradient WRITE setGradient ) + // We have to use a QSize here because using QPoint isn't supported. + // width -> x, height -> y + Q_PROPERTY( QSize mouseHotspotHand WRITE setMouseHotspotHand ) + +public: + TrackContentObjectView( TrackContentObject * tco, TrackView * tv ); + virtual ~TrackContentObjectView(); + + bool fixedTCOs(); + + inline TrackContentObject * getTrackContentObject() + { + return m_tco; + } + + inline TrackView * getTrackView() + { + return m_trackView; + } + + // qproperty access func + QColor mutedColor() const; + QColor mutedBackgroundColor() const; + QColor selectedColor() const; + QColor textColor() const; + QColor textBackgroundColor() const; + QColor textShadowColor() const; + QColor BBPatternBackground() const; + bool gradient() const; + void setMutedColor( const QColor & c ); + void setMutedBackgroundColor( const QColor & c ); + void setSelectedColor( const QColor & c ); + void setTextColor( const QColor & c ); + void setTextBackgroundColor( const QColor & c ); + void setTextShadowColor( const QColor & c ); + void setBBPatternBackground( const QColor & c ); + void setGradient( const bool & b ); + void setMouseHotspotHand(const QSize & s); + + // access needsUpdate member variable + bool needsUpdate(); + void setNeedsUpdate( bool b ); + + // Method to get a QVector of TCOs to be affected by a context menu action + QVector getClickedTCOs(); + + // Methods to remove, copy, cut, paste and mute a QVector of TCO views + void copy( QVector tcovs ); + void cut( QVector tcovs ); + void paste(); + // remove and toggleMute are static because they don't depend + // being called from a particular TCO view, but can be called anywhere as long + // as a valid TCO view list is given, while copy/cut require an instance for + // some metadata to be written to the clipboard. + static void remove( QVector tcovs ); + static void toggleMute( QVector tcovs ); + + QColor getColorForDisplay( QColor ); + +public slots: + virtual bool close(); + void remove(); + void update() override; + + void changeClipColor(); + void useTrackColor(); + +protected: + enum ContextMenuAction + { + Remove, + Cut, + Copy, + Paste, + Mute + }; + + virtual void constructContextMenu( QMenu * ) + { + } + + void contextMenuEvent( QContextMenuEvent * cme ) override; + void contextMenuAction( ContextMenuAction action ); + void dragEnterEvent( QDragEnterEvent * dee ) override; + void dropEvent( QDropEvent * de ) override; + void leaveEvent( QEvent * e ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; + void resizeEvent( QResizeEvent * re ) override + { + m_needsUpdate = true; + selectableObject::resizeEvent( re ); + } + + float pixelsPerBar(); + + + DataFile createTCODataFiles(const QVector & tcos) const; + + virtual void paintTextLabel(QString const & text, QPainter & painter); + + +protected slots: + void updateLength(); + void updatePosition(); + + +private: + enum Actions + { + NoAction, + Move, + MoveSelection, + Resize, + ResizeLeft, + CopySelection, + ToggleSelected + } ; + + static TextFloat * s_textFloat; + + TrackContentObject * m_tco; + TrackView * m_trackView; + Actions m_action; + QPoint m_initialMousePos; + QPoint m_initialMouseGlobalPos; + TimePos m_initialTCOPos; + TimePos m_initialTCOEnd; + QVector m_initialOffsets; + + TextFloat * m_hint; + +// qproperty fields + QColor m_mutedColor; + QColor m_mutedBackgroundColor; + QColor m_selectedColor; + QColor m_textColor; + QColor m_textBackgroundColor; + QColor m_textShadowColor; + QColor m_BBPatternBackground; + bool m_gradient; + QSize m_mouseHotspotHand; // QSize must be used because QPoint isn't supported by property system + bool m_cursorSetYet; + + bool m_needsUpdate; + inline void setInitialPos( QPoint pos ) + { + m_initialMousePos = pos; + m_initialMouseGlobalPos = mapToGlobal( pos ); + m_initialTCOPos = m_tco->startPosition(); + m_initialTCOEnd = m_initialTCOPos + m_tco->length(); + } + void setInitialOffsets(); + + bool mouseMovedDistance( QMouseEvent * me, int distance ); + TimePos draggedTCOPos( QMouseEvent * me ); +} ; + + +#endif diff --git a/include/TrackContentWidget.h b/include/TrackContentWidget.h new file mode 100644 index 00000000000..c2764bfc6b4 --- /dev/null +++ b/include/TrackContentWidget.h @@ -0,0 +1,142 @@ +/* + * TrackContentWidget.h - declaration of TrackContentWidget class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef TRACK_CONTENT_WIDGET_H +#define TRACK_CONTENT_WIDGET_H + +#include + +#include "JournallingObject.h" +#include "TimePos.h" + + +class QMimeData; + +class Track; +class TrackContentObjectView; +class TrackView; + + +class TrackContentWidget : public QWidget, public JournallingObject +{ + Q_OBJECT + + // qproperties for track background gradients + Q_PROPERTY( QBrush darkerColor READ darkerColor WRITE setDarkerColor ) + Q_PROPERTY( QBrush lighterColor READ lighterColor WRITE setLighterColor ) + Q_PROPERTY( QBrush gridColor READ gridColor WRITE setGridColor ) + Q_PROPERTY( QBrush embossColor READ embossColor WRITE setEmbossColor ) + +public: + TrackContentWidget( TrackView * parent ); + virtual ~TrackContentWidget(); + + /*! \brief Updates the background tile pixmap. */ + void updateBackground(); + + void addTCOView( TrackContentObjectView * tcov ); + void removeTCOView( TrackContentObjectView * tcov ); + void removeTCOView( int tcoNum ) + { + if( tcoNum >= 0 && tcoNum < m_tcoViews.size() ) + { + removeTCOView( m_tcoViews[tcoNum] ); + } + } + + bool canPasteSelection( TimePos tcoPos, const QDropEvent *de ); + bool canPasteSelection( TimePos tcoPos, const QMimeData *md, bool allowSameBar = false ); + bool pasteSelection( TimePos tcoPos, QDropEvent * de ); + bool pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck = false ); + + TimePos endPosition( const TimePos & posStart ); + + // qproperty access methods + + QBrush darkerColor() const; + QBrush lighterColor() const; + QBrush gridColor() const; + QBrush embossColor() const; + + void setDarkerColor( const QBrush & c ); + void setLighterColor( const QBrush & c ); + void setGridColor( const QBrush & c ); + void setEmbossColor( const QBrush & c); + +public slots: + void update(); + void changePosition( const TimePos & newPos = TimePos( -1 ) ); + +protected: + enum ContextMenuAction + { + Paste + }; + + void contextMenuEvent( QContextMenuEvent * cme ) override; + void contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ); + void dragEnterEvent( QDragEnterEvent * dee ) override; + void dropEvent( QDropEvent * de ) override; + void mousePressEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + void resizeEvent( QResizeEvent * re ) override; + + QString nodeName() const override + { + return "trackcontentwidget"; + } + + void saveSettings( QDomDocument& doc, QDomElement& element ) override + { + Q_UNUSED(doc) + Q_UNUSED(element) + } + + void loadSettings( const QDomElement& element ) override + { + Q_UNUSED(element) + } + + +private: + Track * getTrack(); + TimePos getPosition( int mouseX ); + + TrackView * m_trackView; + + typedef QVector tcoViewVector; + tcoViewVector m_tcoViews; + + QPixmap m_background; + + // qproperty fields + QBrush m_darkerColor; + QBrush m_lighterColor; + QBrush m_gridColor; + QBrush m_embossColor; +} ; + + + +#endif diff --git a/include/TrackOperationsWidget.h b/include/TrackOperationsWidget.h new file mode 100644 index 00000000000..f78e0a2092a --- /dev/null +++ b/include/TrackOperationsWidget.h @@ -0,0 +1,79 @@ +/* + * TrackOperationsWidget.h - declaration of TrackOperationsWidget class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef TRACK_OPERATIONS_WIDGET_H +#define TRACK_OPERATIONS_WIDGET_H + +#include + +class QPushButton; + +class PixmapButton; +class TrackView; + +class TrackOperationsWidget : public QWidget +{ + Q_OBJECT +public: + TrackOperationsWidget( TrackView * parent ); + ~TrackOperationsWidget(); + + +protected: + void mousePressEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + + +private slots: + void cloneTrack(); + void removeTrack(); + void updateMenu(); + void changeTrackColor(); + void randomTrackColor(); + void resetTrackColor(); + void useTrackColor(); + void toggleRecording(bool on); + void recordingOn(); + void recordingOff(); + void clearTrack(); + +private: + TrackView * m_trackView; + + QPushButton * m_trackOps; + PixmapButton * m_muteBtn; + PixmapButton * m_soloBtn; + + + friend class TrackView; + +signals: + void trackRemovalScheduled( TrackView * t ); + void colorChanged( QColor & c ); + void colorParented(); + void colorReset(); + +} ; + +#endif diff --git a/include/TrackView.h b/include/TrackView.h new file mode 100644 index 00000000000..1029e148c1d --- /dev/null +++ b/include/TrackView.h @@ -0,0 +1,173 @@ +/* + * TrackView.h - declaration of TrackView class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + + +#ifndef TRACK_VIEW_H +#define TRACK_VIEW_H + +#include + +#include "JournallingObject.h" +#include "ModelView.h" +#include "TrackContentWidget.h" +#include "TrackOperationsWidget.h" + + +class QMenu; + +class FadeButton; +class Track; +class TrackContainerView; +class TrackContentObject; + + +const int DEFAULT_SETTINGS_WIDGET_WIDTH = 224; +const int TRACK_OP_WIDTH = 78; +// This shaves 150-ish pixels off track buttons, +// ruled from config: ui.compacttrackbuttons +const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 96; +const int TRACK_OP_WIDTH_COMPACT = 62; + +const int TCO_BORDER_WIDTH = 2; + + +class TrackView : public QWidget, public ModelView, public JournallingObject +{ + Q_OBJECT +public: + TrackView( Track * _track, TrackContainerView* tcv ); + virtual ~TrackView(); + + inline const Track * getTrack() const + { + return m_track; + } + + inline Track * getTrack() + { + return m_track; + } + + inline TrackContainerView* trackContainerView() + { + return m_trackContainerView; + } + + inline TrackOperationsWidget * getTrackOperationsWidget() + { + return &m_trackOperationsWidget; + } + + inline QWidget * getTrackSettingsWidget() + { + return &m_trackSettingsWidget; + } + + inline TrackContentWidget * getTrackContentWidget() + { + return &m_trackContentWidget; + } + + bool isMovingTrack() const + { + return m_action == MoveTrack; + } + + virtual void update(); + + // Create a menu for assigning/creating channels for this track + // Currently instrument track and sample track supports it + virtual QMenu * createFxMenu(QString title, QString newFxLabel); + + +public slots: + virtual bool close(); + + +protected: + void modelChanged() override; + + void saveSettings( QDomDocument& doc, QDomElement& element ) override + { + Q_UNUSED(doc) + Q_UNUSED(element) + } + + void loadSettings( const QDomElement& element ) override + { + Q_UNUSED(element) + } + + QString nodeName() const override + { + return "trackview"; + } + + + void dragEnterEvent( QDragEnterEvent * dee ) override; + void dropEvent( QDropEvent * de ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + void resizeEvent( QResizeEvent * re ) override; + + +private: + enum Actions + { + NoAction, + MoveTrack, + ResizeTrack + } ; + + Track * m_track; + TrackContainerView * m_trackContainerView; + + TrackOperationsWidget m_trackOperationsWidget; + QWidget m_trackSettingsWidget; + TrackContentWidget m_trackContentWidget; + + Actions m_action; + + virtual FadeButton * getActivityIndicator() + { + return nullptr; + } + + void setIndicatorMute(FadeButton* indicator, bool muted); + + friend class TrackLabelButton; + + +private slots: + void createTCOView( TrackContentObject * tco ); + void muteChanged(); + +} ; + + + +#endif diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index 466664b167d..929f7d38cf5 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -35,6 +35,7 @@ #include "lmms_math.h" #include "TrackContainer.h" #include "BBTrack.h" +#include "DataFile.h" #include "InstrumentTrack.h" #include "LocaleHelper.h" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7b38c6c39da..b1e31c6930b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -71,6 +71,7 @@ set(LMMS_SRCS core/ToolPlugin.cpp core/Track.cpp core/TrackContainer.cpp + core/TrackContentObject.cpp core/ValueBuffer.cpp core/VstSyncController.cpp core/StepRecorder.cpp diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 30d0bea23fb..be3d2745ae1 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -48,6 +48,8 @@ #include "FxMixerView.h" #include "GuiApplication.h" #include "ExportFilter.h" +#include "InstrumentTrack.h" +#include "NotePlayHandle.h" #include "Pattern.h" #include "PianoRoll.h" #include "ProjectJournal.h" diff --git a/src/core/Track.cpp b/src/core/Track.cpp index c2178d3be2c..a6111c06e82 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1,6 +1,5 @@ /* - * Track.cpp - implementation of classes concerning tracks -> necessary for - * all track-like objects (beat/bassline, sample-track...) + * Track.cpp - implementation of Track class * * Copyright (c) 2004-2014 Tobias Doerffel * @@ -24,2418 +23,24 @@ */ /** \file Track.cpp - * \brief All classes concerning tracks and track-like objects + * \brief Implementation of Track class */ -/* - * \mainpage Track classes - * - * \section introduction Introduction - * - * \todo fill this out - */ - -#include "Track.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "AutomationPattern.h" -#include "AutomationTrack.h" -#include "AutomationEditor.h" -#include "BBEditor.h" -#include "BBTrack.h" -#include "BBTrackContainer.h" -#include "ConfigManager.h" -#include "Clipboard.h" -#include "ColorChooser.h" -#include "embed.h" -#include "Engine.h" -#include "GuiApplication.h" -#include "FxMixerView.h" -#include "gui_templates.h" -#include "MainWindow.h" -#include "Mixer.h" -#include "ProjectJournal.h" -#include "SampleTrack.h" -#include "Song.h" -#include "SongEditor.h" -#include "StringPairDrag.h" -#include "TextFloat.h" - - -/*! The width of the resize grip in pixels - */ -const int RESIZE_GRIP_WIDTH = 4; - -/*! Alternate between a darker and a lighter background color every 4 bars - */ -const int BARS_PER_GROUP = 4; - - -/*! A pointer for that text bubble used when moving segments, etc. - * - * In a number of situations, LMMS displays a floating text bubble - * beside the cursor as you move or resize elements of a track about. - * This pointer keeps track of it, as you only ever need one at a time. - */ -TextFloat * TrackContentObjectView::s_textFloat = NULL; - - -// =========================================================================== -// TrackContentObject -// =========================================================================== -/*! \brief Create a new TrackContentObject - * - * Creates a new track content object for the given track. - * - * \param _track The track that will contain the new object - */ -TrackContentObject::TrackContentObject( Track * track ) : - Model( track ), - m_track( track ), - m_startPosition(), - m_length(), - m_mutedModel( false, this, tr( "Mute" ) ), - m_selectViewOnCreate( false ), - m_color( 128, 128, 128 ), - m_useCustomClipColor( false ) -{ - if( getTrack() ) - { - getTrack()->addTCO( this ); - } - setJournalling( false ); - movePosition( 0 ); - changeLength( 0 ); - setJournalling( true ); -} - - - - -/*! \brief Destroy a TrackContentObject - * - * Destroys the given track content object. - * - */ -TrackContentObject::~TrackContentObject() -{ - emit destroyedTCO(); - - if( getTrack() ) - { - getTrack()->removeTCO( this ); - } -} - - - - -/*! \brief Move this TrackContentObject's position in time - * - * If the track content object has moved, update its position. We - * also add a journal entry for undo and update the display. - * - * \param _pos The new position of the track content object. - */ -void TrackContentObject::movePosition( const TimePos & pos ) -{ - TimePos newPos = qMax(0, pos.getTicks()); - if (m_startPosition != newPos) - { - Engine::mixer()->requestChangeInModel(); - m_startPosition = newPos; - Engine::mixer()->doneChangeInModel(); - Engine::getSong()->updateLength(); - emit positionChanged(); - } -} - - - - -/*! \brief Change the length of this TrackContentObject - * - * If the track content object's length has changed, update it. We - * also add a journal entry for undo and update the display. - * - * \param _length The new length of the track content object. - */ -void TrackContentObject::changeLength( const TimePos & length ) -{ - m_length = length; - Engine::getSong()->updateLength(); - emit lengthChanged(); -} - - - - -bool TrackContentObject::comparePosition(const TrackContentObject *a, const TrackContentObject *b) -{ - return a->startPosition() < b->startPosition(); -} - - - - -/*! \brief Copies the state of a TrackContentObject to another TrackContentObject - * - * This method copies the state of a TCO to another TCO - */ -void TrackContentObject::copyStateTo( TrackContentObject *src, TrackContentObject *dst ) -{ - // If the node names match we copy the state - if( src->nodeName() == dst->nodeName() ){ - QDomDocument doc; - QDomElement parent = doc.createElement( "StateCopy" ); - src->saveState( doc, parent ); - - const TimePos pos = dst->startPosition(); - dst->restoreState( parent.firstChild().toElement() ); - dst->movePosition( pos ); - - AutomationPattern::resolveAllIDs(); - GuiApplication::instance()->automationEditor()->m_editor->updateAfterPatternChange(); - } -} - - - - -/*! \brief Mutes this TrackContentObject - * - * Restore the previous state of this track content object. This will - * restore the position or the length of the track content object - * depending on what was changed. - * - * \param _je The journal entry to undo - */ -void TrackContentObject::toggleMute() -{ - m_mutedModel.setValue( !m_mutedModel.value() ); - emit dataChanged(); -} - - - - -TimePos TrackContentObject::startTimeOffset() const -{ - return m_startTimeOffset; -} - - - - -void TrackContentObject::setStartTimeOffset( const TimePos &startTimeOffset ) -{ - m_startTimeOffset = startTimeOffset; -} - -// Update TCO color if it follows the track color -void TrackContentObject::updateColor() -{ - if( ! m_useCustomClipColor ) - { - emit trackColorChanged(); - } -} - - -void TrackContentObject::useCustomClipColor( bool b ) -{ - m_useCustomClipColor = b; - updateColor(); -} - - -bool TrackContentObject::hasColor() -{ - return usesCustomClipColor() || getTrack()->useColor(); -} - - - - - -// =========================================================================== -// trackContentObjectView -// =========================================================================== -/*! \brief Create a new trackContentObjectView - * - * Creates a new track content object view for the given - * track content object in the given track view. - * - * \param _tco The track content object to be displayed - * \param _tv The track view that will contain the new object - */ -TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, - TrackView * tv ) : - selectableObject( tv->getTrackContentWidget() ), - ModelView( NULL, this ), - m_tco( tco ), - m_trackView( tv ), - m_action( NoAction ), - m_initialMousePos( QPoint( 0, 0 ) ), - m_initialMouseGlobalPos( QPoint( 0, 0 ) ), - m_initialTCOPos( TimePos(0) ), - m_initialTCOEnd( TimePos(0) ), - m_initialOffsets( QVector() ), - m_hint( NULL ), - m_mutedColor( 0, 0, 0 ), - m_mutedBackgroundColor( 0, 0, 0 ), - m_selectedColor( 0, 0, 0 ), - m_textColor( 0, 0, 0 ), - m_textShadowColor( 0, 0, 0 ), - m_BBPatternBackground( 0, 0, 0 ), - m_gradient( true ), - m_mouseHotspotHand( 0, 0 ), - m_cursorSetYet( false ), - m_needsUpdate( true ) -{ - if( s_textFloat == NULL ) - { - s_textFloat = new TextFloat; - s_textFloat->setPixmap( embed::getIconPixmap( "clock" ) ); - } - - setAttribute( Qt::WA_OpaquePaintEvent, true ); - setAttribute( Qt::WA_DeleteOnClose, true ); - setFocusPolicy( Qt::StrongFocus ); - setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); - move( 0, 0 ); - show(); - - setFixedHeight( tv->getTrackContentWidget()->height() - 1); - setAcceptDrops( true ); - setMouseTracking( true ); - - connect( m_tco, SIGNAL( lengthChanged() ), - this, SLOT( updateLength() ) ); - connect( gui->songEditor()->m_editor->zoomingModel(), SIGNAL( dataChanged() ), this, SLOT( updateLength() ) ); - connect( m_tco, SIGNAL( positionChanged() ), - this, SLOT( updatePosition() ) ); - connect( m_tco, SIGNAL( destroyedTCO() ), this, SLOT( close() ) ); - setModel( m_tco ); - connect( m_tco, SIGNAL( trackColorChanged() ), this, SLOT( update() ) ); - connect( m_trackView->getTrackOperationsWidget(), SIGNAL( colorParented() ), this, SLOT( useTrackColor() ) ); - - m_trackView->getTrackContentWidget()->addTCOView( this ); - updateLength(); - updatePosition(); -} - - - - -/*! \brief Destroy a trackContentObjectView - * - * Destroys the given track content object view. - * - */ -TrackContentObjectView::~TrackContentObjectView() -{ - delete m_hint; - // we have to give our track-container the focus because otherwise the - // op-buttons of our track-widgets could become focus and when the user - // presses space for playing song, just one of these buttons is pressed - // which results in unwanted effects - m_trackView->trackContainerView()->setFocus(); -} - - -/*! \brief Update a TrackContentObjectView - * - * TCO's get drawn only when needed, - * and when a TCO is updated, - * it needs to be redrawn. - * - */ -void TrackContentObjectView::update() -{ - if( !m_cursorSetYet ) - { - setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); - m_cursorSetYet = true; - } - - if( fixedTCOs() ) - { - updateLength(); - } - m_needsUpdate = true; - selectableObject::update(); -} - - - -/*! \brief Does this trackContentObjectView have a fixed TCO? - * - * Returns whether the containing trackView has fixed - * TCOs. - * - * \todo What the hell is a TCO here - track content object? And in - * what circumstance are they fixed? - */ -bool TrackContentObjectView::fixedTCOs() -{ - return m_trackView->trackContainerView()->fixedTCOs(); -} - - - -// qproperty access functions, to be inherited & used by TCOviews -//! \brief CSS theming qproperty access method -QColor TrackContentObjectView::mutedColor() const -{ return m_mutedColor; } - -QColor TrackContentObjectView::mutedBackgroundColor() const -{ return m_mutedBackgroundColor; } - -QColor TrackContentObjectView::selectedColor() const -{ return m_selectedColor; } - -QColor TrackContentObjectView::textColor() const -{ return m_textColor; } - -QColor TrackContentObjectView::textBackgroundColor() const -{ - return m_textBackgroundColor; -} - -QColor TrackContentObjectView::textShadowColor() const -{ return m_textShadowColor; } - -QColor TrackContentObjectView::BBPatternBackground() const -{ return m_BBPatternBackground; } - -bool TrackContentObjectView::gradient() const -{ return m_gradient; } - -//! \brief CSS theming qproperty access method -void TrackContentObjectView::setMutedColor( const QColor & c ) -{ m_mutedColor = QColor( c ); } - -void TrackContentObjectView::setMutedBackgroundColor( const QColor & c ) -{ m_mutedBackgroundColor = QColor( c ); } - -void TrackContentObjectView::setSelectedColor( const QColor & c ) -{ m_selectedColor = QColor( c ); } - -void TrackContentObjectView::setTextColor( const QColor & c ) -{ m_textColor = QColor( c ); } - -void TrackContentObjectView::setTextBackgroundColor( const QColor & c ) -{ - m_textBackgroundColor = c; -} - -void TrackContentObjectView::setTextShadowColor( const QColor & c ) -{ m_textShadowColor = QColor( c ); } - -void TrackContentObjectView::setBBPatternBackground( const QColor & c ) -{ m_BBPatternBackground = QColor( c ); } - -void TrackContentObjectView::setGradient( const bool & b ) -{ m_gradient = b; } - -void TrackContentObjectView::setMouseHotspotHand(const QSize & s) -{ - m_mouseHotspotHand = s; -} - -// access needsUpdate member variable -bool TrackContentObjectView::needsUpdate() -{ return m_needsUpdate; } -void TrackContentObjectView::setNeedsUpdate( bool b ) -{ m_needsUpdate = b; } - -/*! \brief Close a trackContentObjectView - * - * Closes a track content object view by asking the track - * view to remove us and then asking the QWidget to close us. - * - * \return Boolean state of whether the QWidget was able to close. - */ -bool TrackContentObjectView::close() -{ - m_trackView->getTrackContentWidget()->removeTCOView( this ); - return QWidget::close(); -} - - - - -/*! \brief Removes a trackContentObjectView from its track view. - * - * Like the close() method, this asks the track view to remove this - * track content object view. However, the track content object is - * scheduled for later deletion rather than closed immediately. - * - */ -void TrackContentObjectView::remove() -{ - m_trackView->getTrack()->addJournalCheckPoint(); - - // delete ourself - close(); - m_tco->deleteLater(); -} - - - - -/*! \brief Updates a trackContentObjectView's length - * - * If this track content object view has a fixed TCO, then we must - * keep the width of our parent. Otherwise, calculate our width from - * the track content object's length in pixels adding in the border. - * - */ -void TrackContentObjectView::updateLength() -{ - if( fixedTCOs() ) - { - setFixedWidth( parentWidget()->width() ); - } - else - { - setFixedWidth( - static_cast( m_tco->length() * pixelsPerBar() / - TimePos::ticksPerBar() ) + 1 /*+ - TCO_BORDER_WIDTH * 2-1*/ ); - } - m_trackView->trackContainerView()->update(); -} - - - - -/*! \brief Updates a trackContentObjectView's position. - * - * Ask our track view to change our position. Then make sure that the - * track view is updated in case this position has changed the track - * view's length. - * - */ -void TrackContentObjectView::updatePosition() -{ - m_trackView->getTrackContentWidget()->changePosition(); - // moving a TCO can result in change of song-length etc., - // therefore we update the track-container - m_trackView->trackContainerView()->update(); -} - - - - -void TrackContentObjectView::changeClipColor() -{ - // Get a color from the user - QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )->getColor( m_tco->color() ); - if( ! new_color.isValid() ) - { return; } - - // Use that color - m_tco->setColor( new_color ); - m_tco->useCustomClipColor( true ); - update(); -} - - - -void TrackContentObjectView::useTrackColor() -{ - m_tco->useCustomClipColor( false ); - update(); -} - - - - - -/*! \brief Change the trackContentObjectView's display when something - * being dragged enters it. - * - * We need to notify Qt to change our display if something being - * dragged has entered our 'airspace'. - * - * \param dee The QDragEnterEvent to watch. - */ -void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee ) -{ - TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); - TimePos tcoPos = TimePos( m_tco->startPosition() ); - - if( tcw->canPasteSelection( tcoPos, dee ) == false ) - { - dee->ignore(); - } - else - { - StringPairDrag::processDragEnterEvent( dee, "tco_" + - QString::number( m_tco->getTrack()->type() ) ); - } -} - - - - -/*! \brief Handle something being dropped on this trackContentObjectView. - * - * When something has been dropped on this trackContentObjectView, and - * it's a track content object, then use an instance of our dataFile reader - * to take the xml of the track content object and turn it into something - * we can write over our current state. - * - * \param de The QDropEvent to handle. - */ -void TrackContentObjectView::dropEvent( QDropEvent * de ) -{ - QString type = StringPairDrag::decodeKey( de ); - QString value = StringPairDrag::decodeValue( de ); - - // Track must be the same type to paste into - if( type != ( "tco_" + QString::number( m_tco->getTrack()->type() ) ) ) - { - return; - } - - // Defer to rubberband paste if we're in that mode - if( m_trackView->trackContainerView()->allowRubberband() == true ) - { - TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); - TimePos tcoPos = TimePos( m_tco->startPosition() ); - - if( tcw->pasteSelection( tcoPos, de ) == true ) - { - de->accept(); - } - return; - } - - // Don't allow pasting a tco into itself. - QObject* qwSource = de->source(); - if( qwSource != NULL && - dynamic_cast( qwSource ) == this ) - { - return; - } - - // Copy state into existing tco - DataFile dataFile( value.toUtf8() ); - TimePos pos = m_tco->startPosition(); - QDomElement tcos = dataFile.content().firstChildElement( "tcos" ); - m_tco->restoreState( tcos.firstChildElement().firstChildElement() ); - m_tco->movePosition( pos ); - AutomationPattern::resolveAllIDs(); - de->accept(); -} - - - - -/*! \brief Handle a dragged selection leaving our 'airspace'. - * - * \param e The QEvent to watch. - */ -void TrackContentObjectView::leaveEvent( QEvent * e ) -{ - if( cursor().shape() != Qt::BitmapCursor ) - { - setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); - } - if( e != NULL ) - { - QWidget::leaveEvent( e ); - } -} - -/*! \brief Create a DataFile suitable for copying multiple trackContentObjects. - * - * trackContentObjects in the vector are written to the "tcos" node in the - * DataFile. The trackContentObjectView's initial mouse position is written - * to the "initialMouseX" node in the DataFile. When dropped on a track, - * this is used to create copies of the TCOs. - * - * \param tcos The trackContectObjects to save in a DataFile - */ -DataFile TrackContentObjectView::createTCODataFiles( - const QVector & tcoViews) const -{ - Track * t = m_trackView->getTrack(); - TrackContainer * tc = t->trackContainer(); - DataFile dataFile( DataFile::DragNDropData ); - QDomElement tcoParent = dataFile.createElement( "tcos" ); - - typedef QVector tcoViewVector; - for( tcoViewVector::const_iterator it = tcoViews.begin(); - it != tcoViews.end(); ++it ) - { - // Insert into the dom under the "tcos" element - Track* tcoTrack = ( *it )->m_trackView->getTrack(); - int trackIndex = tc->tracks().indexOf( tcoTrack ); - QDomElement tcoElement = dataFile.createElement( "tco" ); - tcoElement.setAttribute( "trackIndex", trackIndex ); - tcoElement.setAttribute( "trackType", tcoTrack->type() ); - tcoElement.setAttribute( "trackName", tcoTrack->name() ); - ( *it )->m_tco->saveState( dataFile, tcoElement ); - tcoParent.appendChild( tcoElement ); - } - - dataFile.content().appendChild( tcoParent ); - - // Add extra metadata needed for calculations later - int initialTrackIndex = tc->tracks().indexOf( t ); - if( initialTrackIndex < 0 ) - { - printf("Failed to find selected track in the TrackContainer.\n"); - return dataFile; - } - QDomElement metadata = dataFile.createElement( "copyMetadata" ); - // initialTrackIndex is the index of the track that was touched - metadata.setAttribute( "initialTrackIndex", initialTrackIndex ); - metadata.setAttribute( "trackContainerId", tc->id() ); - // grabbedTCOPos is the pos of the bar containing the TCO we grabbed - metadata.setAttribute( "grabbedTCOPos", m_tco->startPosition() ); - - dataFile.content().appendChild( metadata ); - - return dataFile; -} - -void TrackContentObjectView::paintTextLabel(QString const & text, QPainter & painter) -{ - if (text.trimmed() == "") - { - return; - } - - painter.setRenderHint( QPainter::TextAntialiasing ); - - QFont labelFont = this->font(); - labelFont.setHintingPreference( QFont::PreferFullHinting ); - painter.setFont( labelFont ); - - const int textTop = TCO_BORDER_WIDTH + 1; - const int textLeft = TCO_BORDER_WIDTH + 3; - - QFontMetrics fontMetrics(labelFont); - QString elidedPatternName = fontMetrics.elidedText(text, Qt::ElideMiddle, width() - 2 * textLeft); - - if (elidedPatternName.length() < 2) - { - elidedPatternName = text.trimmed(); - } - - painter.fillRect(QRect(0, 0, width(), fontMetrics.height() + 2 * textTop), textBackgroundColor()); - - int const finalTextTop = textTop + fontMetrics.ascent(); - painter.setPen(textShadowColor()); - painter.drawText( textLeft + 1, finalTextTop + 1, elidedPatternName ); - painter.setPen( textColor() ); - painter.drawText( textLeft, finalTextTop, elidedPatternName ); -} - -/*! \brief Handle a mouse press on this trackContentObjectView. - * - * Handles the various ways in which a trackContentObjectView can be - * used with a click of a mouse button. - * - * * If our container supports rubber band selection then handle - * selection events. - * * or if shift-left button, add this object to the selection - * * or if ctrl-left button, start a drag-copy event - * * or if just plain left button, resize if we're resizeable - * * or if ctrl-middle button, mute the track content object - * * or if middle button, maybe delete the track content object. - * - * \param me The QMouseEvent to handle. - */ -void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) -{ - // Right now, active is only used on right/mid clicks actions, so we use a ternary operator - // to avoid the overhead of calling getClickedTCOs when it's not used - auto active = me->button() == Qt::LeftButton - ? QVector() - : getClickedTCOs(); - - setInitialPos( me->pos() ); - setInitialOffsets(); - if( !fixedTCOs() && me->button() == Qt::LeftButton ) - { - if( me->modifiers() & Qt::ControlModifier ) - { - if( isSelected() ) - { - m_action = CopySelection; - } - else - { - m_action = ToggleSelected; - } - } - else if( !me->modifiers() - || (me->modifiers() & Qt::AltModifier) - || (me->modifiers() & Qt::ShiftModifier) ) - { - if( isSelected() ) - { - m_action = MoveSelection; - } - else - { - gui->songEditor()->m_editor->selectAllTcos( false ); - m_tco->addJournalCheckPoint(); - - // move or resize - m_tco->setJournalling( false ); - - setInitialPos( me->pos() ); - setInitialOffsets(); - - SampleTCO * sTco = dynamic_cast( m_tco ); - if( me->x() < RESIZE_GRIP_WIDTH && sTco - && !m_tco->getAutoResize() ) - { - m_action = ResizeLeft; - setCursor( Qt::SizeHorCursor ); - } - else if( m_tco->getAutoResize() || me->x() < width() - RESIZE_GRIP_WIDTH ) - { - m_action = Move; - setCursor( Qt::SizeAllCursor ); - } - else - { - m_action = Resize; - setCursor( Qt::SizeHorCursor ); - } - - if( m_action == Move ) - { - s_textFloat->setTitle( tr( "Current position" ) ); - s_textFloat->setText( QString( "%1:%2" ). - arg( m_tco->startPosition().getBar() + 1 ). - arg( m_tco->startPosition().getTicks() % - TimePos::ticksPerBar() ) ); - } - else if( m_action == Resize || m_action == ResizeLeft ) - { - s_textFloat->setTitle( tr( "Current length" ) ); - s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). - arg( m_tco->length().getBar() ). - arg( m_tco->length().getTicks() % - TimePos::ticksPerBar() ). - arg( m_tco->startPosition().getBar() + 1 ). - arg( m_tco->startPosition().getTicks() % - TimePos::ticksPerBar() ). - arg( m_tco->endPosition().getBar() + 1 ). - arg( m_tco->endPosition().getTicks() % - TimePos::ticksPerBar() ) ); - } - // s_textFloat->reparent( this ); - // setup text-float as if TCO was already moved/resized - s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); - s_textFloat->show(); - } - - delete m_hint; - QString hint = m_action == Move || m_action == MoveSelection - ? tr( "Press <%1> and drag to make a copy." ) - : tr( "Press <%1> for free resizing." ); - m_hint = TextFloat::displayMessage( tr( "Hint" ), hint.arg(UI_CTRL_KEY), - embed::getIconPixmap( "hint" ), 0 ); - } - } - else if( me->button() == Qt::RightButton ) - { - if( me->modifiers() & Qt::ControlModifier ) - { - toggleMute( active ); - } - else if( me->modifiers() & Qt::ShiftModifier && !fixedTCOs() ) - { - remove( active ); - } - } - else if( me->button() == Qt::MidButton ) - { - if( me->modifiers() & Qt::ControlModifier ) - { - toggleMute( active ); - } - else if( !fixedTCOs() ) - { - remove( active ); - } - } -} - - - - -/*! \brief Handle a mouse movement (drag) on this trackContentObjectView. - * - * Handles the various ways in which a trackContentObjectView can be - * used with a mouse drag. - * - * * If in move mode, move ourselves in the track, - * * or if in move-selection mode, move the entire selection, - * * or if in resize mode, resize ourselves, - * * otherwise ??? - * - * \param me The QMouseEvent to handle. - * \todo what does the final else case do here? - */ -void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) -{ - if( m_action == CopySelection || m_action == ToggleSelected ) - { - if( mouseMovedDistance( me, 2 ) == true ) - { - QVector tcoViews; - if( m_action == CopySelection ) - { - // Collect all selected TCOs - QVector so = - m_trackView->trackContainerView()->selectedObjects(); - for( auto it = so.begin(); it != so.end(); ++it ) - { - TrackContentObjectView * tcov = - dynamic_cast( *it ); - if( tcov != NULL ) - { - tcoViews.push_back( tcov ); - } - } - } - else - { - gui->songEditor()->m_editor->selectAllTcos( false ); - tcoViews.push_back( this ); - } - // Clear the action here because mouseReleaseEvent will not get - // triggered once we go into drag. - m_action = NoAction; - - // Write the TCOs to the DataFile for copying - DataFile dataFile = createTCODataFiles( tcoViews ); - - // TODO -- thumbnail for all selected - QPixmap thumbnail = grab().scaled( - 128, 128, - Qt::KeepAspectRatio, - Qt::SmoothTransformation ); - new StringPairDrag( QString( "tco_%1" ).arg( - m_tco->getTrack()->type() ), - dataFile.toString(), thumbnail, this ); - } - } - - if( me->modifiers() & Qt::ControlModifier ) - { - delete m_hint; - m_hint = NULL; - } - - const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); - if( m_action == Move ) - { - TimePos newPos = draggedTCOPos( me ); - - m_tco->movePosition(newPos); - newPos = m_tco->startPosition(); // Get the real position the TCO was dragged to for the label - m_trackView->getTrackContentWidget()->changePosition(); - s_textFloat->setText( QString( "%1:%2" ). - arg( newPos.getBar() + 1 ). - arg( newPos.getTicks() % - TimePos::ticksPerBar() ) ); - s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) ); - } - else if( m_action == MoveSelection ) - { - // 1: Find the position we want to move the grabbed TCO to - TimePos newPos = draggedTCOPos( me ); - - // 2: Handle moving the other selected TCOs the same distance - QVector so = - m_trackView->trackContainerView()->selectedObjects(); - QVector tcos; // List of selected clips - int leftmost = 0; // Leftmost clip's offset from grabbed clip - // Populate tcos, find leftmost - for( QVector::iterator it = so.begin(); - it != so.end(); ++it ) - { - TrackContentObjectView * tcov = - dynamic_cast( *it ); - if( tcov == NULL ) { continue; } - tcos.push_back( tcov->m_tco ); - int index = std::distance( so.begin(), it ); - leftmost = min (leftmost, m_initialOffsets[index].getTicks() ); - } - // Make sure the leftmost clip doesn't get moved to a negative position - if ( newPos.getTicks() + leftmost < 0 ) { newPos = -leftmost; } - - for( QVector::iterator it = tcos.begin(); - it != tcos.end(); ++it ) - { - int index = std::distance( tcos.begin(), it ); - ( *it )->movePosition( newPos + m_initialOffsets[index] ); - } - } - else if( m_action == Resize || m_action == ResizeLeft ) - { - // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize - const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier); - const float snapSize = gui->songEditor()->m_editor->getSnapSize(); - // Length in ticks of one snap increment - const TimePos snapLength = TimePos( (int)(snapSize * TimePos::ticksPerBar()) ); - - if( m_action == Resize ) - { - // The clip's new length - TimePos l = static_cast( me->x() * TimePos::ticksPerBar() / ppb ); - - if ( unquantized ) - { // We want to preserve this adjusted offset, - // even if the user switches to snapping later - setInitialPos( m_initialMousePos ); - // Don't resize to less than 1 tick - m_tco->changeLength( qMax( 1, l ) ); - } - else if ( me->modifiers() & Qt::ShiftModifier ) - { // If shift is held, quantize clip's end position - TimePos end = TimePos( m_initialTCOPos + l ).quantize( snapSize ); - // The end position has to be after the clip's start - TimePos min = m_initialTCOPos.quantize( snapSize ); - if ( min <= m_initialTCOPos ) min += snapLength; - m_tco->changeLength( qMax(min - m_initialTCOPos, end - m_initialTCOPos) ); - } - else - { // Otherwise, resize in fixed increments - TimePos initialLength = m_initialTCOEnd - m_initialTCOPos; - TimePos offset = TimePos( l - initialLength ).quantize( snapSize ); - // Don't resize to less than 1 tick - TimePos min = TimePos( initialLength % snapLength ); - if (min < 1) min += snapLength; - m_tco->changeLength( qMax( min, initialLength + offset) ); - } - } - else - { - SampleTCO * sTco = dynamic_cast( m_tco ); - if( sTco ) - { - const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); - - TimePos t = qMax( 0, (int) - m_trackView->trackContainerView()->currentPosition() + - static_cast( x * TimePos::ticksPerBar() / ppb ) ); - - if( unquantized ) - { // We want to preserve this adjusted offset, - // even if the user switches to snapping later - setInitialPos( m_initialMousePos ); - //Don't resize to less than 1 tick - t = qMin( m_initialTCOEnd - 1, t); - } - else if( me->modifiers() & Qt::ShiftModifier ) - { // If shift is held, quantize clip's start position - // Don't let the start position move past the end position - TimePos max = m_initialTCOEnd.quantize( snapSize ); - if ( max >= m_initialTCOEnd ) max -= snapLength; - t = qMin( max, t.quantize( snapSize ) ); - } - else - { // Otherwise, resize in fixed increments - // Don't resize to less than 1 tick - TimePos initialLength = m_initialTCOEnd - m_initialTCOPos; - TimePos minLength = TimePos( initialLength % snapLength ); - if (minLength < 1) minLength += snapLength; - TimePos offset = TimePos(t - m_initialTCOPos).quantize( snapSize ); - t = qMin( m_initialTCOEnd - minLength, m_initialTCOPos + offset ); - } - - TimePos oldPos = m_tco->startPosition(); - if( m_tco->length() + ( oldPos - t ) >= 1 ) - { - m_tco->movePosition( t ); - m_tco->changeLength( m_tco->length() + ( oldPos - t ) ); - sTco->setStartTimeOffset( sTco->startTimeOffset() + ( oldPos - t ) ); - } - } - } - s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). - arg( m_tco->length().getBar() ). - arg( m_tco->length().getTicks() % - TimePos::ticksPerBar() ). - arg( m_tco->startPosition().getBar() + 1 ). - arg( m_tco->startPosition().getTicks() % - TimePos::ticksPerBar() ). - arg( m_tco->endPosition().getBar() + 1 ). - arg( m_tco->endPosition().getTicks() % - TimePos::ticksPerBar() ) ); - s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); - } - else - { - SampleTCO * sTco = dynamic_cast( m_tco ); - if( ( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() ) - || ( me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco && !m_tco->getAutoResize() ) ) - { - setCursor( Qt::SizeHorCursor ); - } - else - { - leaveEvent( NULL ); - } - } -} - - - - -/*! \brief Handle a mouse release on this trackContentObjectView. - * - * If we're in move or resize mode, journal the change as appropriate. - * Then tidy up. - * - * \param me The QMouseEvent to handle. - */ -void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me ) -{ - // If the CopySelection was chosen as the action due to mouse movement, - // it will have been cleared. At this point Toggle is the desired action. - // An active StringPairDrag will prevent this method from being called, - // so a real CopySelection would not have occurred. - if( m_action == CopySelection || - ( m_action == ToggleSelected && mouseMovedDistance( me, 2 ) == false ) ) - { - setSelected( !isSelected() ); - } - - if( m_action == Move || m_action == Resize || m_action == ResizeLeft ) - { - // TODO: Fix m_tco->setJournalling() consistency - m_tco->setJournalling( true ); - } - m_action = NoAction; - delete m_hint; - m_hint = NULL; - s_textFloat->hide(); - leaveEvent( NULL ); - selectableObject::mouseReleaseEvent( me ); -} - - - - -/*! \brief Set up the context menu for this trackContentObjectView. - * - * Set up the various context menu events that can apply to a - * track content object view. - * - * \param cme The QContextMenuEvent to add the actions to. - */ -void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) -{ - // Depending on whether we right-clicked a selection or an individual TCO we will have - // different labels for the actions. - bool individualTCO = getClickedTCOs().size() <= 1; - - if( cme->modifiers() ) - { - return; - } - - QMenu contextMenu( this ); - - if( fixedTCOs() == false ) - { - contextMenu.addAction( - embed::getIconPixmap( "cancel" ), - individualTCO - ? tr("Delete (middle mousebutton)") - : tr("Delete selection (middle mousebutton)"), - [this](){ contextMenuAction( Remove ); } ); - - contextMenu.addSeparator(); - - contextMenu.addAction( - embed::getIconPixmap( "edit_cut" ), - individualTCO - ? tr("Cut") - : tr("Cut selection"), - [this](){ contextMenuAction( Cut ); } ); - } - - contextMenu.addAction( - embed::getIconPixmap( "edit_copy" ), - individualTCO - ? tr("Copy") - : tr("Copy selection"), - [this](){ contextMenuAction( Copy ); } ); - - contextMenu.addAction( - embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), - [this](){ contextMenuAction( Paste ); } ); - - contextMenu.addSeparator(); - - contextMenu.addAction( - embed::getIconPixmap( "muted" ), - (individualTCO - ? tr("Mute/unmute (<%1> + middle click)") - : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), - [this](){ contextMenuAction( Mute ); } ); - - contextMenu.addSeparator(); - - contextMenu.addAction( embed::getIconPixmap( "colorize" ), - tr( "Set clip color" ), this, SLOT( changeClipColor() ) ); - contextMenu.addAction( embed::getIconPixmap( "colorize" ), - tr( "Use track color" ), this, SLOT( useTrackColor() ) ); - - constructContextMenu( &contextMenu ); - - contextMenu.exec( QCursor::pos() ); -} - -// This method processes the actions from the context menu of the TCO View. -void TrackContentObjectView::contextMenuAction( ContextMenuAction action ) -{ - QVector active = getClickedTCOs(); - // active will be later used for the remove, copy, cut or toggleMute methods - - switch( action ) - { - case Remove: - remove( active ); - break; - case Cut: - cut( active ); - break; - case Copy: - copy( active ); - break; - case Paste: - paste(); - break; - case Mute: - toggleMute( active ); - break; - } -} - -QVector TrackContentObjectView::getClickedTCOs() -{ - // Get a list of selected selectableObjects - QVector sos = gui->songEditor()->m_editor->selectedObjects(); - - // Convert to a list of selected TCOVs - QVector selection; - selection.reserve( sos.size() ); - for( auto so: sos ) - { - TrackContentObjectView *tcov = dynamic_cast ( so ); - if( tcov != nullptr ) - { - selection.append( tcov ); - } - } - - // If we clicked part of the selection, affect all selected clips. Otherwise affect the clip we clicked - return selection.contains(this) - ? selection - : QVector( 1, this ); -} - -void TrackContentObjectView::remove( QVector tcovs ) -{ - for( auto tcov: tcovs ) - { - // No need to check if it's nullptr because we check when building the QVector - tcov->remove(); - } -} - -void TrackContentObjectView::copy( QVector tcovs ) -{ - // For copyStringPair() - using namespace Clipboard; - - // Write the TCOs to a DataFile for copying - DataFile dataFile = createTCODataFiles( tcovs ); - - // Copy the TCO type as a key and the TCO data file to the clipboard - copyStringPair( QString( "tco_%1" ).arg( m_tco->getTrack()->type() ), - dataFile.toString() ); -} - -void TrackContentObjectView::cut( QVector tcovs ) -{ - // Copy the selected TCOs - copy( tcovs ); - - // Now that the TCOs are copied we can delete them, since we are cutting - remove( tcovs ); -} - -void TrackContentObjectView::paste() -{ - // For getMimeData() - using namespace Clipboard; - - // If possible, paste the selection on the TimePos of the selected Track and remove it - TimePos tcoPos = TimePos( m_tco->startPosition() ); - - TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); - - if( tcw->pasteSelection( tcoPos, getMimeData() ) ) - { - // If we succeed on the paste we delete the TCO we pasted on - remove(); - } -} - -void TrackContentObjectView::toggleMute( QVector tcovs ) -{ - for( auto tcov: tcovs ) - { - // No need to check for nullptr because we check while building the tcovs QVector - tcov->getTrackContentObject()->toggleMute(); - } -} - - - - -/*! \brief How many pixels a bar takes for this trackContentObjectView. - * - * \return the number of pixels per bar. - */ -float TrackContentObjectView::pixelsPerBar() -{ - return m_trackView->trackContainerView()->pixelsPerBar(); -} - - -/*! \brief Save the offsets between all selected tracks and a clicked track */ -void TrackContentObjectView::setInitialOffsets() -{ - QVector so = m_trackView->trackContainerView()->selectedObjects(); - QVector offsets; - for( QVector::iterator it = so.begin(); - it != so.end(); ++it ) - { - TrackContentObjectView * tcov = - dynamic_cast( *it ); - if( tcov == NULL ) - { - continue; - } - offsets.push_back( tcov->m_tco->startPosition() - m_initialTCOPos ); - } - - m_initialOffsets = offsets; -} - - - - -/*! \brief Detect whether the mouse moved more than n pixels on screen. - * - * \param _me The QMouseEvent. - * \param distance The threshold distance that the mouse has moved to return true. - */ -bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance ) -{ - QPoint dPos = mapToGlobal( me->pos() ) - m_initialMouseGlobalPos; - const int pixelsMoved = dPos.manhattanLength(); - return ( pixelsMoved > distance || pixelsMoved < -distance ); -} - - - -/*! \brief Calculate the new position of a dragged TCO from a mouse event - * - * - * \param me The QMouseEvent - */ -TimePos TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) -{ - //Pixels per bar - const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); - // The pixel distance that the mouse has moved - const int mouseOff = mapToGlobal(me->pos()).x() - m_initialMouseGlobalPos.x(); - TimePos newPos = m_initialTCOPos + mouseOff * TimePos::ticksPerBar() / ppb; - TimePos offset = newPos - m_initialTCOPos; - // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize - if ( me->button() != Qt::NoButton - || (me->modifiers() & Qt::ControlModifier) - || (me->modifiers() & Qt::AltModifier) ) - { - // We want to preserve this adjusted offset, - // even if the user switches to snapping - setInitialPos( m_initialMousePos ); - } - else if ( me->modifiers() & Qt::ShiftModifier ) - { // If shift is held, quantize position (Default in 1.2.0 and earlier) - // or end position, whichever is closest to the actual position - TimePos startQ = newPos.quantize( gui->songEditor()->m_editor->getSnapSize() ); - // Find start position that gives snapped clip end position - TimePos endQ = ( newPos + m_tco->length() ); - endQ = endQ.quantize( gui->songEditor()->m_editor->getSnapSize() ); - endQ = endQ - m_tco->length(); - // Select the position closest to actual position - if ( abs(newPos - startQ) < abs(newPos - endQ) ) newPos = startQ; - else newPos = endQ; - } - else - { // Otherwise, quantize moved distance (preserves user offsets) - newPos = m_initialTCOPos + offset.quantize( gui->songEditor()->m_editor->getSnapSize() ); - } - return newPos; -} - - -// Return the color that the TCO's background should be -QColor TrackContentObjectView::getColorForDisplay( QColor defaultColor ) -{ - // Get the pure TCO color - auto tcoColor = m_tco->hasColor() - ? m_tco->usesCustomClipColor() - ? m_tco->color() - : m_tco->getTrack()->color() - : defaultColor; - - // Set variables - QColor c, mutedCustomColor; - bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); - mutedCustomColor = tcoColor; - mutedCustomColor.setHsv( mutedCustomColor.hsvHue(), mutedCustomColor.hsvSaturation() / 4, mutedCustomColor.value() ); - - // Change the pure color by state: selected, muted, colored, normal - if( isSelected() ) - { - c = m_tco->hasColor() - ? ( muted - ? mutedCustomColor.darker( 350 ) - : tcoColor.darker( 150 ) ) - : selectedColor(); - } - else - { - if( muted ) - { - c = m_tco->hasColor() - ? mutedCustomColor.darker( 250 ) - : mutedBackgroundColor(); - } - else - { - c = tcoColor; - } - } - - // Return color to caller - return c; -} - - - - - -// =========================================================================== -// trackContentWidget -// =========================================================================== -/*! \brief Create a new trackContentWidget - * - * Creates a new track content widget for the given track. - * The content widget comprises the 'grip bar' and the 'tools' button - * for the track's context menu. - * - * \param parent The parent track. - */ -TrackContentWidget::TrackContentWidget( TrackView * parent ) : - QWidget( parent ), - m_trackView( parent ), - m_darkerColor( Qt::SolidPattern ), - m_lighterColor( Qt::SolidPattern ), - m_gridColor( Qt::SolidPattern ), - m_embossColor( Qt::SolidPattern ) -{ - setAcceptDrops( true ); - - connect( parent->trackContainerView(), - SIGNAL( positionChanged( const TimePos & ) ), - this, SLOT( changePosition( const TimePos & ) ) ); - - setStyle( QApplication::style() ); - - updateBackground(); -} - - - - -/*! \brief Destroy this trackContentWidget - * - * Destroys the trackContentWidget. - */ -TrackContentWidget::~TrackContentWidget() -{ -} - - - - -void TrackContentWidget::updateBackground() -{ - const TrackContainerView * tcv = m_trackView->trackContainerView(); - - // Assume even-pixels-per-bar. Makes sense, should be like this anyways - int ppb = static_cast( tcv->pixelsPerBar() ); - - int w = ppb * BARS_PER_GROUP; - int h = height(); - m_background = QPixmap( w * 2, height() ); - QPainter pmp( &m_background ); - - pmp.fillRect( 0, 0, w, h, darkerColor() ); - pmp.fillRect( w, 0, w , h, lighterColor() ); - - // draw lines - // vertical lines - pmp.setPen( QPen( gridColor(), 1 ) ); - for( float x = 0; x < w * 2; x += ppb ) - { - pmp.drawLine( QLineF( x, 0.0, x, h ) ); - } - - pmp.setPen( QPen( embossColor(), 1 ) ); - for( float x = 1.0; x < w * 2; x += ppb ) - { - pmp.drawLine( QLineF( x, 0.0, x, h ) ); - } - - // horizontal line - pmp.setPen( QPen( gridColor(), 1 ) ); - pmp.drawLine( 0, h-1, w*2, h-1 ); - - pmp.end(); - - // Force redraw - update(); -} - - - - -/*! \brief Adds a trackContentObjectView to this widget. - * - * Adds a(nother) trackContentObjectView to our list of views. We also - * check that our position is up-to-date. - * - * \param tcov The trackContentObjectView to add. - */ -void TrackContentWidget::addTCOView( TrackContentObjectView * tcov ) -{ - TrackContentObject * tco = tcov->getTrackContentObject(); - - m_tcoViews.push_back( tcov ); - - tco->saveJournallingState( false ); - changePosition(); - tco->restoreJournallingState(); -} - - - - -/*! \brief Removes the given trackContentObjectView to this widget. - * - * Removes the given trackContentObjectView from our list of views. - * - * \param tcov The trackContentObjectView to add. - */ -void TrackContentWidget::removeTCOView( TrackContentObjectView * tcov ) -{ - tcoViewVector::iterator it = std::find( m_tcoViews.begin(), - m_tcoViews.end(), - tcov ); - if( it != m_tcoViews.end() ) - { - m_tcoViews.erase( it ); - Engine::getSong()->setModified(); - } -} - - - - -/*! \brief Update ourselves by updating all the tCOViews attached. - * - */ -void TrackContentWidget::update() -{ - for( tcoViewVector::iterator it = m_tcoViews.begin(); - it != m_tcoViews.end(); ++it ) - { - ( *it )->setFixedHeight( height() - 1 ); - ( *it )->update(); - } - QWidget::update(); -} - - - - -// resposible for moving track-content-widgets to appropriate position after -// change of visible viewport -/*! \brief Move the trackContentWidget to a new place in time - * - * \param newPos The MIDI time to move to. - */ -void TrackContentWidget::changePosition( const TimePos & newPos ) -{ - if( m_trackView->trackContainerView() == gui->getBBEditor()->trackContainerView() ) - { - const int curBB = Engine::getBBTrackContainer()->currentBB(); - setUpdatesEnabled( false ); - - // first show TCO for current BB... - for( tcoViewVector::iterator it = m_tcoViews.begin(); - it != m_tcoViews.end(); ++it ) - { - if( ( *it )->getTrackContentObject()-> - startPosition().getBar() == curBB ) - { - ( *it )->move( 0, ( *it )->y() ); - ( *it )->raise(); - ( *it )->show(); - } - else - { - ( *it )->lower(); - } - } - // ...then hide others to avoid flickering - for( tcoViewVector::iterator it = m_tcoViews.begin(); - it != m_tcoViews.end(); ++it ) - { - if( ( *it )->getTrackContentObject()-> - startPosition().getBar() != curBB ) - { - ( *it )->hide(); - } - } - setUpdatesEnabled( true ); - return; - } - - TimePos pos = newPos; - if( pos < 0 ) - { - pos = m_trackView->trackContainerView()->currentPosition(); - } - - const int begin = pos; - const int end = endPosition( pos ); - const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); - - setUpdatesEnabled( false ); - for( tcoViewVector::iterator it = m_tcoViews.begin(); - it != m_tcoViews.end(); ++it ) - { - TrackContentObjectView * tcov = *it; - TrackContentObject * tco = tcov->getTrackContentObject(); - - tco->changeLength( tco->length() ); - - const int ts = tco->startPosition(); - const int te = tco->endPosition()-3; - if( ( ts >= begin && ts <= end ) || - ( te >= begin && te <= end ) || - ( ts <= begin && te >= end ) ) - { - tcov->move( static_cast( ( ts - begin ) * ppb / - TimePos::ticksPerBar() ), - tcov->y() ); - if( !tcov->isVisible() ) - { - tcov->show(); - } - } - else - { - tcov->move( -tcov->width()-10, tcov->y() ); - } - } - setUpdatesEnabled( true ); - - // redraw background -// update(); -} - - - - -/*! \brief Return the position of the trackContentWidget in bars. - * - * \param mouseX the mouse's current X position in pixels. - */ -TimePos TrackContentWidget::getPosition( int mouseX ) -{ - TrackContainerView * tv = m_trackView->trackContainerView(); - return TimePos( tv->currentPosition() + - mouseX * - TimePos::ticksPerBar() / - static_cast( tv->pixelsPerBar() ) ); -} - - - - -/*! \brief Respond to a drag enter event on the trackContentWidget - * - * \param dee the Drag Enter Event to respond to - */ -void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) -{ - TimePos tcoPos = getPosition( dee->pos().x() ); - if( canPasteSelection( tcoPos, dee ) == false ) - { - dee->ignore(); - } - else - { - StringPairDrag::processDragEnterEvent( dee, "tco_" + - QString::number( getTrack()->type() ) ); - } -} - - - - -/*! \brief Returns whether a selection of TCOs can be pasted into this - * - * \param tcoPos the position of the TCO slot being pasted on - * \param de the DropEvent generated - */ -bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QDropEvent* de ) -{ - const QMimeData * mimeData = de->mimeData(); - - // If the source of the DropEvent is the current instance of LMMS we don't allow pasting in the same bar - // if it's another instance of LMMS we allow it - return de->source() - ? canPasteSelection( tcoPos, mimeData ) - : canPasteSelection( tcoPos, mimeData, true ); -} - -// Overloaded method to make it possible to call this method without a Drag&Drop event -bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QMimeData* md , bool allowSameBar ) -{ - // For decodeKey() and decodeValue() - using namespace Clipboard; - - Track * t = getTrack(); - QString type = decodeKey( md ); - QString value = decodeValue( md ); - - // We can only paste into tracks of the same type - if( type != ( "tco_" + QString::number( t->type() ) ) || - m_trackView->trackContainerView()->fixedTCOs() == true ) - { - return false; - } - - // value contains XML needed to reconstruct TCOs and place them - DataFile dataFile( value.toUtf8() ); - - // Extract the metadata and which TCO was grabbed - QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" ); - QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); - TimePos grabbedTCOPos = tcoPosAttr.value().toInt(); - TimePos grabbedTCOBar = TimePos( grabbedTCOPos.getBar(), 0 ); - - // Extract the track index that was originally clicked - QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); - const int initialTrackIndex = tiAttr.value().toInt(); - - // Get the current track's index - const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); - const int currentTrackIndex = tracks.indexOf( t ); - - // Don't paste if we're on the same bar and allowSameBar is false - auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); - if( !allowSameBar && sourceTrackContainerId == t->trackContainer()->id() && - tcoPos == grabbedTCOBar && currentTrackIndex == initialTrackIndex ) - { - return false; - } - - // Extract the tco data - QDomElement tcoParent = dataFile.content().firstChildElement( "tcos" ); - QDomNodeList tcoNodes = tcoParent.childNodes(); - - // Determine if all the TCOs will land on a valid track - for( int i = 0; i < tcoNodes.length(); i++ ) - { - QDomElement tcoElement = tcoNodes.item( i ).toElement(); - int trackIndex = tcoElement.attributeNode( "trackIndex" ).value().toInt(); - int finalTrackIndex = trackIndex + currentTrackIndex - initialTrackIndex; - - // Track must be in TrackContainer's tracks - if( finalTrackIndex < 0 || finalTrackIndex >= tracks.size() ) - { - return false; - } - - // Track must be of the same type - auto startTrackType = tcoElement.attributeNode("trackType").value().toInt(); - Track * endTrack = tracks.at( finalTrackIndex ); - if( startTrackType != endTrack->type() ) - { - return false; - } - } - - return true; -} - -/*! \brief Pastes a selection of TCOs onto the track - * - * \param tcoPos the position of the TCO slot being pasted on - * \param de the DropEvent generated - */ -bool TrackContentWidget::pasteSelection( TimePos tcoPos, QDropEvent * de ) -{ - const QMimeData * mimeData = de->mimeData(); - - if( canPasteSelection( tcoPos, de ) == false ) - { - return false; - } - - // We set skipSafetyCheck to true because we already called canPasteSelection - return pasteSelection( tcoPos, mimeData, true ); -} - -// Overloaded method so we can call it without a Drag&Drop event -bool TrackContentWidget::pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck ) -{ - // For decodeKey() and decodeValue() - using namespace Clipboard; - - // When canPasteSelection was already called before, skipSafetyCheck will skip this - if( !skipSafetyCheck && canPasteSelection( tcoPos, md ) == false ) - { - return false; - } - - QString type = decodeKey( md ); - QString value = decodeValue( md ); - - getTrack()->addJournalCheckPoint(); - - // value contains XML needed to reconstruct TCOs and place them - DataFile dataFile( value.toUtf8() ); - - // Extract the tco data - QDomElement tcoParent = dataFile.content().firstChildElement( "tcos" ); - QDomNodeList tcoNodes = tcoParent.childNodes(); - - // Extract the track index that was originally clicked - QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" ); - QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); - int initialTrackIndex = tiAttr.value().toInt(); - QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); - TimePos grabbedTCOPos = tcoPosAttr.value().toInt(); - - // Snap the mouse position to the beginning of the dropped bar, in ticks - const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks(); - const int currentTrackIndex = tracks.indexOf( getTrack() ); - - bool wasSelection = m_trackView->trackContainerView()->rubberBand()->selectedObjects().count(); - - // Unselect the old group - const QVector so = - m_trackView->trackContainerView()->selectedObjects(); - for( QVector::const_iterator it = so.begin(); - it != so.end(); ++it ) - { - ( *it )->setSelected( false ); - } - - - // TODO -- Need to draw the hovericon either way, or ghost the TCOs - // onto their final position. - - float snapSize = gui->songEditor()->m_editor->getSnapSize(); - // All patterns should be offset the same amount as the grabbed pattern - TimePos offset = TimePos(tcoPos - grabbedTCOPos); - // Users expect clips to "fall" backwards, so bias the offset - offset = offset - TimePos::ticksPerBar() * snapSize / 2; - // The offset is quantized (rather than the positions) to preserve fine adjustments - offset = offset.quantize(snapSize); - - // Get the leftmost TCO and fix the offset if it reaches below bar 0 - TimePos leftmostPos = grabbedTCOPos; - for(int i = 0; i < tcoNodes.length(); ++i) - { - QDomElement outerTCOElement = tcoNodes.item(i).toElement(); - QDomElement tcoElement = outerTCOElement.firstChildElement(); - - TimePos pos = tcoElement.attributeNode("pos").value().toInt(); - - if(pos < leftmostPos) { leftmostPos = pos; } - } - // Fix offset if it sets the left most TCO to a negative position - offset = std::max(offset.getTicks(), -leftmostPos.getTicks()); - - for( int i = 0; isongEditor()->m_editor->getSnapSize(); - if (offset == 0) { pos += shift; } - - TrackContentObject * tco = t->createTCO( pos ); - tco->restoreState( tcoElement ); - tco->movePosition(pos); // Because we restored the state, we need to move the TCO again. - if( wasSelection ) - { - tco->selectViewOnCreate( true ); - } - } - - AutomationPattern::resolveAllIDs(); - - return true; -} - - -/*! \brief Respond to a drop event on the trackContentWidget - * - * \param de the Drop Event to respond to - */ -void TrackContentWidget::dropEvent( QDropEvent * de ) -{ - TimePos tcoPos = TimePos( getPosition( de->pos().x() ) ); - if( pasteSelection( tcoPos, de ) == true ) - { - de->accept(); - } -} - - - - -/*! \brief Respond to a mouse press on the trackContentWidget - * - * \param me the mouse press event to respond to - */ -void TrackContentWidget::mousePressEvent( QMouseEvent * me ) -{ - if( m_trackView->trackContainerView()->allowRubberband() == true ) - { - QWidget::mousePressEvent( me ); - } - else if( me->modifiers() & Qt::ShiftModifier ) - { - QWidget::mousePressEvent( me ); - } - else if( me->button() == Qt::LeftButton && - !m_trackView->trackContainerView()->fixedTCOs() ) - { - QVector so = m_trackView->trackContainerView()->rubberBand()->selectedObjects(); - for( int i = 0; i < so.count(); ++i ) - { - so.at( i )->setSelected( false); - } - getTrack()->addJournalCheckPoint(); - const TimePos pos = getPosition( me->x() ).getBar() * - TimePos::ticksPerBar(); - getTrack()->createTCO(pos); - } -} - - - - -/*! \brief Repaint the trackContentWidget on command - * - * \param pe the Paint Event to respond to - */ -void TrackContentWidget::paintEvent( QPaintEvent * pe ) -{ - // Assume even-pixels-per-bar. Makes sense, should be like this anyways - const TrackContainerView * tcv = m_trackView->trackContainerView(); - int ppb = static_cast( tcv->pixelsPerBar() ); - QPainter p( this ); - // Don't draw background on BB-Editor - if( m_trackView->trackContainerView() != gui->getBBEditor()->trackContainerView() ) - { - p.drawTiledPixmap( rect(), m_background, QPoint( - tcv->currentPosition().getBar() * ppb, 0 ) ); - } -} - - - - -/*! \brief Updates the background tile pixmap on size changes. - * - * \param resizeEvent the resize event to pass to base class - */ -void TrackContentWidget::resizeEvent( QResizeEvent * resizeEvent ) -{ - // Update backgroud - updateBackground(); - // Force redraw - QWidget::resizeEvent( resizeEvent ); -} - - - - -/*! \brief Return the track shown by the trackContentWidget - * - */ -Track * TrackContentWidget::getTrack() -{ - return m_trackView->getTrack(); -} - - - - -/*! \brief Return the end position of the trackContentWidget in Bars. - * - * \param posStart the starting position of the Widget (from getPosition()) - */ -TimePos TrackContentWidget::endPosition( const TimePos & posStart ) -{ - const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); - const int w = width(); - return posStart + static_cast( w * TimePos::ticksPerBar() / ppb ); -} - -void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) -{ - // For hasFormat(), MimeType enum class and getMimeData() - using namespace Clipboard; - - if( cme->modifiers() ) - { - return; - } - - // If we don't have TCO data in the clipboard there's no need to create this menu - // since "paste" is the only action at the moment. - if( ! hasFormat( MimeType::StringPair ) ) - { - return; - } - - QMenu contextMenu( this ); - QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } ); - // If we can't paste in the current TCW for some reason, disable the action so the user knows - pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), getMimeData() ) ? true : false ); - - contextMenu.exec( QCursor::pos() ); -} - -void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ) -{ - // For getMimeData() - using namespace Clipboard; - - switch( action ) - { - case Paste: - // Paste the selection on the TimePos of the context menu event - TimePos tcoPos = getPosition( cme->x() ); - - pasteSelection( tcoPos, getMimeData() ); - break; - } -} - - - -// qproperty access methods -//! \brief CSS theming qproperty access method -QBrush TrackContentWidget::darkerColor() const -{ return m_darkerColor; } - -//! \brief CSS theming qproperty access method -QBrush TrackContentWidget::lighterColor() const -{ return m_lighterColor; } - -//! \brief CSS theming qproperty access method -QBrush TrackContentWidget::gridColor() const -{ return m_gridColor; } - -//! \brief CSS theming qproperty access method -QBrush TrackContentWidget::embossColor() const -{ return m_embossColor; } - -//! \brief CSS theming qproperty access method -void TrackContentWidget::setDarkerColor( const QBrush & c ) -{ m_darkerColor = c; } - -//! \brief CSS theming qproperty access method -void TrackContentWidget::setLighterColor( const QBrush & c ) -{ m_lighterColor = c; } - -//! \brief CSS theming qproperty access method -void TrackContentWidget::setGridColor( const QBrush & c ) -{ m_gridColor = c; } - -//! \brief CSS theming qproperty access method -void TrackContentWidget::setEmbossColor( const QBrush & c ) -{ m_embossColor = c; } - - -// =========================================================================== -// trackOperationsWidget -// =========================================================================== - -/*! \brief Create a new trackOperationsWidget - * - * The trackOperationsWidget is the grip and the mute button of a track. - * - * \param parent the trackView to contain this widget - */ -TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : - QWidget( parent ), /*!< The parent widget */ - m_trackView( parent ) /*!< The parent track view */ -{ - ToolTip::add( this, tr( "Press <%1> while clicking on move-grip " - "to begin a new drag'n'drop action." ).arg(UI_CTRL_KEY) ); - - QMenu * toMenu = new QMenu( this ); - toMenu->setFont( pointSize<9>( toMenu->font() ) ); - connect( toMenu, SIGNAL( aboutToShow() ), this, SLOT( updateMenu() ) ); - - - setObjectName( "automationEnabled" ); - - - m_trackOps = new QPushButton( this ); - m_trackOps->move( 12, 1 ); - m_trackOps->setFocusPolicy( Qt::NoFocus ); - m_trackOps->setMenu( toMenu ); - ToolTip::add( m_trackOps, tr( "Actions" ) ); - - - m_muteBtn = new PixmapButton( this, tr( "Mute" ) ); - m_muteBtn->setActiveGraphic( embed::getIconPixmap( "led_off" ) ); - m_muteBtn->setInactiveGraphic( embed::getIconPixmap( "led_green" ) ); - m_muteBtn->setCheckable( true ); - - m_soloBtn = new PixmapButton( this, tr( "Solo" ) ); - m_soloBtn->setActiveGraphic( embed::getIconPixmap( "led_red" ) ); - m_soloBtn->setInactiveGraphic( embed::getIconPixmap( "led_off" ) ); - m_soloBtn->setCheckable( true ); - - if( ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ) - { - m_muteBtn->move( 46, 0 ); - m_soloBtn->move( 46, 16 ); - } - else - { - m_muteBtn->move( 46, 8 ); - m_soloBtn->move( 62, 8 ); - } - - m_muteBtn->show(); - ToolTip::add( m_muteBtn, tr( "Mute" ) ); - - m_soloBtn->show(); - ToolTip::add( m_soloBtn, tr( "Solo" ) ); - - connect( this, SIGNAL( trackRemovalScheduled( TrackView * ) ), - m_trackView->trackContainerView(), - SLOT( deleteTrackView( TrackView * ) ), - Qt::QueuedConnection ); - - connect( m_trackView->getTrack()->getMutedModel(), SIGNAL( dataChanged() ), - this, SLOT( update() ) ); - -} - - - - -/*! \brief Destroy an existing trackOperationsWidget - * - */ -TrackOperationsWidget::~TrackOperationsWidget() -{ -} - - - - -/*! \brief Respond to trackOperationsWidget mouse events - * - * If it's the left mouse button, and Ctrl is held down, and we're - * not a Beat+Bassline Editor track, then start a new drag event to - * copy this track. - * - * Otherwise, ignore all other events. - * - * \param me The mouse event to respond to. - */ -void TrackOperationsWidget::mousePressEvent( QMouseEvent * me ) -{ - if( me->button() == Qt::LeftButton && - me->modifiers() & Qt::ControlModifier && - m_trackView->getTrack()->type() != Track::BBTrack ) - { - DataFile dataFile( DataFile::DragNDropData ); - m_trackView->getTrack()->saveState( dataFile, dataFile.content() ); - new StringPairDrag( QString( "track_%1" ).arg( - m_trackView->getTrack()->type() ), - dataFile.toString(), m_trackView->getTrackSettingsWidget()->grab(), - this ); - } - else if( me->button() == Qt::LeftButton ) - { - // track-widget (parent-widget) initiates track-move - me->ignore(); - } -} - - - - -/*! \brief Repaint the trackOperationsWidget - * - * If we're not moving, and in the Beat+Bassline Editor, then turn - * automation on or off depending on its previous state and show - * ourselves. - * - * Otherwise, hide ourselves. - * - * \todo Flesh this out a bit - is it correct? - * \param pe The paint event to respond to - */ -void TrackOperationsWidget::paintEvent( QPaintEvent * pe ) -{ - QPainter p( this ); - - p.fillRect( rect(), palette().brush(QPalette::Background) ); - - if( m_trackView->getTrack()->useColor() && ! m_trackView->getTrack()->getMutedModel()->value() ) - { - QRect coloredRect( 0, 0, 10, m_trackView->getTrack()->getHeight() ); - - p.fillRect( coloredRect, m_trackView->getTrack()->color() ); - } - - if( m_trackView->isMovingTrack() == false ) - { - p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip")); - } - else - { - p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip_c")); - } -} - - - - -/*! \brief Clone this track - * - */ -void TrackOperationsWidget::cloneTrack() -{ - TrackContainerView *tcView = m_trackView->trackContainerView(); - - Track *newTrack = m_trackView->getTrack()->clone(); - TrackView *newTrackView = tcView->createTrackView( newTrack ); - - int index = tcView->trackViews().indexOf( m_trackView ); - int i = tcView->trackViews().size(); - while ( i != index + 1 ) - { - tcView->moveTrackView( newTrackView, i - 1 ); - i--; - } -} - - -/*! \brief Clear this track - clears all TCOs from the track */ -void TrackOperationsWidget::clearTrack() -{ - Track * t = m_trackView->getTrack(); - t->addJournalCheckPoint(); - t->lock(); - t->deleteTCOs(); - t->unlock(); -} - - - -/*! \brief Remove this track from the track list - * - */ -void TrackOperationsWidget::removeTrack() -{ - emit trackRemovalScheduled( m_trackView ); -} - -void TrackOperationsWidget::changeTrackColor() -{ - QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )-> \ - getColor( m_trackView->getTrack()->color() ); - - if( ! new_color.isValid() ) - { return; } - - emit colorChanged( new_color ); - - Engine::getSong()->setModified(); - update(); -} - -void TrackOperationsWidget::resetTrackColor() -{ - emit colorReset(); - Engine::getSong()->setModified(); - update(); -} - -void TrackOperationsWidget::randomTrackColor() -{ - QColor buffer = ColorChooser::getPalette( ColorChooser::Palette::Track )[ rand() % 48 ]; - - emit colorChanged( buffer ); - Engine::getSong()->setModified(); - update(); -} - -void TrackOperationsWidget::useTrackColor() -{ - emit colorParented(); - Engine::getSong()->setModified(); -} - - -/*! \brief Update the trackOperationsWidget context menu - * - * For all track types, we have the Clone and Remove options. - * For instrument-tracks we also offer the MIDI-control-menu - * For automation tracks, extra options: turn on/off recording - * on all TCOs (same should be added for sample tracks when - * sampletrack recording is implemented) - */ -void TrackOperationsWidget::updateMenu() -{ - QMenu * toMenu = m_trackOps->menu(); - toMenu->clear(); - toMenu->addAction( embed::getIconPixmap( "edit_copy", 16, 16 ), - tr( "Clone this track" ), - this, SLOT( cloneTrack() ) ); - toMenu->addAction( embed::getIconPixmap( "cancel", 16, 16 ), - tr( "Remove this track" ), - this, SLOT( removeTrack() ) ); - - if( ! m_trackView->trackContainerView()->fixedTCOs() ) - { - toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) ); - } - if (QMenu *fxMenu = m_trackView->createFxMenu(tr("FX %1: %2"), tr("Assign to new FX Channel"))) - { - toMenu->addMenu(fxMenu); - } - - if (InstrumentTrackView * trackView = dynamic_cast(m_trackView)) - { - toMenu->addSeparator(); - toMenu->addMenu(trackView->midiMenu()); - } - if( dynamic_cast( m_trackView ) ) - { - toMenu->addAction( tr( "Turn all recording on" ), this, SLOT( recordingOn() ) ); - toMenu->addAction( tr( "Turn all recording off" ), this, SLOT( recordingOff() ) ); - } - - toMenu->addSeparator(); - toMenu->addAction( embed::getIconPixmap( "colorize" ), - tr( "Change color" ), this, SLOT( changeTrackColor() ) ); - toMenu->addAction( embed::getIconPixmap( "colorize" ), - tr( "Reset color to default" ), this, SLOT( resetTrackColor() ) ); - toMenu->addAction( embed::getIconPixmap( "colorize" ), - tr( "Set random color" ), this, SLOT( randomTrackColor() ) ); - toMenu->addSeparator(); - toMenu->addAction( embed::getIconPixmap( "colorize" ), - tr( "Clear clip colors" ), this, SLOT( useTrackColor() ) ); -} - - -void TrackOperationsWidget::toggleRecording( bool on ) -{ - AutomationTrackView * atv = dynamic_cast( m_trackView ); - if( atv ) - { - for( TrackContentObject * tco : atv->getTrack()->getTCOs() ) - { - AutomationPattern * ap = dynamic_cast( tco ); - if( ap ) { ap->setRecording( on ); } - } - atv->update(); - } -} - - - -void TrackOperationsWidget::recordingOn() -{ - toggleRecording( true ); -} +#include "Track.h" -void TrackOperationsWidget::recordingOff() -{ - toggleRecording( false ); -} +#include +#include "AutomationPattern.h" +#include "AutomationTrack.h" +#include "BBTrack.h" +#include "BBTrackContainer.h" +#include "ConfigManager.h" +#include "Engine.h" +#include "InstrumentTrack.h" +#include "SampleTrack.h" +#include "Song.h" -// =========================================================================== -// track -// =========================================================================== /*! \brief Create a new (empty) track object * @@ -2452,10 +57,8 @@ Track::Track( TrackTypes type, TrackContainer * tc ) : m_trackContainer( tc ), /*!< The track container object */ m_type( type ), /*!< The track type */ m_name(), /*!< The track's name */ - m_mutedModel( false, this, tr( "Mute" ) ), - /*!< For controlling track muting */ - m_soloModel( false, this, tr( "Solo" ) ), - /*!< For controlling track soloing */ + m_mutedModel( false, this, tr( "Mute" ) ), /*!< For controlling track muting */ + m_soloModel( false, this, tr( "Solo" ) ), /*!< For controlling track soloing */ m_simpleSerializingMode( false ), m_trackContentObjects(), /*!< The track content objects (segments) */ m_color( 0, 0, 0 ), @@ -3069,421 +672,3 @@ BoolModel *Track::getMutedModel() return &m_mutedModel; } - - - - - -// =========================================================================== -// trackView -// =========================================================================== - -/*! \brief Create a new track View. - * - * The track View is handles the actual display of the track, including - * displaying its various widgets and the track segments. - * - * \param track The track to display. - * \param tcv The track Container View for us to be displayed in. - * \todo Is my description of these properties correct? - */ -TrackView::TrackView( Track * track, TrackContainerView * tcv ) : - QWidget( tcv->contentWidget() ), /*!< The Track Container View's content widget. */ - ModelView( NULL, this ), /*!< The model view of this track */ - m_track( track ), /*!< The track we're displaying */ - m_trackContainerView( tcv ), /*!< The track Container View we're displayed in */ - m_trackOperationsWidget( this ), /*!< Our trackOperationsWidget */ - m_trackSettingsWidget( this ), /*!< Our trackSettingsWidget */ - m_trackContentWidget( this ), /*!< Our trackContentWidget */ - m_action( NoAction ) /*!< The action we're currently performing */ -{ - setAutoFillBackground( true ); - QPalette pal; - pal.setColor( backgroundRole(), QColor( 32, 36, 40 ) ); - setPalette( pal ); - - m_trackSettingsWidget.setAutoFillBackground( true ); - - QHBoxLayout * layout = new QHBoxLayout( this ); - layout->setMargin( 0 ); - layout->setSpacing( 0 ); - layout->addWidget( &m_trackOperationsWidget ); - layout->addWidget( &m_trackSettingsWidget ); - layout->addWidget( &m_trackContentWidget, 1 ); - setFixedHeight( m_track->getHeight() ); - - resizeEvent( NULL ); - - setAcceptDrops( true ); - setAttribute( Qt::WA_DeleteOnClose, true ); - - - connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) ); - connect( m_track, - SIGNAL( trackContentObjectAdded( TrackContentObject * ) ), - this, SLOT( createTCOView( TrackContentObject * ) ), - Qt::QueuedConnection ); - - connect( &m_track->m_mutedModel, SIGNAL( dataChanged() ), - &m_trackContentWidget, SLOT( update() ) ); - - connect(&m_track->m_mutedModel, SIGNAL(dataChanged()), - this, SLOT(muteChanged())); - - connect( &m_track->m_soloModel, SIGNAL( dataChanged() ), - m_track, SLOT( toggleSolo() ), Qt::DirectConnection ); - - connect( &m_trackOperationsWidget, SIGNAL( colorChanged( QColor & ) ), - m_track, SLOT( trackColorChanged( QColor & ) ) ); - - connect( &m_trackOperationsWidget, SIGNAL( colorReset() ), - m_track, SLOT( trackColorReset() ) ); - - // create views for already existing TCOs - for( Track::tcoVector::iterator it = - m_track->m_trackContentObjects.begin(); - it != m_track->m_trackContentObjects.end(); ++it ) - { - createTCOView( *it ); - } - - m_trackContainerView->addTrackView( this ); -} - - - - -/*! \brief Destroy this track View. - * - */ -TrackView::~TrackView() -{ -} - - - - -/*! \brief Resize this track View. - * - * \param re the Resize Event to handle. - */ -void TrackView::resizeEvent( QResizeEvent * re ) -{ - if( ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ) - { - m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH_COMPACT, height() - 1 ); - m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT, height() - 1 ); - } - else - { - m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH, height() - 1 ); - m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH, height() - 1 ); - } - m_trackContentWidget.setFixedHeight( height() ); -} - - - - -/*! \brief Update this track View and all its content objects. - * - */ -void TrackView::update() -{ - m_trackContentWidget.update(); - if( !m_trackContainerView->fixedTCOs() ) - { - m_trackContentWidget.changePosition(); - } - QWidget::update(); -} - - - - -/*! \brief Create a menu for assigning/creating channels for this track. - * - */ -QMenu * TrackView::createFxMenu(QString title, QString newFxLabel) -{ - Q_UNUSED(title) - Q_UNUSED(newFxLabel) - return NULL; -} - - - - -/*! \brief Close this track View. - * - */ -bool TrackView::close() -{ - m_trackContainerView->removeTrackView( this ); - return QWidget::close(); -} - - - - -/*! \brief Register that the model of this track View has changed. - * - */ -void TrackView::modelChanged() -{ - m_track = castModel(); - assert( m_track != NULL ); - connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) ); - m_trackOperationsWidget.m_muteBtn->setModel( &m_track->m_mutedModel ); - m_trackOperationsWidget.m_soloBtn->setModel( &m_track->m_soloModel ); - ModelView::modelChanged(); - setFixedHeight( m_track->getHeight() ); -} - - - - -/*! \brief Start a drag event on this track View. - * - * \param dee the DragEnterEvent to start. - */ -void TrackView::dragEnterEvent( QDragEnterEvent * dee ) -{ - StringPairDrag::processDragEnterEvent( dee, "track_" + - QString::number( m_track->type() ) ); -} - - - - -/*! \brief Accept a drop event on this track View. - * - * We only accept drop events that are of the same type as this track. - * If so, we decode the data from the drop event by just feeding it - * back into the engine as a state. - * - * \param de the DropEvent to handle. - */ -void TrackView::dropEvent( QDropEvent * de ) -{ - QString type = StringPairDrag::decodeKey( de ); - QString value = StringPairDrag::decodeValue( de ); - if( type == ( "track_" + QString::number( m_track->type() ) ) ) - { - // value contains our XML-data so simply create a - // DataFile which does the rest for us... - DataFile dataFile( value.toUtf8() ); - Engine::mixer()->requestChangeInModel(); - m_track->restoreState( dataFile.content().firstChild().toElement() ); - Engine::mixer()->doneChangeInModel(); - de->accept(); - } -} - - - - -/*! \brief Handle a mouse press event on this track View. - * - * If this track container supports rubber band selection, let the - * widget handle that and don't bother with any other handling. - * - * If the left mouse button is pressed, we handle two things. If - * SHIFT is pressed, then we resize vertically. Otherwise we start - * the process of moving this track to a new position. - * - * Otherwise we let the widget handle the mouse event as normal. - * - * \param me the MouseEvent to handle. - */ -void TrackView::mousePressEvent( QMouseEvent * me ) -{ - - // If previously dragged too small, restore on shift-leftclick - if( height() < DEFAULT_TRACK_HEIGHT && - me->modifiers() & Qt::ShiftModifier && - me->button() == Qt::LeftButton ) - { - setFixedHeight( DEFAULT_TRACK_HEIGHT ); - m_track->setHeight( DEFAULT_TRACK_HEIGHT ); - } - - - int widgetTotal = ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt()==1 ? - DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT : - DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; - if( m_trackContainerView->allowRubberband() == true && me->x() > widgetTotal ) - { - QWidget::mousePressEvent( me ); - } - else if( me->button() == Qt::LeftButton ) - { - if( me->modifiers() & Qt::ShiftModifier ) - { - m_action = ResizeTrack; - QCursor::setPos( mapToGlobal( QPoint( me->x(), - height() ) ) ); - QCursor c( Qt::SizeVerCursor); - QApplication::setOverrideCursor( c ); - } - else - { - if( me->x()>10 ) // 10 = The width of the grip + 2 pixels to the left and right. - { - QWidget::mousePressEvent( me ); - return; - } - - m_action = MoveTrack; - - QCursor c( Qt::SizeVerCursor ); - QApplication::setOverrideCursor( c ); - // update because in move-mode, all elements in - // track-op-widgets are hidden as a visual feedback - m_trackOperationsWidget.update(); - } - - me->accept(); - } - else - { - QWidget::mousePressEvent( me ); - } -} - - - - -/*! \brief Handle a mouse move event on this track View. - * - * If this track container supports rubber band selection, let the - * widget handle that and don't bother with any other handling. - * - * Otherwise if we've started the move process (from mousePressEvent()) - * then move ourselves into that position, reordering the track list - * with moveTrackViewUp() and moveTrackViewDown() to suit. We make a - * note of this in the undo journal in case the user wants to undo this - * move. - * - * Likewise if we've started a resize process, handle this too, making - * sure that we never go below the minimum track height. - * - * \param me the MouseEvent to handle. - */ -void TrackView::mouseMoveEvent( QMouseEvent * me ) -{ - int widgetTotal = ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt()==1 ? - DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT : - DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; - if( m_trackContainerView->allowRubberband() == true && me->x() > widgetTotal ) - { - QWidget::mouseMoveEvent( me ); - } - else if( m_action == MoveTrack ) - { - // look which track-widget the mouse-cursor is over - const int yPos = - m_trackContainerView->contentWidget()->mapFromGlobal( me->globalPos() ).y(); - const TrackView * trackAtY = m_trackContainerView->trackViewAt( yPos ); - - // debug code - // qDebug( "y position %d", yPos ); - - // a track-widget not equal to ourself? - if( trackAtY != NULL && trackAtY != this ) - { - // then move us up/down there! - if( me->y() < 0 ) - { - m_trackContainerView->moveTrackViewUp( this ); - } - else - { - m_trackContainerView->moveTrackViewDown( this ); - } - } - } - else if( m_action == ResizeTrack ) - { - setFixedHeight( qMax( me->y(), MINIMAL_TRACK_HEIGHT ) ); - m_trackContainerView->realignTracks(); - m_track->setHeight( height() ); - } - - if( height() < DEFAULT_TRACK_HEIGHT ) - { - ToolTip::add( this, m_track->m_name ); - } -} - - - -/*! \brief Handle a mouse release event on this track View. - * - * \param me the MouseEvent to handle. - */ -void TrackView::mouseReleaseEvent( QMouseEvent * me ) -{ - m_action = NoAction; - while( QApplication::overrideCursor() != NULL ) - { - QApplication::restoreOverrideCursor(); - } - m_trackOperationsWidget.update(); - - QWidget::mouseReleaseEvent( me ); -} - - - - -/*! \brief Repaint this track View. - * - * \param pe the PaintEvent to start. - */ -void TrackView::paintEvent( QPaintEvent * pe ) -{ - QStyleOption opt; - opt.initFrom( this ); - QPainter p( this ); - style()->drawPrimitive( QStyle::PE_Widget, &opt, &p, this ); -} - - - - -/*! \brief Create a TrackContentObject View in this track View. - * - * \param tco the TrackContentObject to create the view for. - * \todo is this a good description for what this method does? - */ -void TrackView::createTCOView( TrackContentObject * tco ) -{ - TrackContentObjectView * tv = tco->createView( this ); - if( tco->getSelectViewOnCreate() == true ) - { - tv->setSelected( true ); - } - tco->selectViewOnCreate( false ); -} - - - - -void TrackView::muteChanged() -{ - FadeButton * indicator = getActivityIndicator(); - if (indicator) { setIndicatorMute(indicator, m_track->m_mutedModel.value()); } -} - - - - -void TrackView::setIndicatorMute(FadeButton* indicator, bool muted) -{ - QPalette::ColorRole role = muted ? QPalette::Highlight : QPalette::BrightText; - indicator->setActiveColor(QApplication::palette().color(QPalette::Active, role)); -} diff --git a/src/core/TrackContentObject.cpp b/src/core/TrackContentObject.cpp new file mode 100644 index 00000000000..ad41fd11099 --- /dev/null +++ b/src/core/TrackContentObject.cpp @@ -0,0 +1,206 @@ +/* + * TrackContentObject.cpp - implementation of TrackContentObject class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "TrackContentObject.h" + +#include + +#include "AutomationEditor.h" +#include "AutomationPattern.h" +#include "Engine.h" +#include "GuiApplication.h" +#include "Song.h" + + +/*! \brief Create a new TrackContentObject + * + * Creates a new track content object for the given track. + * + * \param _track The track that will contain the new object + */ +TrackContentObject::TrackContentObject( Track * track ) : + Model( track ), + m_track( track ), + m_startPosition(), + m_length(), + m_mutedModel( false, this, tr( "Mute" ) ), + m_selectViewOnCreate( false ), + m_color( 128, 128, 128 ), + m_useCustomClipColor( false ) +{ + if( getTrack() ) + { + getTrack()->addTCO( this ); + } + setJournalling( false ); + movePosition( 0 ); + changeLength( 0 ); + setJournalling( true ); +} + + + + +/*! \brief Destroy a TrackContentObject + * + * Destroys the given track content object. + * + */ +TrackContentObject::~TrackContentObject() +{ + emit destroyedTCO(); + + if( getTrack() ) + { + getTrack()->removeTCO( this ); + } +} + + + + +/*! \brief Move this TrackContentObject's position in time + * + * If the track content object has moved, update its position. We + * also add a journal entry for undo and update the display. + * + * \param _pos The new position of the track content object. + */ +void TrackContentObject::movePosition( const TimePos & pos ) +{ + TimePos newPos = qMax(0, pos.getTicks()); + if (m_startPosition != newPos) + { + Engine::mixer()->requestChangeInModel(); + m_startPosition = newPos; + Engine::mixer()->doneChangeInModel(); + Engine::getSong()->updateLength(); + emit positionChanged(); + } +} + + + + +/*! \brief Change the length of this TrackContentObject + * + * If the track content object's length has changed, update it. We + * also add a journal entry for undo and update the display. + * + * \param _length The new length of the track content object. + */ +void TrackContentObject::changeLength( const TimePos & length ) +{ + m_length = length; + Engine::getSong()->updateLength(); + emit lengthChanged(); +} + + + + +bool TrackContentObject::comparePosition(const TrackContentObject *a, const TrackContentObject *b) +{ + return a->startPosition() < b->startPosition(); +} + + + + +/*! \brief Copies the state of a TrackContentObject to another TrackContentObject + * + * This method copies the state of a TCO to another TCO + */ +void TrackContentObject::copyStateTo( TrackContentObject *src, TrackContentObject *dst ) +{ + // If the node names match we copy the state + if( src->nodeName() == dst->nodeName() ){ + QDomDocument doc; + QDomElement parent = doc.createElement( "StateCopy" ); + src->saveState( doc, parent ); + + const TimePos pos = dst->startPosition(); + dst->restoreState( parent.firstChild().toElement() ); + dst->movePosition( pos ); + + AutomationPattern::resolveAllIDs(); + GuiApplication::instance()->automationEditor()->m_editor->updateAfterPatternChange(); + } +} + + + + +/*! \brief Mutes this TrackContentObject + * + * Restore the previous state of this track content object. This will + * restore the position or the length of the track content object + * depending on what was changed. + * + * \param _je The journal entry to undo + */ +void TrackContentObject::toggleMute() +{ + m_mutedModel.setValue( !m_mutedModel.value() ); + emit dataChanged(); +} + + + + +TimePos TrackContentObject::startTimeOffset() const +{ + return m_startTimeOffset; +} + + + + +void TrackContentObject::setStartTimeOffset( const TimePos &startTimeOffset ) +{ + m_startTimeOffset = startTimeOffset; +} + +// Update TCO color if it follows the track color +void TrackContentObject::updateColor() +{ + if( ! m_useCustomClipColor ) + { + emit trackColorChanged(); + } +} + + +void TrackContentObject::useCustomClipColor( bool b ) +{ + m_useCustomClipColor = b; + updateColor(); +} + + +bool TrackContentObject::hasColor() +{ + return usesCustomClipColor() || getTrack()->useColor(); +} + diff --git a/src/core/main.cpp b/src/core/main.cpp index 36fe587574b..08599362ec8 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -59,6 +59,7 @@ #include "MainApplication.h" #include "ConfigManager.h" +#include "DataFile.h" #include "NotePlayHandle.h" #include "embed.h" #include "Engine.h" diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 193c0b9012c..4b7018a5579 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -35,6 +35,8 @@ SET(LMMS_SRCS gui/TimeLineWidget.cpp gui/ToolPluginView.cpp gui/TrackContainerView.cpp + gui/TrackContentObjectView.cpp + gui/TrackView.cpp gui/dialogs/FileDialog.cpp gui/dialogs/VersionedSaveDialog.cpp @@ -98,6 +100,8 @@ SET(LMMS_SRCS gui/widgets/ToolButton.cpp gui/widgets/ToolTip.cpp gui/widgets/TrackLabelButton.cpp + gui/widgets/TrackContentWidget.cpp + gui/widgets/TrackOperationsWidget.cpp gui/widgets/TrackRenameLineEdit.cpp gui/widgets/StepRecorderWidget.cpp diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 797e19c00e6..9ba634ea56a 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -39,6 +39,7 @@ #include "FileBrowser.h" #include "BBTrackContainer.h" #include "ConfigManager.h" +#include "DataFile.h" #include "embed.h" #include "Engine.h" #include "GuiApplication.h" diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index 5170ffb8c44..3effe20afde 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -34,6 +34,7 @@ #include "ConfigManager.h" #include "ControllerRackView.h" #include "FxMixerView.h" +#include "InstrumentTrack.h" #include "MainWindow.h" #include "PianoRoll.h" #include "ProjectNotes.h" diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index b1de8e1a47a..c5b179b9fc6 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include "FxMixerView.h" #include "GuiApplication.h" #include "ImportFilter.h" +#include "InstrumentTrack.h" #include "PianoRoll.h" #include "PluginBrowser.h" #include "PluginFactory.h" @@ -71,6 +73,7 @@ #include "lmmsversion.h" + #if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BUILD_APPLE) && !defined(LMMS_BUILD_HAIKU) //Work around an issue on KDE5 as per https://bugs.kde.org/show_bug.cgi?id=337491#c21 void disableAutoKeyAccelerators(QWidget* mainWindow) diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index fe73de1b1d1..2ee33586812 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -29,15 +29,18 @@ #include #include #include +#include #include #include "TrackContainer.h" #include "BBTrack.h" +#include "DataFile.h" #include "MainWindow.h" #include "Mixer.h" #include "FileBrowser.h" #include "ImportFilter.h" #include "Instrument.h" +#include "InstrumentTrack.h" #include "Song.h" #include "StringPairDrag.h" #include "GuiApplication.h" diff --git a/src/gui/TrackContentObjectView.cpp b/src/gui/TrackContentObjectView.cpp new file mode 100644 index 00000000000..0cd34bdd117 --- /dev/null +++ b/src/gui/TrackContentObjectView.cpp @@ -0,0 +1,1243 @@ +/* + * TrackContentObjectView.cpp - implementation of TrackContentObjectView class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "TrackContentObjectView.h" + +#include +#include +#include + +#include "AutomationPattern.h" +#include "Clipboard.h" +#include "ColorChooser.h" +#include "ComboBoxModel.h" +#include "DataFile.h" +#include "embed.h" +#include "GuiApplication.h" +#include "SampleTrack.h" +#include "SongEditor.h" +#include "StringPairDrag.h" +#include "TextFloat.h" +#include "TrackContainer.h" +#include "TrackContainerView.h" +#include "TrackView.h" + + +/*! The width of the resize grip in pixels + */ +const int RESIZE_GRIP_WIDTH = 4; + + +/*! A pointer for that text bubble used when moving segments, etc. + * + * In a number of situations, LMMS displays a floating text bubble + * beside the cursor as you move or resize elements of a track about. + * This pointer keeps track of it, as you only ever need one at a time. + */ +TextFloat * TrackContentObjectView::s_textFloat = NULL; + + +/*! \brief Create a new trackContentObjectView + * + * Creates a new track content object view for the given + * track content object in the given track view. + * + * \param _tco The track content object to be displayed + * \param _tv The track view that will contain the new object + */ +TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, + TrackView * tv ) : + selectableObject( tv->getTrackContentWidget() ), + ModelView( NULL, this ), + m_tco( tco ), + m_trackView( tv ), + m_action( NoAction ), + m_initialMousePos( QPoint( 0, 0 ) ), + m_initialMouseGlobalPos( QPoint( 0, 0 ) ), + m_initialTCOPos( TimePos(0) ), + m_initialTCOEnd( TimePos(0) ), + m_initialOffsets( QVector() ), + m_hint( NULL ), + m_mutedColor( 0, 0, 0 ), + m_mutedBackgroundColor( 0, 0, 0 ), + m_selectedColor( 0, 0, 0 ), + m_textColor( 0, 0, 0 ), + m_textShadowColor( 0, 0, 0 ), + m_BBPatternBackground( 0, 0, 0 ), + m_gradient( true ), + m_mouseHotspotHand( 0, 0 ), + m_cursorSetYet( false ), + m_needsUpdate( true ) +{ + if( s_textFloat == NULL ) + { + s_textFloat = new TextFloat; + s_textFloat->setPixmap( embed::getIconPixmap( "clock" ) ); + } + + setAttribute( Qt::WA_OpaquePaintEvent, true ); + setAttribute( Qt::WA_DeleteOnClose, true ); + setFocusPolicy( Qt::StrongFocus ); + setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + move( 0, 0 ); + show(); + + setFixedHeight( tv->getTrackContentWidget()->height() - 1); + setAcceptDrops( true ); + setMouseTracking( true ); + + connect( m_tco, SIGNAL( lengthChanged() ), + this, SLOT( updateLength() ) ); + connect( gui->songEditor()->m_editor->zoomingModel(), SIGNAL( dataChanged() ), this, SLOT( updateLength() ) ); + connect( m_tco, SIGNAL( positionChanged() ), + this, SLOT( updatePosition() ) ); + connect( m_tco, SIGNAL( destroyedTCO() ), this, SLOT( close() ) ); + setModel( m_tco ); + connect( m_tco, SIGNAL( trackColorChanged() ), this, SLOT( update() ) ); + connect( m_trackView->getTrackOperationsWidget(), SIGNAL( colorParented() ), this, SLOT( useTrackColor() ) ); + + m_trackView->getTrackContentWidget()->addTCOView( this ); + updateLength(); + updatePosition(); +} + + + + +/*! \brief Destroy a trackContentObjectView + * + * Destroys the given track content object view. + * + */ +TrackContentObjectView::~TrackContentObjectView() +{ + delete m_hint; + // we have to give our track-container the focus because otherwise the + // op-buttons of our track-widgets could become focus and when the user + // presses space for playing song, just one of these buttons is pressed + // which results in unwanted effects + m_trackView->trackContainerView()->setFocus(); +} + + +/*! \brief Update a TrackContentObjectView + * + * TCO's get drawn only when needed, + * and when a TCO is updated, + * it needs to be redrawn. + * + */ +void TrackContentObjectView::update() +{ + if( !m_cursorSetYet ) + { + setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + m_cursorSetYet = true; + } + + if( fixedTCOs() ) + { + updateLength(); + } + m_needsUpdate = true; + selectableObject::update(); +} + + + +/*! \brief Does this trackContentObjectView have a fixed TCO? + * + * Returns whether the containing trackView has fixed + * TCOs. + * + * \todo What the hell is a TCO here - track content object? And in + * what circumstance are they fixed? + */ +bool TrackContentObjectView::fixedTCOs() +{ + return m_trackView->trackContainerView()->fixedTCOs(); +} + + + +// qproperty access functions, to be inherited & used by TCOviews +//! \brief CSS theming qproperty access method +QColor TrackContentObjectView::mutedColor() const +{ return m_mutedColor; } + +QColor TrackContentObjectView::mutedBackgroundColor() const +{ return m_mutedBackgroundColor; } + +QColor TrackContentObjectView::selectedColor() const +{ return m_selectedColor; } + +QColor TrackContentObjectView::textColor() const +{ return m_textColor; } + +QColor TrackContentObjectView::textBackgroundColor() const +{ + return m_textBackgroundColor; +} + +QColor TrackContentObjectView::textShadowColor() const +{ return m_textShadowColor; } + +QColor TrackContentObjectView::BBPatternBackground() const +{ return m_BBPatternBackground; } + +bool TrackContentObjectView::gradient() const +{ return m_gradient; } + +//! \brief CSS theming qproperty access method +void TrackContentObjectView::setMutedColor( const QColor & c ) +{ m_mutedColor = QColor( c ); } + +void TrackContentObjectView::setMutedBackgroundColor( const QColor & c ) +{ m_mutedBackgroundColor = QColor( c ); } + +void TrackContentObjectView::setSelectedColor( const QColor & c ) +{ m_selectedColor = QColor( c ); } + +void TrackContentObjectView::setTextColor( const QColor & c ) +{ m_textColor = QColor( c ); } + +void TrackContentObjectView::setTextBackgroundColor( const QColor & c ) +{ + m_textBackgroundColor = c; +} + +void TrackContentObjectView::setTextShadowColor( const QColor & c ) +{ m_textShadowColor = QColor( c ); } + +void TrackContentObjectView::setBBPatternBackground( const QColor & c ) +{ m_BBPatternBackground = QColor( c ); } + +void TrackContentObjectView::setGradient( const bool & b ) +{ m_gradient = b; } + +void TrackContentObjectView::setMouseHotspotHand(const QSize & s) +{ + m_mouseHotspotHand = s; +} + +// access needsUpdate member variable +bool TrackContentObjectView::needsUpdate() +{ return m_needsUpdate; } +void TrackContentObjectView::setNeedsUpdate( bool b ) +{ m_needsUpdate = b; } + +/*! \brief Close a trackContentObjectView + * + * Closes a track content object view by asking the track + * view to remove us and then asking the QWidget to close us. + * + * \return Boolean state of whether the QWidget was able to close. + */ +bool TrackContentObjectView::close() +{ + m_trackView->getTrackContentWidget()->removeTCOView( this ); + return QWidget::close(); +} + + + + +/*! \brief Removes a trackContentObjectView from its track view. + * + * Like the close() method, this asks the track view to remove this + * track content object view. However, the track content object is + * scheduled for later deletion rather than closed immediately. + * + */ +void TrackContentObjectView::remove() +{ + m_trackView->getTrack()->addJournalCheckPoint(); + + // delete ourself + close(); + m_tco->deleteLater(); +} + + + + +/*! \brief Updates a trackContentObjectView's length + * + * If this track content object view has a fixed TCO, then we must + * keep the width of our parent. Otherwise, calculate our width from + * the track content object's length in pixels adding in the border. + * + */ +void TrackContentObjectView::updateLength() +{ + if( fixedTCOs() ) + { + setFixedWidth( parentWidget()->width() ); + } + else + { + setFixedWidth( + static_cast( m_tco->length() * pixelsPerBar() / + TimePos::ticksPerBar() ) + 1 /*+ + TCO_BORDER_WIDTH * 2-1*/ ); + } + m_trackView->trackContainerView()->update(); +} + + + + +/*! \brief Updates a trackContentObjectView's position. + * + * Ask our track view to change our position. Then make sure that the + * track view is updated in case this position has changed the track + * view's length. + * + */ +void TrackContentObjectView::updatePosition() +{ + m_trackView->getTrackContentWidget()->changePosition(); + // moving a TCO can result in change of song-length etc., + // therefore we update the track-container + m_trackView->trackContainerView()->update(); +} + + + + +void TrackContentObjectView::changeClipColor() +{ + // Get a color from the user + QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )->getColor( m_tco->color() ); + if( ! new_color.isValid() ) + { return; } + + // Use that color + m_tco->setColor( new_color ); + m_tco->useCustomClipColor( true ); + update(); +} + + + +void TrackContentObjectView::useTrackColor() +{ + m_tco->useCustomClipColor( false ); + update(); +} + + + + + +/*! \brief Change the trackContentObjectView's display when something + * being dragged enters it. + * + * We need to notify Qt to change our display if something being + * dragged has entered our 'airspace'. + * + * \param dee The QDragEnterEvent to watch. + */ +void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee ) +{ + TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); + TimePos tcoPos = TimePos( m_tco->startPosition() ); + + if( tcw->canPasteSelection( tcoPos, dee ) == false ) + { + dee->ignore(); + } + else + { + StringPairDrag::processDragEnterEvent( dee, "tco_" + + QString::number( m_tco->getTrack()->type() ) ); + } +} + + + + +/*! \brief Handle something being dropped on this trackContentObjectView. + * + * When something has been dropped on this trackContentObjectView, and + * it's a track content object, then use an instance of our dataFile reader + * to take the xml of the track content object and turn it into something + * we can write over our current state. + * + * \param de The QDropEvent to handle. + */ +void TrackContentObjectView::dropEvent( QDropEvent * de ) +{ + QString type = StringPairDrag::decodeKey( de ); + QString value = StringPairDrag::decodeValue( de ); + + // Track must be the same type to paste into + if( type != ( "tco_" + QString::number( m_tco->getTrack()->type() ) ) ) + { + return; + } + + // Defer to rubberband paste if we're in that mode + if( m_trackView->trackContainerView()->allowRubberband() == true ) + { + TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); + TimePos tcoPos = TimePos( m_tco->startPosition() ); + + if( tcw->pasteSelection( tcoPos, de ) == true ) + { + de->accept(); + } + return; + } + + // Don't allow pasting a tco into itself. + QObject* qwSource = de->source(); + if( qwSource != NULL && + dynamic_cast( qwSource ) == this ) + { + return; + } + + // Copy state into existing tco + DataFile dataFile( value.toUtf8() ); + TimePos pos = m_tco->startPosition(); + QDomElement tcos = dataFile.content().firstChildElement( "tcos" ); + m_tco->restoreState( tcos.firstChildElement().firstChildElement() ); + m_tco->movePosition( pos ); + AutomationPattern::resolveAllIDs(); + de->accept(); +} + + + + +/*! \brief Handle a dragged selection leaving our 'airspace'. + * + * \param e The QEvent to watch. + */ +void TrackContentObjectView::leaveEvent( QEvent * e ) +{ + if( cursor().shape() != Qt::BitmapCursor ) + { + setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + } + if( e != NULL ) + { + QWidget::leaveEvent( e ); + } +} + +/*! \brief Create a DataFile suitable for copying multiple trackContentObjects. + * + * trackContentObjects in the vector are written to the "tcos" node in the + * DataFile. The trackContentObjectView's initial mouse position is written + * to the "initialMouseX" node in the DataFile. When dropped on a track, + * this is used to create copies of the TCOs. + * + * \param tcos The trackContectObjects to save in a DataFile + */ +DataFile TrackContentObjectView::createTCODataFiles( + const QVector & tcoViews) const +{ + Track * t = m_trackView->getTrack(); + TrackContainer * tc = t->trackContainer(); + DataFile dataFile( DataFile::DragNDropData ); + QDomElement tcoParent = dataFile.createElement( "tcos" ); + + typedef QVector tcoViewVector; + for( tcoViewVector::const_iterator it = tcoViews.begin(); + it != tcoViews.end(); ++it ) + { + // Insert into the dom under the "tcos" element + Track* tcoTrack = ( *it )->m_trackView->getTrack(); + int trackIndex = tc->tracks().indexOf( tcoTrack ); + QDomElement tcoElement = dataFile.createElement( "tco" ); + tcoElement.setAttribute( "trackIndex", trackIndex ); + tcoElement.setAttribute( "trackType", tcoTrack->type() ); + tcoElement.setAttribute( "trackName", tcoTrack->name() ); + ( *it )->m_tco->saveState( dataFile, tcoElement ); + tcoParent.appendChild( tcoElement ); + } + + dataFile.content().appendChild( tcoParent ); + + // Add extra metadata needed for calculations later + int initialTrackIndex = tc->tracks().indexOf( t ); + if( initialTrackIndex < 0 ) + { + printf("Failed to find selected track in the TrackContainer.\n"); + return dataFile; + } + QDomElement metadata = dataFile.createElement( "copyMetadata" ); + // initialTrackIndex is the index of the track that was touched + metadata.setAttribute( "initialTrackIndex", initialTrackIndex ); + metadata.setAttribute( "trackContainerId", tc->id() ); + // grabbedTCOPos is the pos of the bar containing the TCO we grabbed + metadata.setAttribute( "grabbedTCOPos", m_tco->startPosition() ); + + dataFile.content().appendChild( metadata ); + + return dataFile; +} + +void TrackContentObjectView::paintTextLabel(QString const & text, QPainter & painter) +{ + if (text.trimmed() == "") + { + return; + } + + painter.setRenderHint( QPainter::TextAntialiasing ); + + QFont labelFont = this->font(); + labelFont.setHintingPreference( QFont::PreferFullHinting ); + painter.setFont( labelFont ); + + const int textTop = TCO_BORDER_WIDTH + 1; + const int textLeft = TCO_BORDER_WIDTH + 3; + + QFontMetrics fontMetrics(labelFont); + QString elidedPatternName = fontMetrics.elidedText(text, Qt::ElideMiddle, width() - 2 * textLeft); + + if (elidedPatternName.length() < 2) + { + elidedPatternName = text.trimmed(); + } + + painter.fillRect(QRect(0, 0, width(), fontMetrics.height() + 2 * textTop), textBackgroundColor()); + + int const finalTextTop = textTop + fontMetrics.ascent(); + painter.setPen(textShadowColor()); + painter.drawText( textLeft + 1, finalTextTop + 1, elidedPatternName ); + painter.setPen( textColor() ); + painter.drawText( textLeft, finalTextTop, elidedPatternName ); +} + +/*! \brief Handle a mouse press on this trackContentObjectView. + * + * Handles the various ways in which a trackContentObjectView can be + * used with a click of a mouse button. + * + * * If our container supports rubber band selection then handle + * selection events. + * * or if shift-left button, add this object to the selection + * * or if ctrl-left button, start a drag-copy event + * * or if just plain left button, resize if we're resizeable + * * or if ctrl-middle button, mute the track content object + * * or if middle button, maybe delete the track content object. + * + * \param me The QMouseEvent to handle. + */ +void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) +{ + // Right now, active is only used on right/mid clicks actions, so we use a ternary operator + // to avoid the overhead of calling getClickedTCOs when it's not used + auto active = me->button() == Qt::LeftButton + ? QVector() + : getClickedTCOs(); + + setInitialPos( me->pos() ); + setInitialOffsets(); + if( !fixedTCOs() && me->button() == Qt::LeftButton ) + { + if( me->modifiers() & Qt::ControlModifier ) + { + if( isSelected() ) + { + m_action = CopySelection; + } + else + { + m_action = ToggleSelected; + } + } + else if( !me->modifiers() + || (me->modifiers() & Qt::AltModifier) + || (me->modifiers() & Qt::ShiftModifier) ) + { + if( isSelected() ) + { + m_action = MoveSelection; + } + else + { + gui->songEditor()->m_editor->selectAllTcos( false ); + m_tco->addJournalCheckPoint(); + + // move or resize + m_tco->setJournalling( false ); + + setInitialPos( me->pos() ); + setInitialOffsets(); + + SampleTCO * sTco = dynamic_cast( m_tco ); + if( me->x() < RESIZE_GRIP_WIDTH && sTco + && !m_tco->getAutoResize() ) + { + m_action = ResizeLeft; + setCursor( Qt::SizeHorCursor ); + } + else if( m_tco->getAutoResize() || me->x() < width() - RESIZE_GRIP_WIDTH ) + { + m_action = Move; + setCursor( Qt::SizeAllCursor ); + } + else + { + m_action = Resize; + setCursor( Qt::SizeHorCursor ); + } + + if( m_action == Move ) + { + s_textFloat->setTitle( tr( "Current position" ) ); + s_textFloat->setText( QString( "%1:%2" ). + arg( m_tco->startPosition().getBar() + 1 ). + arg( m_tco->startPosition().getTicks() % + TimePos::ticksPerBar() ) ); + } + else if( m_action == Resize || m_action == ResizeLeft ) + { + s_textFloat->setTitle( tr( "Current length" ) ); + s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). + arg( m_tco->length().getBar() ). + arg( m_tco->length().getTicks() % + TimePos::ticksPerBar() ). + arg( m_tco->startPosition().getBar() + 1 ). + arg( m_tco->startPosition().getTicks() % + TimePos::ticksPerBar() ). + arg( m_tco->endPosition().getBar() + 1 ). + arg( m_tco->endPosition().getTicks() % + TimePos::ticksPerBar() ) ); + } + // s_textFloat->reparent( this ); + // setup text-float as if TCO was already moved/resized + s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); + s_textFloat->show(); + } + + delete m_hint; + QString hint = m_action == Move || m_action == MoveSelection + ? tr( "Press <%1> and drag to make a copy." ) + : tr( "Press <%1> for free resizing." ); + m_hint = TextFloat::displayMessage( tr( "Hint" ), hint.arg(UI_CTRL_KEY), + embed::getIconPixmap( "hint" ), 0 ); + } + } + else if( me->button() == Qt::RightButton ) + { + if( me->modifiers() & Qt::ControlModifier ) + { + toggleMute( active ); + } + else if( me->modifiers() & Qt::ShiftModifier && !fixedTCOs() ) + { + remove( active ); + } + } + else if( me->button() == Qt::MidButton ) + { + if( me->modifiers() & Qt::ControlModifier ) + { + toggleMute( active ); + } + else if( !fixedTCOs() ) + { + remove( active ); + } + } +} + + + + +/*! \brief Handle a mouse movement (drag) on this trackContentObjectView. + * + * Handles the various ways in which a trackContentObjectView can be + * used with a mouse drag. + * + * * If in move mode, move ourselves in the track, + * * or if in move-selection mode, move the entire selection, + * * or if in resize mode, resize ourselves, + * * otherwise ??? + * + * \param me The QMouseEvent to handle. + * \todo what does the final else case do here? + */ +void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) +{ + if( m_action == CopySelection || m_action == ToggleSelected ) + { + if( mouseMovedDistance( me, 2 ) == true ) + { + QVector tcoViews; + if( m_action == CopySelection ) + { + // Collect all selected TCOs + QVector so = + m_trackView->trackContainerView()->selectedObjects(); + for( auto it = so.begin(); it != so.end(); ++it ) + { + TrackContentObjectView * tcov = + dynamic_cast( *it ); + if( tcov != NULL ) + { + tcoViews.push_back( tcov ); + } + } + } + else + { + gui->songEditor()->m_editor->selectAllTcos( false ); + tcoViews.push_back( this ); + } + // Clear the action here because mouseReleaseEvent will not get + // triggered once we go into drag. + m_action = NoAction; + + // Write the TCOs to the DataFile for copying + DataFile dataFile = createTCODataFiles( tcoViews ); + + // TODO -- thumbnail for all selected + QPixmap thumbnail = grab().scaled( + 128, 128, + Qt::KeepAspectRatio, + Qt::SmoothTransformation ); + new StringPairDrag( QString( "tco_%1" ).arg( + m_tco->getTrack()->type() ), + dataFile.toString(), thumbnail, this ); + } + } + + if( me->modifiers() & Qt::ControlModifier ) + { + delete m_hint; + m_hint = NULL; + } + + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); + if( m_action == Move ) + { + TimePos newPos = draggedTCOPos( me ); + + m_tco->movePosition(newPos); + newPos = m_tco->startPosition(); // Get the real position the TCO was dragged to for the label + m_trackView->getTrackContentWidget()->changePosition(); + s_textFloat->setText( QString( "%1:%2" ). + arg( newPos.getBar() + 1 ). + arg( newPos.getTicks() % + TimePos::ticksPerBar() ) ); + s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) ); + } + else if( m_action == MoveSelection ) + { + // 1: Find the position we want to move the grabbed TCO to + TimePos newPos = draggedTCOPos( me ); + + // 2: Handle moving the other selected TCOs the same distance + QVector so = + m_trackView->trackContainerView()->selectedObjects(); + QVector tcos; // List of selected clips + int leftmost = 0; // Leftmost clip's offset from grabbed clip + // Populate tcos, find leftmost + for( QVector::iterator it = so.begin(); + it != so.end(); ++it ) + { + TrackContentObjectView * tcov = + dynamic_cast( *it ); + if( tcov == NULL ) { continue; } + tcos.push_back( tcov->m_tco ); + int index = std::distance( so.begin(), it ); + leftmost = std::min(leftmost, m_initialOffsets[index].getTicks()); + } + // Make sure the leftmost clip doesn't get moved to a negative position + if ( newPos.getTicks() + leftmost < 0 ) { newPos = -leftmost; } + + for( QVector::iterator it = tcos.begin(); + it != tcos.end(); ++it ) + { + int index = std::distance( tcos.begin(), it ); + ( *it )->movePosition( newPos + m_initialOffsets[index] ); + } + } + else if( m_action == Resize || m_action == ResizeLeft ) + { + // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize + const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier); + const float snapSize = gui->songEditor()->m_editor->getSnapSize(); + // Length in ticks of one snap increment + const TimePos snapLength = TimePos( (int)(snapSize * TimePos::ticksPerBar()) ); + + if( m_action == Resize ) + { + // The clip's new length + TimePos l = static_cast( me->x() * TimePos::ticksPerBar() / ppb ); + + if ( unquantized ) + { // We want to preserve this adjusted offset, + // even if the user switches to snapping later + setInitialPos( m_initialMousePos ); + // Don't resize to less than 1 tick + m_tco->changeLength( qMax( 1, l ) ); + } + else if ( me->modifiers() & Qt::ShiftModifier ) + { // If shift is held, quantize clip's end position + TimePos end = TimePos( m_initialTCOPos + l ).quantize( snapSize ); + // The end position has to be after the clip's start + TimePos min = m_initialTCOPos.quantize( snapSize ); + if ( min <= m_initialTCOPos ) min += snapLength; + m_tco->changeLength( qMax(min - m_initialTCOPos, end - m_initialTCOPos) ); + } + else + { // Otherwise, resize in fixed increments + TimePos initialLength = m_initialTCOEnd - m_initialTCOPos; + TimePos offset = TimePos( l - initialLength ).quantize( snapSize ); + // Don't resize to less than 1 tick + TimePos min = TimePos( initialLength % snapLength ); + if (min < 1) min += snapLength; + m_tco->changeLength( qMax( min, initialLength + offset) ); + } + } + else + { + SampleTCO * sTco = dynamic_cast( m_tco ); + if( sTco ) + { + const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); + + TimePos t = qMax( 0, (int) + m_trackView->trackContainerView()->currentPosition() + + static_cast( x * TimePos::ticksPerBar() / ppb ) ); + + if( unquantized ) + { // We want to preserve this adjusted offset, + // even if the user switches to snapping later + setInitialPos( m_initialMousePos ); + //Don't resize to less than 1 tick + t = qMin( m_initialTCOEnd - 1, t); + } + else if( me->modifiers() & Qt::ShiftModifier ) + { // If shift is held, quantize clip's start position + // Don't let the start position move past the end position + TimePos max = m_initialTCOEnd.quantize( snapSize ); + if ( max >= m_initialTCOEnd ) max -= snapLength; + t = qMin( max, t.quantize( snapSize ) ); + } + else + { // Otherwise, resize in fixed increments + // Don't resize to less than 1 tick + TimePos initialLength = m_initialTCOEnd - m_initialTCOPos; + TimePos minLength = TimePos( initialLength % snapLength ); + if (minLength < 1) minLength += snapLength; + TimePos offset = TimePos(t - m_initialTCOPos).quantize( snapSize ); + t = qMin( m_initialTCOEnd - minLength, m_initialTCOPos + offset ); + } + + TimePos oldPos = m_tco->startPosition(); + if( m_tco->length() + ( oldPos - t ) >= 1 ) + { + m_tco->movePosition( t ); + m_tco->changeLength( m_tco->length() + ( oldPos - t ) ); + sTco->setStartTimeOffset( sTco->startTimeOffset() + ( oldPos - t ) ); + } + } + } + s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). + arg( m_tco->length().getBar() ). + arg( m_tco->length().getTicks() % + TimePos::ticksPerBar() ). + arg( m_tco->startPosition().getBar() + 1 ). + arg( m_tco->startPosition().getTicks() % + TimePos::ticksPerBar() ). + arg( m_tco->endPosition().getBar() + 1 ). + arg( m_tco->endPosition().getTicks() % + TimePos::ticksPerBar() ) ); + s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); + } + else + { + SampleTCO * sTco = dynamic_cast( m_tco ); + if( ( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() ) + || ( me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco && !m_tco->getAutoResize() ) ) + { + setCursor( Qt::SizeHorCursor ); + } + else + { + leaveEvent( NULL ); + } + } +} + + + + +/*! \brief Handle a mouse release on this trackContentObjectView. + * + * If we're in move or resize mode, journal the change as appropriate. + * Then tidy up. + * + * \param me The QMouseEvent to handle. + */ +void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me ) +{ + // If the CopySelection was chosen as the action due to mouse movement, + // it will have been cleared. At this point Toggle is the desired action. + // An active StringPairDrag will prevent this method from being called, + // so a real CopySelection would not have occurred. + if( m_action == CopySelection || + ( m_action == ToggleSelected && mouseMovedDistance( me, 2 ) == false ) ) + { + setSelected( !isSelected() ); + } + + if( m_action == Move || m_action == Resize || m_action == ResizeLeft ) + { + // TODO: Fix m_tco->setJournalling() consistency + m_tco->setJournalling( true ); + } + m_action = NoAction; + delete m_hint; + m_hint = NULL; + s_textFloat->hide(); + leaveEvent( NULL ); + selectableObject::mouseReleaseEvent( me ); +} + + + + +/*! \brief Set up the context menu for this trackContentObjectView. + * + * Set up the various context menu events that can apply to a + * track content object view. + * + * \param cme The QContextMenuEvent to add the actions to. + */ +void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) +{ + // Depending on whether we right-clicked a selection or an individual TCO we will have + // different labels for the actions. + bool individualTCO = getClickedTCOs().size() <= 1; + + if( cme->modifiers() ) + { + return; + } + + QMenu contextMenu( this ); + + if( fixedTCOs() == false ) + { + contextMenu.addAction( + embed::getIconPixmap( "cancel" ), + individualTCO + ? tr("Delete (middle mousebutton)") + : tr("Delete selection (middle mousebutton)"), + [this](){ contextMenuAction( Remove ); } ); + + contextMenu.addSeparator(); + + contextMenu.addAction( + embed::getIconPixmap( "edit_cut" ), + individualTCO + ? tr("Cut") + : tr("Cut selection"), + [this](){ contextMenuAction( Cut ); } ); + } + + contextMenu.addAction( + embed::getIconPixmap( "edit_copy" ), + individualTCO + ? tr("Copy") + : tr("Copy selection"), + [this](){ contextMenuAction( Copy ); } ); + + contextMenu.addAction( + embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), + [this](){ contextMenuAction( Paste ); } ); + + contextMenu.addSeparator(); + + contextMenu.addAction( + embed::getIconPixmap( "muted" ), + (individualTCO + ? tr("Mute/unmute (<%1> + middle click)") + : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), + [this](){ contextMenuAction( Mute ); } ); + + contextMenu.addSeparator(); + + contextMenu.addAction( embed::getIconPixmap( "colorize" ), + tr( "Set clip color" ), this, SLOT( changeClipColor() ) ); + contextMenu.addAction( embed::getIconPixmap( "colorize" ), + tr( "Use track color" ), this, SLOT( useTrackColor() ) ); + + constructContextMenu( &contextMenu ); + + contextMenu.exec( QCursor::pos() ); +} + +// This method processes the actions from the context menu of the TCO View. +void TrackContentObjectView::contextMenuAction( ContextMenuAction action ) +{ + QVector active = getClickedTCOs(); + // active will be later used for the remove, copy, cut or toggleMute methods + + switch( action ) + { + case Remove: + remove( active ); + break; + case Cut: + cut( active ); + break; + case Copy: + copy( active ); + break; + case Paste: + paste(); + break; + case Mute: + toggleMute( active ); + break; + } +} + +QVector TrackContentObjectView::getClickedTCOs() +{ + // Get a list of selected selectableObjects + QVector sos = gui->songEditor()->m_editor->selectedObjects(); + + // Convert to a list of selected TCOVs + QVector selection; + selection.reserve( sos.size() ); + for( auto so: sos ) + { + TrackContentObjectView *tcov = dynamic_cast ( so ); + if( tcov != nullptr ) + { + selection.append( tcov ); + } + } + + // If we clicked part of the selection, affect all selected clips. Otherwise affect the clip we clicked + return selection.contains(this) + ? selection + : QVector( 1, this ); +} + +void TrackContentObjectView::remove( QVector tcovs ) +{ + for( auto tcov: tcovs ) + { + // No need to check if it's nullptr because we check when building the QVector + tcov->remove(); + } +} + +void TrackContentObjectView::copy( QVector tcovs ) +{ + // For copyStringPair() + using namespace Clipboard; + + // Write the TCOs to a DataFile for copying + DataFile dataFile = createTCODataFiles( tcovs ); + + // Copy the TCO type as a key and the TCO data file to the clipboard + copyStringPair( QString( "tco_%1" ).arg( m_tco->getTrack()->type() ), + dataFile.toString() ); +} + +void TrackContentObjectView::cut( QVector tcovs ) +{ + // Copy the selected TCOs + copy( tcovs ); + + // Now that the TCOs are copied we can delete them, since we are cutting + remove( tcovs ); +} + +void TrackContentObjectView::paste() +{ + // For getMimeData() + using namespace Clipboard; + + // If possible, paste the selection on the TimePos of the selected Track and remove it + TimePos tcoPos = TimePos( m_tco->startPosition() ); + + TrackContentWidget *tcw = getTrackView()->getTrackContentWidget(); + + if( tcw->pasteSelection( tcoPos, getMimeData() ) ) + { + // If we succeed on the paste we delete the TCO we pasted on + remove(); + } +} + +void TrackContentObjectView::toggleMute( QVector tcovs ) +{ + for( auto tcov: tcovs ) + { + // No need to check for nullptr because we check while building the tcovs QVector + tcov->getTrackContentObject()->toggleMute(); + } +} + + + + +/*! \brief How many pixels a bar takes for this trackContentObjectView. + * + * \return the number of pixels per bar. + */ +float TrackContentObjectView::pixelsPerBar() +{ + return m_trackView->trackContainerView()->pixelsPerBar(); +} + + +/*! \brief Save the offsets between all selected tracks and a clicked track */ +void TrackContentObjectView::setInitialOffsets() +{ + QVector so = m_trackView->trackContainerView()->selectedObjects(); + QVector offsets; + for( QVector::iterator it = so.begin(); + it != so.end(); ++it ) + { + TrackContentObjectView * tcov = + dynamic_cast( *it ); + if( tcov == NULL ) + { + continue; + } + offsets.push_back( tcov->m_tco->startPosition() - m_initialTCOPos ); + } + + m_initialOffsets = offsets; +} + + + + +/*! \brief Detect whether the mouse moved more than n pixels on screen. + * + * \param _me The QMouseEvent. + * \param distance The threshold distance that the mouse has moved to return true. + */ +bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance ) +{ + QPoint dPos = mapToGlobal( me->pos() ) - m_initialMouseGlobalPos; + const int pixelsMoved = dPos.manhattanLength(); + return ( pixelsMoved > distance || pixelsMoved < -distance ); +} + + + +/*! \brief Calculate the new position of a dragged TCO from a mouse event + * + * + * \param me The QMouseEvent + */ +TimePos TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) +{ + //Pixels per bar + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); + // The pixel distance that the mouse has moved + const int mouseOff = mapToGlobal(me->pos()).x() - m_initialMouseGlobalPos.x(); + TimePos newPos = m_initialTCOPos + mouseOff * TimePos::ticksPerBar() / ppb; + TimePos offset = newPos - m_initialTCOPos; + // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize + if ( me->button() != Qt::NoButton + || (me->modifiers() & Qt::ControlModifier) + || (me->modifiers() & Qt::AltModifier) ) + { + // We want to preserve this adjusted offset, + // even if the user switches to snapping + setInitialPos( m_initialMousePos ); + } + else if ( me->modifiers() & Qt::ShiftModifier ) + { // If shift is held, quantize position (Default in 1.2.0 and earlier) + // or end position, whichever is closest to the actual position + TimePos startQ = newPos.quantize( gui->songEditor()->m_editor->getSnapSize() ); + // Find start position that gives snapped clip end position + TimePos endQ = ( newPos + m_tco->length() ); + endQ = endQ.quantize( gui->songEditor()->m_editor->getSnapSize() ); + endQ = endQ - m_tco->length(); + // Select the position closest to actual position + if ( abs(newPos - startQ) < abs(newPos - endQ) ) newPos = startQ; + else newPos = endQ; + } + else + { // Otherwise, quantize moved distance (preserves user offsets) + newPos = m_initialTCOPos + offset.quantize( gui->songEditor()->m_editor->getSnapSize() ); + } + return newPos; +} + + +// Return the color that the TCO's background should be +QColor TrackContentObjectView::getColorForDisplay( QColor defaultColor ) +{ + // Get the pure TCO color + auto tcoColor = m_tco->hasColor() + ? m_tco->usesCustomClipColor() + ? m_tco->color() + : m_tco->getTrack()->color() + : defaultColor; + + // Set variables + QColor c, mutedCustomColor; + bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); + mutedCustomColor = tcoColor; + mutedCustomColor.setHsv( mutedCustomColor.hsvHue(), mutedCustomColor.hsvSaturation() / 4, mutedCustomColor.value() ); + + // Change the pure color by state: selected, muted, colored, normal + if( isSelected() ) + { + c = m_tco->hasColor() + ? ( muted + ? mutedCustomColor.darker( 350 ) + : tcoColor.darker( 150 ) ) + : selectedColor(); + } + else + { + if( muted ) + { + c = m_tco->hasColor() + ? mutedCustomColor.darker( 250 ) + : mutedBackgroundColor(); + } + else + { + c = tcoColor; + } + } + + // Return color to caller + return c; +} + diff --git a/src/gui/TrackView.cpp b/src/gui/TrackView.cpp new file mode 100644 index 00000000000..a9387ea0d80 --- /dev/null +++ b/src/gui/TrackView.cpp @@ -0,0 +1,458 @@ +/* + * TrackView.cpp - implementation of TrackView class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "TrackView.h" + +#include +#include +#include +#include +#include +#include +#include + + +#include "ConfigManager.h" +#include "DataFile.h" +#include "Engine.h" +#include "FadeButton.h" +#include "Mixer.h" +#include "PixmapButton.h" +#include "StringPairDrag.h" +#include "ToolTip.h" +#include "Track.h" +#include "TrackContainerView.h" +#include "TrackContentObjectView.h" + + +/*! \brief Create a new track View. + * + * The track View is handles the actual display of the track, including + * displaying its various widgets and the track segments. + * + * \param track The track to display. + * \param tcv The track Container View for us to be displayed in. + * \todo Is my description of these properties correct? + */ +TrackView::TrackView( Track * track, TrackContainerView * tcv ) : + QWidget( tcv->contentWidget() ), /*!< The Track Container View's content widget. */ + ModelView( NULL, this ), /*!< The model view of this track */ + m_track( track ), /*!< The track we're displaying */ + m_trackContainerView( tcv ), /*!< The track Container View we're displayed in */ + m_trackOperationsWidget( this ), /*!< Our trackOperationsWidget */ + m_trackSettingsWidget( this ), /*!< Our trackSettingsWidget */ + m_trackContentWidget( this ), /*!< Our trackContentWidget */ + m_action( NoAction ) /*!< The action we're currently performing */ +{ + setAutoFillBackground( true ); + QPalette pal; + pal.setColor( backgroundRole(), QColor( 32, 36, 40 ) ); + setPalette( pal ); + + m_trackSettingsWidget.setAutoFillBackground( true ); + + QHBoxLayout * layout = new QHBoxLayout( this ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + layout->addWidget( &m_trackOperationsWidget ); + layout->addWidget( &m_trackSettingsWidget ); + layout->addWidget( &m_trackContentWidget, 1 ); + setFixedHeight( m_track->getHeight() ); + + resizeEvent( NULL ); + + setAcceptDrops( true ); + setAttribute( Qt::WA_DeleteOnClose, true ); + + + connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) ); + connect( m_track, + SIGNAL( trackContentObjectAdded( TrackContentObject * ) ), + this, SLOT( createTCOView( TrackContentObject * ) ), + Qt::QueuedConnection ); + + connect( &m_track->m_mutedModel, SIGNAL( dataChanged() ), + &m_trackContentWidget, SLOT( update() ) ); + + connect(&m_track->m_mutedModel, SIGNAL(dataChanged()), + this, SLOT(muteChanged())); + + connect( &m_track->m_soloModel, SIGNAL( dataChanged() ), + m_track, SLOT( toggleSolo() ), Qt::DirectConnection ); + + connect( &m_trackOperationsWidget, SIGNAL( colorChanged( QColor & ) ), + m_track, SLOT( trackColorChanged( QColor & ) ) ); + + connect( &m_trackOperationsWidget, SIGNAL( colorReset() ), + m_track, SLOT( trackColorReset() ) ); + + // create views for already existing TCOs + for( Track::tcoVector::iterator it = + m_track->m_trackContentObjects.begin(); + it != m_track->m_trackContentObjects.end(); ++it ) + { + createTCOView( *it ); + } + + m_trackContainerView->addTrackView( this ); +} + + + + +/*! \brief Destroy this track View. + * + */ +TrackView::~TrackView() +{ +} + + + + +/*! \brief Resize this track View. + * + * \param re the Resize Event to handle. + */ +void TrackView::resizeEvent( QResizeEvent * re ) +{ + if( ConfigManager::inst()->value( "ui", + "compacttrackbuttons" ).toInt() ) + { + m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH_COMPACT, height() - 1 ); + m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT, height() - 1 ); + } + else + { + m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH, height() - 1 ); + m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH, height() - 1 ); + } + m_trackContentWidget.setFixedHeight( height() ); +} + + + + +/*! \brief Update this track View and all its content objects. + * + */ +void TrackView::update() +{ + m_trackContentWidget.update(); + if( !m_trackContainerView->fixedTCOs() ) + { + m_trackContentWidget.changePosition(); + } + QWidget::update(); +} + + + + +/*! \brief Create a menu for assigning/creating channels for this track. + * + */ +QMenu * TrackView::createFxMenu(QString title, QString newFxLabel) +{ + Q_UNUSED(title) + Q_UNUSED(newFxLabel) + return NULL; +} + + + + +/*! \brief Close this track View. + * + */ +bool TrackView::close() +{ + m_trackContainerView->removeTrackView( this ); + return QWidget::close(); +} + + + + +/*! \brief Register that the model of this track View has changed. + * + */ +void TrackView::modelChanged() +{ + m_track = castModel(); + Q_ASSERT( m_track != NULL ); + connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) ); + m_trackOperationsWidget.m_muteBtn->setModel( &m_track->m_mutedModel ); + m_trackOperationsWidget.m_soloBtn->setModel( &m_track->m_soloModel ); + ModelView::modelChanged(); + setFixedHeight( m_track->getHeight() ); +} + + + + +/*! \brief Start a drag event on this track View. + * + * \param dee the DragEnterEvent to start. + */ +void TrackView::dragEnterEvent( QDragEnterEvent * dee ) +{ + StringPairDrag::processDragEnterEvent( dee, "track_" + + QString::number( m_track->type() ) ); +} + + + + +/*! \brief Accept a drop event on this track View. + * + * We only accept drop events that are of the same type as this track. + * If so, we decode the data from the drop event by just feeding it + * back into the engine as a state. + * + * \param de the DropEvent to handle. + */ +void TrackView::dropEvent( QDropEvent * de ) +{ + QString type = StringPairDrag::decodeKey( de ); + QString value = StringPairDrag::decodeValue( de ); + if( type == ( "track_" + QString::number( m_track->type() ) ) ) + { + // value contains our XML-data so simply create a + // DataFile which does the rest for us... + DataFile dataFile( value.toUtf8() ); + Engine::mixer()->requestChangeInModel(); + m_track->restoreState( dataFile.content().firstChild().toElement() ); + Engine::mixer()->doneChangeInModel(); + de->accept(); + } +} + + + + +/*! \brief Handle a mouse press event on this track View. + * + * If this track container supports rubber band selection, let the + * widget handle that and don't bother with any other handling. + * + * If the left mouse button is pressed, we handle two things. If + * SHIFT is pressed, then we resize vertically. Otherwise we start + * the process of moving this track to a new position. + * + * Otherwise we let the widget handle the mouse event as normal. + * + * \param me the MouseEvent to handle. + */ +void TrackView::mousePressEvent( QMouseEvent * me ) +{ + + // If previously dragged too small, restore on shift-leftclick + if( height() < DEFAULT_TRACK_HEIGHT && + me->modifiers() & Qt::ShiftModifier && + me->button() == Qt::LeftButton ) + { + setFixedHeight( DEFAULT_TRACK_HEIGHT ); + m_track->setHeight( DEFAULT_TRACK_HEIGHT ); + } + + + int widgetTotal = ConfigManager::inst()->value( "ui", + "compacttrackbuttons" ).toInt()==1 ? + DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT : + DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; + if( m_trackContainerView->allowRubberband() == true && me->x() > widgetTotal ) + { + QWidget::mousePressEvent( me ); + } + else if( me->button() == Qt::LeftButton ) + { + if( me->modifiers() & Qt::ShiftModifier ) + { + m_action = ResizeTrack; + QCursor::setPos( mapToGlobal( QPoint( me->x(), + height() ) ) ); + QCursor c( Qt::SizeVerCursor); + QApplication::setOverrideCursor( c ); + } + else + { + if( me->x()>10 ) // 10 = The width of the grip + 2 pixels to the left and right. + { + QWidget::mousePressEvent( me ); + return; + } + + m_action = MoveTrack; + + QCursor c( Qt::SizeVerCursor ); + QApplication::setOverrideCursor( c ); + // update because in move-mode, all elements in + // track-op-widgets are hidden as a visual feedback + m_trackOperationsWidget.update(); + } + + me->accept(); + } + else + { + QWidget::mousePressEvent( me ); + } +} + + + + +/*! \brief Handle a mouse move event on this track View. + * + * If this track container supports rubber band selection, let the + * widget handle that and don't bother with any other handling. + * + * Otherwise if we've started the move process (from mousePressEvent()) + * then move ourselves into that position, reordering the track list + * with moveTrackViewUp() and moveTrackViewDown() to suit. We make a + * note of this in the undo journal in case the user wants to undo this + * move. + * + * Likewise if we've started a resize process, handle this too, making + * sure that we never go below the minimum track height. + * + * \param me the MouseEvent to handle. + */ +void TrackView::mouseMoveEvent( QMouseEvent * me ) +{ + int widgetTotal = ConfigManager::inst()->value( "ui", + "compacttrackbuttons" ).toInt()==1 ? + DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT : + DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; + if( m_trackContainerView->allowRubberband() == true && me->x() > widgetTotal ) + { + QWidget::mouseMoveEvent( me ); + } + else if( m_action == MoveTrack ) + { + // look which track-widget the mouse-cursor is over + const int yPos = + m_trackContainerView->contentWidget()->mapFromGlobal( me->globalPos() ).y(); + const TrackView * trackAtY = m_trackContainerView->trackViewAt( yPos ); + + // debug code + // qDebug( "y position %d", yPos ); + + // a track-widget not equal to ourself? + if( trackAtY != NULL && trackAtY != this ) + { + // then move us up/down there! + if( me->y() < 0 ) + { + m_trackContainerView->moveTrackViewUp( this ); + } + else + { + m_trackContainerView->moveTrackViewDown( this ); + } + } + } + else if( m_action == ResizeTrack ) + { + setFixedHeight( qMax( me->y(), MINIMAL_TRACK_HEIGHT ) ); + m_trackContainerView->realignTracks(); + m_track->setHeight( height() ); + } + + if( height() < DEFAULT_TRACK_HEIGHT ) + { + ToolTip::add( this, m_track->m_name ); + } +} + + + +/*! \brief Handle a mouse release event on this track View. + * + * \param me the MouseEvent to handle. + */ +void TrackView::mouseReleaseEvent( QMouseEvent * me ) +{ + m_action = NoAction; + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + m_trackOperationsWidget.update(); + + QWidget::mouseReleaseEvent( me ); +} + + + + +/*! \brief Repaint this track View. + * + * \param pe the PaintEvent to start. + */ +void TrackView::paintEvent( QPaintEvent * pe ) +{ + QStyleOption opt; + opt.initFrom( this ); + QPainter p( this ); + style()->drawPrimitive( QStyle::PE_Widget, &opt, &p, this ); +} + + + + +/*! \brief Create a TrackContentObject View in this track View. + * + * \param tco the TrackContentObject to create the view for. + * \todo is this a good description for what this method does? + */ +void TrackView::createTCOView( TrackContentObject * tco ) +{ + TrackContentObjectView * tv = tco->createView( this ); + if( tco->getSelectViewOnCreate() == true ) + { + tv->setSelected( true ); + } + tco->selectViewOnCreate( false ); +} + + + + +void TrackView::muteChanged() +{ + FadeButton * indicator = getActivityIndicator(); + if (indicator) { setIndicatorMute(indicator, m_track->m_mutedModel.value()); } +} + + + + +void TrackView::setIndicatorMute(FadeButton* indicator, bool muted) +{ + QPalette::ColorRole role = muted ? QPalette::Highlight : QPalette::BrightText; + indicator->setActiveColor(QApplication::palette().color(QPalette::Active, role)); +} diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index 1406e683dac..f40d30bdac8 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -31,6 +31,7 @@ #include "ComboBox.h" #include "BBTrack.h" #include "BBTrackContainer.h" +#include "DataFile.h" #include "embed.h" #include "MainWindow.h" #include "Song.h" diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index c99176cc7fa..cee6870b7a3 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -52,6 +52,7 @@ #include "Clipboard.h" #include "ComboBox.h" #include "ConfigManager.h" +#include "DataFile.h" #include "debug.h" #include "DeprecationHelper.h" #include "DetuningHelper.h" diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 39fa1f6276a..c20ac7a3b7f 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "AudioDevice.h" @@ -293,7 +294,7 @@ float SongEditor::getSnapSize() const { val = val - m_zoomingModel->value() + 3; } - val = max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing. + val = std::max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing. if ( val >= 0 ){ return 1 << val; @@ -307,7 +308,7 @@ QString SongEditor::getSnapSizeString() const { int val = -m_snappingModel->value() + 3; val = val - m_zoomingModel->value() + 3; - val = max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing. + val = std::max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing. if ( val >= 0 ){ int bars = 1 << val; diff --git a/src/gui/widgets/FxLineLcdSpinBox.cpp b/src/gui/widgets/FxLineLcdSpinBox.cpp index bfe4a9637f9..5edf50c6c3e 100644 --- a/src/gui/widgets/FxLineLcdSpinBox.cpp +++ b/src/gui/widgets/FxLineLcdSpinBox.cpp @@ -27,7 +27,7 @@ #include "CaptionMenu.h" #include "FxMixerView.h" #include "GuiApplication.h" -#include "Track.h" +#include "TrackView.h" void FxLineLcdSpinBox::setTrackView(TrackView * tv) { diff --git a/src/gui/widgets/TrackContentWidget.cpp b/src/gui/widgets/TrackContentWidget.cpp new file mode 100644 index 00000000000..7064b1e7540 --- /dev/null +++ b/src/gui/widgets/TrackContentWidget.cpp @@ -0,0 +1,710 @@ +/* + * TrackContentWidget.cpp - implementation of TrackContentWidget class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "TrackContentWidget.h" + +#include +#include +#include +#include + +#include "AutomationPattern.h" +#include "BBEditor.h" +#include "BBTrackContainer.h" +#include "Clipboard.h" +#include "DataFile.h" +#include "Engine.h" +#include "GuiApplication.h" +#include "Song.h" +#include "SongEditor.h" +#include "StringPairDrag.h" +#include "TrackContainerView.h" +#include "TrackContentObjectView.h" +#include "TrackView.h" + + +/*! Alternate between a darker and a lighter background color every 4 bars + */ +const int BARS_PER_GROUP = 4; + + +/*! \brief Create a new trackContentWidget + * + * Creates a new track content widget for the given track. + * The content widget comprises the 'grip bar' and the 'tools' button + * for the track's context menu. + * + * \param parent The parent track. + */ +TrackContentWidget::TrackContentWidget( TrackView * parent ) : + QWidget( parent ), + m_trackView( parent ), + m_darkerColor( Qt::SolidPattern ), + m_lighterColor( Qt::SolidPattern ), + m_gridColor( Qt::SolidPattern ), + m_embossColor( Qt::SolidPattern ) +{ + setAcceptDrops( true ); + + connect( parent->trackContainerView(), + SIGNAL( positionChanged( const TimePos & ) ), + this, SLOT( changePosition( const TimePos & ) ) ); + + setStyle( QApplication::style() ); + + updateBackground(); +} + + + + +/*! \brief Destroy this trackContentWidget + * + * Destroys the trackContentWidget. + */ +TrackContentWidget::~TrackContentWidget() +{ +} + + + + +void TrackContentWidget::updateBackground() +{ + const TrackContainerView * tcv = m_trackView->trackContainerView(); + + // Assume even-pixels-per-bar. Makes sense, should be like this anyways + int ppb = static_cast( tcv->pixelsPerBar() ); + + int w = ppb * BARS_PER_GROUP; + int h = height(); + m_background = QPixmap( w * 2, height() ); + QPainter pmp( &m_background ); + + pmp.fillRect( 0, 0, w, h, darkerColor() ); + pmp.fillRect( w, 0, w , h, lighterColor() ); + + // draw lines + // vertical lines + pmp.setPen( QPen( gridColor(), 1 ) ); + for( float x = 0; x < w * 2; x += ppb ) + { + pmp.drawLine( QLineF( x, 0.0, x, h ) ); + } + + pmp.setPen( QPen( embossColor(), 1 ) ); + for( float x = 1.0; x < w * 2; x += ppb ) + { + pmp.drawLine( QLineF( x, 0.0, x, h ) ); + } + + // horizontal line + pmp.setPen( QPen( gridColor(), 1 ) ); + pmp.drawLine( 0, h-1, w*2, h-1 ); + + pmp.end(); + + // Force redraw + update(); +} + + + + +/*! \brief Adds a trackContentObjectView to this widget. + * + * Adds a(nother) trackContentObjectView to our list of views. We also + * check that our position is up-to-date. + * + * \param tcov The trackContentObjectView to add. + */ +void TrackContentWidget::addTCOView( TrackContentObjectView * tcov ) +{ + TrackContentObject * tco = tcov->getTrackContentObject(); + + m_tcoViews.push_back( tcov ); + + tco->saveJournallingState( false ); + changePosition(); + tco->restoreJournallingState(); +} + + + + +/*! \brief Removes the given trackContentObjectView to this widget. + * + * Removes the given trackContentObjectView from our list of views. + * + * \param tcov The trackContentObjectView to add. + */ +void TrackContentWidget::removeTCOView( TrackContentObjectView * tcov ) +{ + tcoViewVector::iterator it = std::find( m_tcoViews.begin(), + m_tcoViews.end(), + tcov ); + if( it != m_tcoViews.end() ) + { + m_tcoViews.erase( it ); + Engine::getSong()->setModified(); + } +} + + + + +/*! \brief Update ourselves by updating all the tCOViews attached. + * + */ +void TrackContentWidget::update() +{ + for( tcoViewVector::iterator it = m_tcoViews.begin(); + it != m_tcoViews.end(); ++it ) + { + ( *it )->setFixedHeight( height() - 1 ); + ( *it )->update(); + } + QWidget::update(); +} + + + + +// resposible for moving track-content-widgets to appropriate position after +// change of visible viewport +/*! \brief Move the trackContentWidget to a new place in time + * + * \param newPos The MIDI time to move to. + */ +void TrackContentWidget::changePosition( const TimePos & newPos ) +{ + if( m_trackView->trackContainerView() == gui->getBBEditor()->trackContainerView() ) + { + const int curBB = Engine::getBBTrackContainer()->currentBB(); + setUpdatesEnabled( false ); + + // first show TCO for current BB... + for( tcoViewVector::iterator it = m_tcoViews.begin(); + it != m_tcoViews.end(); ++it ) + { + if( ( *it )->getTrackContentObject()-> + startPosition().getBar() == curBB ) + { + ( *it )->move( 0, ( *it )->y() ); + ( *it )->raise(); + ( *it )->show(); + } + else + { + ( *it )->lower(); + } + } + // ...then hide others to avoid flickering + for( tcoViewVector::iterator it = m_tcoViews.begin(); + it != m_tcoViews.end(); ++it ) + { + if( ( *it )->getTrackContentObject()-> + startPosition().getBar() != curBB ) + { + ( *it )->hide(); + } + } + setUpdatesEnabled( true ); + return; + } + + TimePos pos = newPos; + if( pos < 0 ) + { + pos = m_trackView->trackContainerView()->currentPosition(); + } + + const int begin = pos; + const int end = endPosition( pos ); + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); + + setUpdatesEnabled( false ); + for( tcoViewVector::iterator it = m_tcoViews.begin(); + it != m_tcoViews.end(); ++it ) + { + TrackContentObjectView * tcov = *it; + TrackContentObject * tco = tcov->getTrackContentObject(); + + tco->changeLength( tco->length() ); + + const int ts = tco->startPosition(); + const int te = tco->endPosition()-3; + if( ( ts >= begin && ts <= end ) || + ( te >= begin && te <= end ) || + ( ts <= begin && te >= end ) ) + { + tcov->move( static_cast( ( ts - begin ) * ppb / + TimePos::ticksPerBar() ), + tcov->y() ); + if( !tcov->isVisible() ) + { + tcov->show(); + } + } + else + { + tcov->move( -tcov->width()-10, tcov->y() ); + } + } + setUpdatesEnabled( true ); + + // redraw background +// update(); +} + + + + +/*! \brief Return the position of the trackContentWidget in bars. + * + * \param mouseX the mouse's current X position in pixels. + */ +TimePos TrackContentWidget::getPosition( int mouseX ) +{ + TrackContainerView * tv = m_trackView->trackContainerView(); + return TimePos( tv->currentPosition() + + mouseX * + TimePos::ticksPerBar() / + static_cast( tv->pixelsPerBar() ) ); +} + + + + +/*! \brief Respond to a drag enter event on the trackContentWidget + * + * \param dee the Drag Enter Event to respond to + */ +void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) +{ + TimePos tcoPos = getPosition( dee->pos().x() ); + if( canPasteSelection( tcoPos, dee ) == false ) + { + dee->ignore(); + } + else + { + StringPairDrag::processDragEnterEvent( dee, "tco_" + + QString::number( getTrack()->type() ) ); + } +} + + + + +/*! \brief Returns whether a selection of TCOs can be pasted into this + * + * \param tcoPos the position of the TCO slot being pasted on + * \param de the DropEvent generated + */ +bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QDropEvent* de ) +{ + const QMimeData * mimeData = de->mimeData(); + + // If the source of the DropEvent is the current instance of LMMS we don't allow pasting in the same bar + // if it's another instance of LMMS we allow it + return de->source() + ? canPasteSelection( tcoPos, mimeData ) + : canPasteSelection( tcoPos, mimeData, true ); +} + +// Overloaded method to make it possible to call this method without a Drag&Drop event +bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QMimeData* md , bool allowSameBar ) +{ + // For decodeKey() and decodeValue() + using namespace Clipboard; + + Track * t = getTrack(); + QString type = decodeKey( md ); + QString value = decodeValue( md ); + + // We can only paste into tracks of the same type + if( type != ( "tco_" + QString::number( t->type() ) ) || + m_trackView->trackContainerView()->fixedTCOs() == true ) + { + return false; + } + + // value contains XML needed to reconstruct TCOs and place them + DataFile dataFile( value.toUtf8() ); + + // Extract the metadata and which TCO was grabbed + QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" ); + QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); + TimePos grabbedTCOPos = tcoPosAttr.value().toInt(); + TimePos grabbedTCOBar = TimePos( grabbedTCOPos.getBar(), 0 ); + + // Extract the track index that was originally clicked + QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); + const int initialTrackIndex = tiAttr.value().toInt(); + + // Get the current track's index + const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); + const int currentTrackIndex = tracks.indexOf( t ); + + // Don't paste if we're on the same bar and allowSameBar is false + auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); + if( !allowSameBar && sourceTrackContainerId == t->trackContainer()->id() && + tcoPos == grabbedTCOBar && currentTrackIndex == initialTrackIndex ) + { + return false; + } + + // Extract the tco data + QDomElement tcoParent = dataFile.content().firstChildElement( "tcos" ); + QDomNodeList tcoNodes = tcoParent.childNodes(); + + // Determine if all the TCOs will land on a valid track + for( int i = 0; i < tcoNodes.length(); i++ ) + { + QDomElement tcoElement = tcoNodes.item( i ).toElement(); + int trackIndex = tcoElement.attributeNode( "trackIndex" ).value().toInt(); + int finalTrackIndex = trackIndex + currentTrackIndex - initialTrackIndex; + + // Track must be in TrackContainer's tracks + if( finalTrackIndex < 0 || finalTrackIndex >= tracks.size() ) + { + return false; + } + + // Track must be of the same type + auto startTrackType = tcoElement.attributeNode("trackType").value().toInt(); + Track * endTrack = tracks.at( finalTrackIndex ); + if( startTrackType != endTrack->type() ) + { + return false; + } + } + + return true; +} + +/*! \brief Pastes a selection of TCOs onto the track + * + * \param tcoPos the position of the TCO slot being pasted on + * \param de the DropEvent generated + */ +bool TrackContentWidget::pasteSelection( TimePos tcoPos, QDropEvent * de ) +{ + const QMimeData * mimeData = de->mimeData(); + + if( canPasteSelection( tcoPos, de ) == false ) + { + return false; + } + + // We set skipSafetyCheck to true because we already called canPasteSelection + return pasteSelection( tcoPos, mimeData, true ); +} + +// Overloaded method so we can call it without a Drag&Drop event +bool TrackContentWidget::pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck ) +{ + // For decodeKey() and decodeValue() + using namespace Clipboard; + + // When canPasteSelection was already called before, skipSafetyCheck will skip this + if( !skipSafetyCheck && canPasteSelection( tcoPos, md ) == false ) + { + return false; + } + + QString type = decodeKey( md ); + QString value = decodeValue( md ); + + getTrack()->addJournalCheckPoint(); + + // value contains XML needed to reconstruct TCOs and place them + DataFile dataFile( value.toUtf8() ); + + // Extract the tco data + QDomElement tcoParent = dataFile.content().firstChildElement( "tcos" ); + QDomNodeList tcoNodes = tcoParent.childNodes(); + + // Extract the track index that was originally clicked + QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" ); + QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); + int initialTrackIndex = tiAttr.value().toInt(); + QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); + TimePos grabbedTCOPos = tcoPosAttr.value().toInt(); + + // Snap the mouse position to the beginning of the dropped bar, in ticks + const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks(); + const int currentTrackIndex = tracks.indexOf( getTrack() ); + + bool wasSelection = m_trackView->trackContainerView()->rubberBand()->selectedObjects().count(); + + // Unselect the old group + const QVector so = + m_trackView->trackContainerView()->selectedObjects(); + for( QVector::const_iterator it = so.begin(); + it != so.end(); ++it ) + { + ( *it )->setSelected( false ); + } + + + // TODO -- Need to draw the hovericon either way, or ghost the TCOs + // onto their final position. + + float snapSize = gui->songEditor()->m_editor->getSnapSize(); + // All patterns should be offset the same amount as the grabbed pattern + TimePos offset = TimePos(tcoPos - grabbedTCOPos); + // Users expect clips to "fall" backwards, so bias the offset + offset = offset - TimePos::ticksPerBar() * snapSize / 2; + // The offset is quantized (rather than the positions) to preserve fine adjustments + offset = offset.quantize(snapSize); + + // Get the leftmost TCO and fix the offset if it reaches below bar 0 + TimePos leftmostPos = grabbedTCOPos; + for(int i = 0; i < tcoNodes.length(); ++i) + { + QDomElement outerTCOElement = tcoNodes.item(i).toElement(); + QDomElement tcoElement = outerTCOElement.firstChildElement(); + + TimePos pos = tcoElement.attributeNode("pos").value().toInt(); + + if(pos < leftmostPos) { leftmostPos = pos; } + } + // Fix offset if it sets the left most TCO to a negative position + offset = std::max(offset.getTicks(), -leftmostPos.getTicks()); + + for( int i = 0; isongEditor()->m_editor->getSnapSize(); + if (offset == 0) { pos += shift; } + + TrackContentObject * tco = t->createTCO( pos ); + tco->restoreState( tcoElement ); + tco->movePosition(pos); // Because we restored the state, we need to move the TCO again. + if( wasSelection ) + { + tco->selectViewOnCreate( true ); + } + } + + AutomationPattern::resolveAllIDs(); + + return true; +} + + +/*! \brief Respond to a drop event on the trackContentWidget + * + * \param de the Drop Event to respond to + */ +void TrackContentWidget::dropEvent( QDropEvent * de ) +{ + TimePos tcoPos = TimePos( getPosition( de->pos().x() ) ); + if( pasteSelection( tcoPos, de ) == true ) + { + de->accept(); + } +} + + + + +/*! \brief Respond to a mouse press on the trackContentWidget + * + * \param me the mouse press event to respond to + */ +void TrackContentWidget::mousePressEvent( QMouseEvent * me ) +{ + if( m_trackView->trackContainerView()->allowRubberband() == true ) + { + QWidget::mousePressEvent( me ); + } + else if( me->modifiers() & Qt::ShiftModifier ) + { + QWidget::mousePressEvent( me ); + } + else if( me->button() == Qt::LeftButton && + !m_trackView->trackContainerView()->fixedTCOs() ) + { + QVector so = m_trackView->trackContainerView()->rubberBand()->selectedObjects(); + for( int i = 0; i < so.count(); ++i ) + { + so.at( i )->setSelected( false); + } + getTrack()->addJournalCheckPoint(); + const TimePos pos = getPosition( me->x() ).getBar() * + TimePos::ticksPerBar(); + getTrack()->createTCO(pos); + } +} + + + + +/*! \brief Repaint the trackContentWidget on command + * + * \param pe the Paint Event to respond to + */ +void TrackContentWidget::paintEvent( QPaintEvent * pe ) +{ + // Assume even-pixels-per-bar. Makes sense, should be like this anyways + const TrackContainerView * tcv = m_trackView->trackContainerView(); + int ppb = static_cast( tcv->pixelsPerBar() ); + QPainter p( this ); + // Don't draw background on BB-Editor + if( m_trackView->trackContainerView() != gui->getBBEditor()->trackContainerView() ) + { + p.drawTiledPixmap( rect(), m_background, QPoint( + tcv->currentPosition().getBar() * ppb, 0 ) ); + } +} + + + + +/*! \brief Updates the background tile pixmap on size changes. + * + * \param resizeEvent the resize event to pass to base class + */ +void TrackContentWidget::resizeEvent( QResizeEvent * resizeEvent ) +{ + // Update backgroud + updateBackground(); + // Force redraw + QWidget::resizeEvent( resizeEvent ); +} + + + + +/*! \brief Return the track shown by the trackContentWidget + * + */ +Track * TrackContentWidget::getTrack() +{ + return m_trackView->getTrack(); +} + + + + +/*! \brief Return the end position of the trackContentWidget in Bars. + * + * \param posStart the starting position of the Widget (from getPosition()) + */ +TimePos TrackContentWidget::endPosition( const TimePos & posStart ) +{ + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); + const int w = width(); + return posStart + static_cast( w * TimePos::ticksPerBar() / ppb ); +} + +void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) +{ + // For hasFormat(), MimeType enum class and getMimeData() + using namespace Clipboard; + + if( cme->modifiers() ) + { + return; + } + + // If we don't have TCO data in the clipboard there's no need to create this menu + // since "paste" is the only action at the moment. + if( ! hasFormat( MimeType::StringPair ) ) + { + return; + } + + QMenu contextMenu( this ); + QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } ); + // If we can't paste in the current TCW for some reason, disable the action so the user knows + pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), getMimeData() ) ? true : false ); + + contextMenu.exec( QCursor::pos() ); +} + +void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action ) +{ + // For getMimeData() + using namespace Clipboard; + + switch( action ) + { + case Paste: + // Paste the selection on the TimePos of the context menu event + TimePos tcoPos = getPosition( cme->x() ); + + pasteSelection( tcoPos, getMimeData() ); + break; + } +} + + + +// qproperty access methods +//! \brief CSS theming qproperty access method +QBrush TrackContentWidget::darkerColor() const +{ return m_darkerColor; } + +//! \brief CSS theming qproperty access method +QBrush TrackContentWidget::lighterColor() const +{ return m_lighterColor; } + +//! \brief CSS theming qproperty access method +QBrush TrackContentWidget::gridColor() const +{ return m_gridColor; } + +//! \brief CSS theming qproperty access method +QBrush TrackContentWidget::embossColor() const +{ return m_embossColor; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setDarkerColor( const QBrush & c ) +{ m_darkerColor = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setLighterColor( const QBrush & c ) +{ m_lighterColor = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setGridColor( const QBrush & c ) +{ m_gridColor = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setEmbossColor( const QBrush & c ) +{ m_embossColor = c; } + diff --git a/src/gui/widgets/TrackOperationsWidget.cpp b/src/gui/widgets/TrackOperationsWidget.cpp new file mode 100644 index 00000000000..8588aeb5a5d --- /dev/null +++ b/src/gui/widgets/TrackOperationsWidget.cpp @@ -0,0 +1,353 @@ +/* + * TrackOperationsWidget.cpp - implementation of TrackOperationsWidget class + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "TrackOperationsWidget.h" + +#include +#include +#include +#include + +#include "AutomationPattern.h" +#include "AutomationTrack.h" +#include "ColorChooser.h" +#include "ConfigManager.h" +#include "DataFile.h" +#include "embed.h" +#include "Engine.h" +#include "gui_templates.h" +#include "InstrumentTrack.h" +#include "PixmapButton.h" +#include "Song.h" +#include "StringPairDrag.h" +#include "ToolTip.h" +#include "Track.h" +#include "TrackContainerView.h" +#include "TrackView.h" + +/*! \brief Create a new trackOperationsWidget + * + * The trackOperationsWidget is the grip and the mute button of a track. + * + * \param parent the trackView to contain this widget + */ +TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : + QWidget( parent ), /*!< The parent widget */ + m_trackView( parent ) /*!< The parent track view */ +{ + ToolTip::add( this, tr( "Press <%1> while clicking on move-grip " + "to begin a new drag'n'drop action." ).arg(UI_CTRL_KEY) ); + + QMenu * toMenu = new QMenu( this ); + toMenu->setFont( pointSize<9>( toMenu->font() ) ); + connect( toMenu, SIGNAL( aboutToShow() ), this, SLOT( updateMenu() ) ); + + + setObjectName( "automationEnabled" ); + + + m_trackOps = new QPushButton( this ); + m_trackOps->move( 12, 1 ); + m_trackOps->setFocusPolicy( Qt::NoFocus ); + m_trackOps->setMenu( toMenu ); + ToolTip::add( m_trackOps, tr( "Actions" ) ); + + + m_muteBtn = new PixmapButton( this, tr( "Mute" ) ); + m_muteBtn->setActiveGraphic( embed::getIconPixmap( "led_off" ) ); + m_muteBtn->setInactiveGraphic( embed::getIconPixmap( "led_green" ) ); + m_muteBtn->setCheckable( true ); + + m_soloBtn = new PixmapButton( this, tr( "Solo" ) ); + m_soloBtn->setActiveGraphic( embed::getIconPixmap( "led_red" ) ); + m_soloBtn->setInactiveGraphic( embed::getIconPixmap( "led_off" ) ); + m_soloBtn->setCheckable( true ); + + if( ConfigManager::inst()->value( "ui", + "compacttrackbuttons" ).toInt() ) + { + m_muteBtn->move( 46, 0 ); + m_soloBtn->move( 46, 16 ); + } + else + { + m_muteBtn->move( 46, 8 ); + m_soloBtn->move( 62, 8 ); + } + + m_muteBtn->show(); + ToolTip::add( m_muteBtn, tr( "Mute" ) ); + + m_soloBtn->show(); + ToolTip::add( m_soloBtn, tr( "Solo" ) ); + + connect( this, SIGNAL( trackRemovalScheduled( TrackView * ) ), + m_trackView->trackContainerView(), + SLOT( deleteTrackView( TrackView * ) ), + Qt::QueuedConnection ); + + connect( m_trackView->getTrack()->getMutedModel(), SIGNAL( dataChanged() ), + this, SLOT( update() ) ); + +} + + + + +/*! \brief Destroy an existing trackOperationsWidget + * + */ +TrackOperationsWidget::~TrackOperationsWidget() +{ +} + + + + +/*! \brief Respond to trackOperationsWidget mouse events + * + * If it's the left mouse button, and Ctrl is held down, and we're + * not a Beat+Bassline Editor track, then start a new drag event to + * copy this track. + * + * Otherwise, ignore all other events. + * + * \param me The mouse event to respond to. + */ +void TrackOperationsWidget::mousePressEvent( QMouseEvent * me ) +{ + if( me->button() == Qt::LeftButton && + me->modifiers() & Qt::ControlModifier && + m_trackView->getTrack()->type() != Track::BBTrack ) + { + DataFile dataFile( DataFile::DragNDropData ); + m_trackView->getTrack()->saveState( dataFile, dataFile.content() ); + new StringPairDrag( QString( "track_%1" ).arg( + m_trackView->getTrack()->type() ), + dataFile.toString(), m_trackView->getTrackSettingsWidget()->grab(), + this ); + } + else if( me->button() == Qt::LeftButton ) + { + // track-widget (parent-widget) initiates track-move + me->ignore(); + } +} + + + + +/*! \brief Repaint the trackOperationsWidget + * + * If we're not moving, and in the Beat+Bassline Editor, then turn + * automation on or off depending on its previous state and show + * ourselves. + * + * Otherwise, hide ourselves. + * + * \todo Flesh this out a bit - is it correct? + * \param pe The paint event to respond to + */ +void TrackOperationsWidget::paintEvent( QPaintEvent * pe ) +{ + QPainter p( this ); + + p.fillRect( rect(), palette().brush(QPalette::Background) ); + + if( m_trackView->getTrack()->useColor() && ! m_trackView->getTrack()->getMutedModel()->value() ) + { + QRect coloredRect( 0, 0, 10, m_trackView->getTrack()->getHeight() ); + + p.fillRect( coloredRect, m_trackView->getTrack()->color() ); + } + + if( m_trackView->isMovingTrack() == false ) + { + p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip")); + } + else + { + p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip_c")); + } +} + + + + +/*! \brief Clone this track + * + */ +void TrackOperationsWidget::cloneTrack() +{ + TrackContainerView *tcView = m_trackView->trackContainerView(); + + Track *newTrack = m_trackView->getTrack()->clone(); + TrackView *newTrackView = tcView->createTrackView( newTrack ); + + int index = tcView->trackViews().indexOf( m_trackView ); + int i = tcView->trackViews().size(); + while ( i != index + 1 ) + { + tcView->moveTrackView( newTrackView, i - 1 ); + i--; + } +} + + +/*! \brief Clear this track - clears all TCOs from the track */ +void TrackOperationsWidget::clearTrack() +{ + Track * t = m_trackView->getTrack(); + t->addJournalCheckPoint(); + t->lock(); + t->deleteTCOs(); + t->unlock(); +} + + + +/*! \brief Remove this track from the track list + * + */ +void TrackOperationsWidget::removeTrack() +{ + emit trackRemovalScheduled( m_trackView ); +} + +void TrackOperationsWidget::changeTrackColor() +{ + QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )-> \ + getColor( m_trackView->getTrack()->color() ); + + if( ! new_color.isValid() ) + { return; } + + emit colorChanged( new_color ); + + Engine::getSong()->setModified(); + update(); +} + +void TrackOperationsWidget::resetTrackColor() +{ + emit colorReset(); + Engine::getSong()->setModified(); + update(); +} + +void TrackOperationsWidget::randomTrackColor() +{ + QColor buffer = ColorChooser::getPalette( ColorChooser::Palette::Track )[ rand() % 48 ]; + + emit colorChanged( buffer ); + Engine::getSong()->setModified(); + update(); +} + +void TrackOperationsWidget::useTrackColor() +{ + emit colorParented(); + Engine::getSong()->setModified(); +} + + +/*! \brief Update the trackOperationsWidget context menu + * + * For all track types, we have the Clone and Remove options. + * For instrument-tracks we also offer the MIDI-control-menu + * For automation tracks, extra options: turn on/off recording + * on all TCOs (same should be added for sample tracks when + * sampletrack recording is implemented) + */ +void TrackOperationsWidget::updateMenu() +{ + QMenu * toMenu = m_trackOps->menu(); + toMenu->clear(); + toMenu->addAction( embed::getIconPixmap( "edit_copy", 16, 16 ), + tr( "Clone this track" ), + this, SLOT( cloneTrack() ) ); + toMenu->addAction( embed::getIconPixmap( "cancel", 16, 16 ), + tr( "Remove this track" ), + this, SLOT( removeTrack() ) ); + + if( ! m_trackView->trackContainerView()->fixedTCOs() ) + { + toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) ); + } + if (QMenu *fxMenu = m_trackView->createFxMenu(tr("FX %1: %2"), tr("Assign to new FX Channel"))) + { + toMenu->addMenu(fxMenu); + } + + if (InstrumentTrackView * trackView = dynamic_cast(m_trackView)) + { + toMenu->addSeparator(); + toMenu->addMenu(trackView->midiMenu()); + } + if( dynamic_cast( m_trackView ) ) + { + toMenu->addAction( tr( "Turn all recording on" ), this, SLOT( recordingOn() ) ); + toMenu->addAction( tr( "Turn all recording off" ), this, SLOT( recordingOff() ) ); + } + + toMenu->addSeparator(); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Change color" ), this, SLOT( changeTrackColor() ) ); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Reset color to default" ), this, SLOT( resetTrackColor() ) ); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Set random color" ), this, SLOT( randomTrackColor() ) ); + toMenu->addSeparator(); + toMenu->addAction( embed::getIconPixmap( "colorize" ), + tr( "Clear clip colors" ), this, SLOT( useTrackColor() ) ); +} + + +void TrackOperationsWidget::toggleRecording( bool on ) +{ + AutomationTrackView * atv = dynamic_cast( m_trackView ); + if( atv ) + { + for( TrackContentObject * tco : atv->getTrack()->getTCOs() ) + { + AutomationPattern * ap = dynamic_cast( tco ); + if( ap ) { ap->setRecording( on ); } + } + atv->update(); + } +} + + + +void TrackOperationsWidget::recordingOn() +{ + toggleRecording( true ); +} + + +void TrackOperationsWidget::recordingOff() +{ + toggleRecording( false ); +} + diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 8de8437abd3..1460d5eef79 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -23,6 +23,7 @@ */ #include "BBTrack.h" +#include #include #include diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 702257f2959..a3fc0bc5699 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -42,6 +42,7 @@ #include "CaptionMenu.h" #include "ConfigManager.h" #include "ControllerConnection.h" +#include "DataFile.h" #include "EffectChain.h" #include "EffectRackView.h" #include "embed.h" diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 31768de75da..613d8f173ac 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -24,6 +24,7 @@ */ #include "SampleTrack.h" +#include #include #include #include From 3ad0462d44196d6e48fd66350b6af46860547c5f Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 7 Dec 2020 13:55:50 +0900 Subject: [PATCH 172/180] Fix too small height of the carla instrument window (#5829) --- src/tracks/InstrumentTrack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a3fc0bc5699..596bd5c1237 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1551,14 +1551,14 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : vlayout->addWidget( m_pianoView ); setModel( _itv->model() ); - updateInstrumentView(); - QMdiSubWindow* subWin = gui->mainWindow()->addWindowedWidget( this ); Qt::WindowFlags flags = subWin->windowFlags(); flags |= Qt::MSWindowsFixedSizeDialogHint; flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); + updateInstrumentView(); + // Hide the Size and Maximize options from the system menu // since the dialog size is fixed. QMenu * systemMenu = subWin->systemMenu(); From 53a733ba664b5350d5b1c98f9d4890ff2ddff6e7 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 7 Dec 2020 17:11:41 -0300 Subject: [PATCH 173/180] Fixes bug where clicking on the Activity Indicator doesn't play a note (#5824) --- include/MidiEvent.h | 27 ++++++++++++++------------- src/tracks/InstrumentTrack.cpp | 6 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/MidiEvent.h b/include/MidiEvent.h index 177ef75ea94..a82c5d4645c 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -33,30 +33,32 @@ class MidiEvent { public: + enum class Source { Internal, External }; + MidiEvent(MidiEventTypes type = MidiActiveSensing, int8_t channel = 0, int16_t param1 = 0, int16_t param2 = 0, const void* sourcePort = nullptr, - bool ignoreOnExport = true) : + Source source = Source::External) : m_type( type ), m_metaEvent( MidiMetaInvalid ), m_channel( channel ), m_sysExData( NULL ), m_sourcePort(sourcePort), - m_ignoreOnExport(ignoreOnExport) + m_source(source) { m_data.m_param[0] = param1; m_data.m_param[1] = param2; } - MidiEvent(MidiEventTypes type, const char* sysExData, int dataLen, bool ignoreOnExport = true) : + MidiEvent(MidiEventTypes type, const char* sysExData, std::size_t dataLen, Source source = Source::External) : m_type( type ), m_metaEvent( MidiMetaInvalid ), m_channel( 0 ), m_sysExData( sysExData ), m_sourcePort(nullptr), - m_ignoreOnExport(ignoreOnExport) + m_source(source) { m_data.m_sysExDataLen = dataLen; } @@ -68,7 +70,7 @@ class MidiEvent m_data( other.m_data ), m_sysExData( other.m_sysExData ), m_sourcePort(other.m_sourcePort), - m_ignoreOnExport(other.m_ignoreOnExport) + m_source(other.m_source) { } @@ -194,14 +196,14 @@ class MidiEvent setParam( 0, pitchBend ); } - bool ignoreOnExport() const + Source source() const { - return m_ignoreOnExport; + return m_source; } - void setIgnoreOnExport(bool value) + void setSource(Source value) { - m_ignoreOnExport = value; + m_source = value; } @@ -212,16 +214,15 @@ class MidiEvent union { int16_t m_param[2]; // first/second parameter (key/velocity) - uint8_t m_bytes[4]; // raw bytes + uint8_t m_bytes[4]; // raw bytes int32_t m_sysExDataLen; // len of m_sysExData } m_data; const char* m_sysExData; const void* m_sourcePort; - // This helps us ignore MIDI events that shouldn't be processed - // during a project export, like physical controller events. - bool m_ignoreOnExport; + // Stores the source of the MidiEvent: Internal or External (hardware controllers). + Source m_source; } ; #endif diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 596bd5c1237..4c5304fe9d2 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -268,10 +268,10 @@ void InstrumentTrack::processCCEvent(int controller) uint16_t cc = static_cast(controller); uint16_t value = static_cast(m_midiCCModel[controller]->value()); - // Process the MIDI CC event as an input event but with ignoreOnExport set to false + // Process the MIDI CC event as an input event but with source set to Internal // so we can know LMMS generated the event, not a controller, and can process it during // the project export - processInEvent(MidiEvent(MidiControlChange, channel, cc, value, NULL, false)); + processInEvent(MidiEvent(MidiControlChange, channel, cc, value, nullptr, MidiEvent::Source::Internal)); } @@ -279,7 +279,7 @@ void InstrumentTrack::processCCEvent(int controller) void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { - if (Engine::getSong()->isExporting() && event.ignoreOnExport()) + if (Engine::getSong()->isExporting() && event.source() == MidiEvent::Source::External) { return; } From 118d63bada26ab99a4289705c0aabee383414909 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Mon, 7 Dec 2020 15:42:05 -0600 Subject: [PATCH 174/180] Change abs to std::abs (#5831) This prevents GCC 6 from raising an ambiguous call error. --- plugins/Vectorscope/VectorView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Vectorscope/VectorView.cpp b/plugins/Vectorscope/VectorView.cpp index 9a3f855eb98..330c666cf12 100644 --- a/plugins/Vectorscope/VectorView.cpp +++ b/plugins/Vectorscope/VectorView.cpp @@ -168,7 +168,7 @@ void VectorView::paintEvent(QPaintEvent *event) // To better preserve shapes, the log scale is applied to the distance from origin, // not the individual channels. const float distance = sqrt(inLeft * inLeft + inRight * inRight); - const float distanceLog = log10(1 + 9 * abs(distance)); + const float distanceLog = log10(1 + 9 * std::abs(distance)); const float angleCos = inLeft / distance; const float angleSin = inRight / distance; left = distanceLog * angleCos * (activeSize - 1) / 4; @@ -222,7 +222,7 @@ void VectorView::paintEvent(QPaintEvent *event) float inRight = inBuffer[frame][1] * m_zoom; if (logScale) { const float distance = sqrt(inLeft * inLeft + inRight * inRight); - const float distanceLog = log10(1 + 9 * abs(distance)); + const float distanceLog = log10(1 + 9 * std::abs(distance)); const float angleCos = inLeft / distance; const float angleSin = inRight / distance; left = distanceLog * angleCos * (activeSize - 1) / 4; From cd2366a21c543abcd66929b203943ab3737ade00 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Mon, 7 Dec 2020 19:09:34 -0300 Subject: [PATCH 175/180] Fix style on "SampleBuffer.cpp" and "SampleBuffer.h" (#5791) Fix code style issues in the `SampleBuffer` class. Remove strange comments around "not an Ogg Vorbis file" warning. --- include/SampleBuffer.h | 158 +++--- src/core/SampleBuffer.cpp | 1086 +++++++++++++++++++------------------ 2 files changed, 639 insertions(+), 605 deletions(-) diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 680ec60c488..75b90e01708 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -62,7 +62,7 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject { MM_OPERATORS public: - handleState( bool _varying_pitch = false, int interpolation_mode = SRC_LINEAR ); + handleState(bool varyingPitch = false, int interpolationMode = SRC_LINEAR); virtual ~handleState(); const f_cnt_t frameIndex() const @@ -70,9 +70,9 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject return m_frameIndex; } - void setFrameIndex( f_cnt_t _index ) + void setFrameIndex(f_cnt_t index) { - m_frameIndex = _index; + m_frameIndex = index; } bool isBackwards() const @@ -80,9 +80,9 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject return m_isBackwards; } - void setBackwards( bool _backwards ) + void setBackwards(bool backwards) { - m_isBackwards = _backwards; + m_isBackwards = backwards; } int interpolationMode() const @@ -106,21 +106,35 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject SampleBuffer(); // constructor which either loads sample _audio_file or decodes // base64-data out of string - SampleBuffer( const QString & _audio_file, bool _is_base64_data = false ); - SampleBuffer( const sampleFrame * _data, const f_cnt_t _frames ); - explicit SampleBuffer( const f_cnt_t _frames ); + SampleBuffer(const QString & audioFile, bool isBase64Data = false); + SampleBuffer(const sampleFrame * data, const f_cnt_t frames); + explicit SampleBuffer(const f_cnt_t frames); virtual ~SampleBuffer(); - bool play( sampleFrame * _ab, handleState * _state, - const fpp_t _frames, - const float _freq, - const LoopMode _loopmode = LoopOff ); - - void visualize(QPainter & p, const QRect & dr, const QRect & clip, f_cnt_t from_frame = 0, f_cnt_t to_frame = 0); - inline void visualize(QPainter & p, const QRect & dr, f_cnt_t from_frame = 0, f_cnt_t to_frame = 0) + bool play( + sampleFrame * ab, + handleState * state, + const fpp_t frames, + const float freq, + const LoopMode loopMode = LoopOff + ); + + void visualize( + QPainter & p, + const QRect & dr, + const QRect & clip, + f_cnt_t fromFrame = 0, + f_cnt_t toFrame = 0 + ); + inline void visualize( + QPainter & p, + const QRect & dr, + f_cnt_t fromFrame = 0, + f_cnt_t toFrame = 0 + ) { - visualize(p, dr, dr, from_frame, to_frame); + visualize(p, dr, dr, fromFrame, toFrame); } inline const QString & audioFile() const @@ -148,22 +162,27 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject return m_loopEndFrame; } - void setLoopStartFrame( f_cnt_t _start ) + void setLoopStartFrame(f_cnt_t start) { - m_loopStartFrame = _start; + m_loopStartFrame = start; } - void setLoopEndFrame( f_cnt_t _end ) + void setLoopEndFrame(f_cnt_t end) { - m_loopEndFrame = _end; + m_loopEndFrame = end; } - void setAllPointFrames( f_cnt_t _start, f_cnt_t _end, f_cnt_t _loopstart, f_cnt_t _loopend ) + void setAllPointFrames( + f_cnt_t start, + f_cnt_t end, + f_cnt_t loopStart, + f_cnt_t loopEnd + ) { - m_startFrame = _start; - m_endFrame = _end; - m_loopStartFrame = _loopstart; - m_loopEndFrame = _loopend; + m_startFrame = start; + m_endFrame = end; + m_loopStartFrame = loopStart; + m_loopEndFrame = loopEnd; } inline f_cnt_t frames() const @@ -193,17 +212,17 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject int sampleLength() const { - return double( m_endFrame - m_startFrame ) / m_sampleRate * 1000; + return double(m_endFrame - m_startFrame) / m_sampleRate * 1000; } - inline void setFrequency( float _freq ) + inline void setFrequency(float freq) { - m_frequency = _freq; + m_frequency = freq; } - inline void setSampleRate( sample_rate_t _rate ) + inline void setSampleRate(sample_rate_t rate) { - m_sampleRate = _rate; + m_sampleRate = rate; } inline const sampleFrame * data() const @@ -215,30 +234,28 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject QString openAndSetAudioFile(); QString openAndSetWaveformFile(); - QString & toBase64( QString & _dst ) const; + QString & toBase64(QString & dst) const; // protect calls from the GUI to this function with dataReadLock() and // dataUnlock() - SampleBuffer * resample( const sample_rate_t _src_sr, - const sample_rate_t _dst_sr ); + SampleBuffer * resample(const sample_rate_t srcSR, const sample_rate_t dstSR); - void normalizeSampleRate( const sample_rate_t _src_sr, - bool _keep_settings = false ); + void normalizeSampleRate(const sample_rate_t srcSR, bool keepSettings = false); // protect calls from the GUI to this function with dataReadLock() and // dataUnlock(), out of loops for efficiency - inline sample_t userWaveSample( const float _sample ) const + inline sample_t userWaveSample(const float sample) const { f_cnt_t frames = m_frames; sampleFrame * data = m_data; - const float frame = _sample * frames; - f_cnt_t f1 = static_cast( frame ) % frames; - if( f1 < 0 ) + const float frame = sample * frames; + f_cnt_t f1 = static_cast(frame) % frames; + if (f1 < 0) { f1 += frames; } - return linearInterpolate( data[f1][0], data[ (f1 + 1) % frames ][0], fraction( frame ) ); + return linearInterpolate(data[f1][0], data[(f1 + 1) % frames][0], fraction(frame)); } void dataReadLock() @@ -253,33 +270,42 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject public slots: - void setAudioFile( const QString & _audio_file ); - void loadFromBase64( const QString & _data ); - void setStartFrame( const f_cnt_t _s ); - void setEndFrame( const f_cnt_t _e ); - void setAmplification( float _a ); - void setReversed( bool _on ); + void setAudioFile(const QString & audioFile); + void loadFromBase64(const QString & data); + void setStartFrame(const f_cnt_t s); + void setEndFrame(const f_cnt_t e); + void setAmplification(float a); + void setReversed(bool on); void sampleRateChanged(); private: static sample_rate_t mixerSampleRate(); - void update( bool _keep_settings = false ); + void update(bool keepSettings = false); void convertIntToFloat(int_sample_t * & ibuf, f_cnt_t frames, int channels); void directFloatWrite(sample_t * & fbuf, f_cnt_t frames, int channels); - f_cnt_t decodeSampleSF( QString _f, sample_t * & _buf, - ch_cnt_t & _channels, - sample_rate_t & _sample_rate ); + f_cnt_t decodeSampleSF( + QString fileName, + sample_t * & buf, + ch_cnt_t & channels, + sample_rate_t & samplerate + ); #ifdef LMMS_HAVE_OGGVORBIS - f_cnt_t decodeSampleOGGVorbis( QString _f, int_sample_t * & _buf, - ch_cnt_t & _channels, - sample_rate_t & _sample_rate ); + f_cnt_t decodeSampleOGGVorbis( + QString fileName, + int_sample_t * & buf, + ch_cnt_t & channels, + sample_rate_t & samplerate + ); #endif - f_cnt_t decodeSampleDS( QString _f, int_sample_t * & _buf, - ch_cnt_t & _channels, - sample_rate_t & _sample_rate ); + f_cnt_t decodeSampleDS( + QString fileName, + int_sample_t * & buf, + ch_cnt_t & channels, + sample_rate_t & samplerate + ); QString m_audioFile; sampleFrame * m_origData; @@ -296,13 +322,19 @@ public slots: float m_frequency; sample_rate_t m_sampleRate; - sampleFrame * getSampleFragment( f_cnt_t _index, f_cnt_t _frames, - LoopMode _loopmode, - sampleFrame * * _tmp, - bool * _backwards, f_cnt_t _loopstart, f_cnt_t _loopend, - f_cnt_t _end ) const; - f_cnt_t getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const; - f_cnt_t getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const; + sampleFrame * getSampleFragment( + f_cnt_t index, + f_cnt_t frames, + LoopMode loopMode, + sampleFrame * * tmp, + bool * backwards, + f_cnt_t loopStart, + f_cnt_t loopEnd, + f_cnt_t end + ) const; + + f_cnt_t getLoopedIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const; + f_cnt_t getPingPongIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const; signals: diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index d057b8a9112..102b7e1d36d 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -62,38 +62,37 @@ SampleBuffer::SampleBuffer() : - m_audioFile( "" ), - m_origData( NULL ), - m_origFrames( 0 ), - m_data( NULL ), - m_frames( 0 ), - m_startFrame( 0 ), - m_endFrame( 0 ), - m_loopStartFrame( 0 ), - m_loopEndFrame( 0 ), - m_amplification( 1.0f ), - m_reversed( false ), - m_frequency( BaseFreq ), - m_sampleRate( mixerSampleRate () ) + m_audioFile(""), + m_origData(nullptr), + m_origFrames(0), + m_data(nullptr), + m_frames(0), + m_startFrame(0), + m_endFrame(0), + m_loopStartFrame(0), + m_loopEndFrame(0), + m_amplification(1.0f), + m_reversed(false), + m_frequency(BaseFreq), + m_sampleRate(mixerSampleRate()) { - connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) ); + connect(Engine::mixer(), SIGNAL(sampleRateChanged()), this, SLOT(sampleRateChanged())); update(); } -SampleBuffer::SampleBuffer( const QString & _audio_file, - bool _is_base64_data ) +SampleBuffer::SampleBuffer(const QString & audioFile, bool isBase64Data) : SampleBuffer() { - if( _is_base64_data ) + if (isBase64Data) { - loadFromBase64( _audio_file ); + loadFromBase64(audioFile); } else { - m_audioFile = _audio_file; + m_audioFile = audioFile; update(); } } @@ -101,14 +100,14 @@ SampleBuffer::SampleBuffer( const QString & _audio_file, -SampleBuffer::SampleBuffer( const sampleFrame * _data, const f_cnt_t _frames ) +SampleBuffer::SampleBuffer(const sampleFrame * data, const f_cnt_t frames) : SampleBuffer() { - if( _frames > 0 ) + if (frames > 0) { - m_origData = MM_ALLOC( sampleFrame, _frames ); - memcpy( m_origData, _data, _frames * BYTES_PER_FRAME ); - m_origFrames = _frames; + m_origData = MM_ALLOC(sampleFrame, frames); + memcpy(m_origData, data, frames * BYTES_PER_FRAME); + m_origFrames = frames; update(); } } @@ -116,14 +115,14 @@ SampleBuffer::SampleBuffer( const sampleFrame * _data, const f_cnt_t _frames ) -SampleBuffer::SampleBuffer( const f_cnt_t _frames ) +SampleBuffer::SampleBuffer(const f_cnt_t frames) : SampleBuffer() { - if( _frames > 0 ) + if (frames > 0) { - m_origData = MM_ALLOC( sampleFrame, _frames ); - memset( m_origData, 0, _frames * BYTES_PER_FRAME ); - m_origFrames = _frames; + m_origData = MM_ALLOC(sampleFrame, frames); + memset(m_origData, 0, frames * BYTES_PER_FRAME); + m_origFrames = frames; update(); } } @@ -133,15 +132,15 @@ SampleBuffer::SampleBuffer( const f_cnt_t _frames ) SampleBuffer::~SampleBuffer() { - MM_FREE( m_origData ); - MM_FREE( m_data ); + MM_FREE(m_origData); + MM_FREE(m_data); } void SampleBuffer::sampleRateChanged() { - update( true ); + update(true); } sample_rate_t SampleBuffer::mixerSampleRate() @@ -150,14 +149,14 @@ sample_rate_t SampleBuffer::mixerSampleRate() } -void SampleBuffer::update( bool _keep_settings ) +void SampleBuffer::update(bool keepSettings) { - const bool lock = ( m_data != NULL ); - if( lock ) + const bool lock = (m_data != nullptr); + if (lock) { Engine::mixer()->requestChangeInModel(); m_varLock.lockForWrite(); - MM_FREE( m_data ); + MM_FREE(m_data); } // File size and sample length limits @@ -165,30 +164,30 @@ void SampleBuffer::update( bool _keep_settings ) const int sampleLengthMax = 90; // Minutes bool fileLoadError = false; - if( m_audioFile.isEmpty() && m_origData != NULL && m_origFrames > 0 ) + if (m_audioFile.isEmpty() && m_origData != nullptr && m_origFrames > 0) { // TODO: reverse- and amplification-property is not covered // by following code... - m_data = MM_ALLOC( sampleFrame, m_origFrames ); - memcpy( m_data, m_origData, m_origFrames * BYTES_PER_FRAME ); - if( _keep_settings == false ) + m_data = MM_ALLOC(sampleFrame, m_origFrames); + memcpy(m_data, m_origData, m_origFrames * BYTES_PER_FRAME); + if (keepSettings == false) { m_frames = m_origFrames; m_loopStartFrame = m_startFrame = 0; m_loopEndFrame = m_endFrame = m_frames; } } - else if( !m_audioFile.isEmpty() ) + else if (!m_audioFile.isEmpty()) { - QString file = PathUtil::toAbsolute( m_audioFile ); - int_sample_t * buf = NULL; - sample_t * fbuf = NULL; + QString file = PathUtil::toAbsolute(m_audioFile); + int_sample_t * buf = nullptr; + sample_t * fbuf = nullptr; ch_cnt_t channels = DEFAULT_CHANNELS; sample_rate_t samplerate = mixerSampleRate(); m_frames = 0; - const QFileInfo fileInfo( file ); - if( fileInfo.size() > fileSizeMax * 1024 * 1024 ) + const QFileInfo fileInfo(file); + if (fileInfo.size() > fileSizeMax * 1024 * 1024) { fileLoadError = true; } @@ -196,79 +195,76 @@ void SampleBuffer::update( bool _keep_settings ) { // Use QFile to handle unicode file names on Windows QFile f(file); - SNDFILE * snd_file; - SF_INFO sf_info; - sf_info.format = 0; - if (f.open(QIODevice::ReadOnly) && (snd_file = sf_open_fd(f.handle(), SFM_READ, &sf_info, false))) + SNDFILE * sndFile; + SF_INFO sfInfo; + sfInfo.format = 0; + if (f.open(QIODevice::ReadOnly) && (sndFile = sf_open_fd(f.handle(), SFM_READ, &sfInfo, false))) { - f_cnt_t frames = sf_info.frames; - int rate = sf_info.samplerate; - if( frames / rate > sampleLengthMax * 60 ) + f_cnt_t frames = sfInfo.frames; + int rate = sfInfo.samplerate; + if (frames / rate > sampleLengthMax * 60) { fileLoadError = true; } - sf_close( snd_file ); + sf_close(sndFile); } f.close(); } - if( !fileLoadError ) + if (!fileLoadError) { #ifdef LMMS_HAVE_OGGVORBIS // workaround for a bug in libsndfile or our libsndfile decoder // causing some OGG files to be distorted -> try with OGG Vorbis // decoder first if filename extension matches "ogg" - if( m_frames == 0 && fileInfo.suffix() == "ogg" ) + if (m_frames == 0 && fileInfo.suffix() == "ogg") { - m_frames = decodeSampleOGGVorbis( file, buf, channels, samplerate ); + m_frames = decodeSampleOGGVorbis(file, buf, channels, samplerate); } #endif - if( m_frames == 0 ) + if (m_frames == 0) { - m_frames = decodeSampleSF( file, fbuf, channels, - samplerate ); + m_frames = decodeSampleSF(file, fbuf, channels, samplerate); } #ifdef LMMS_HAVE_OGGVORBIS - if( m_frames == 0 ) + if (m_frames == 0) { - m_frames = decodeSampleOGGVorbis( file, buf, channels, - samplerate ); + m_frames = decodeSampleOGGVorbis(file, buf, channels, samplerate); } #endif - if( m_frames == 0 ) + if (m_frames == 0) { - m_frames = decodeSampleDS( file, buf, channels, - samplerate ); + m_frames = decodeSampleDS(file, buf, channels, samplerate); } } - if ( m_frames == 0 || fileLoadError ) // if still no frames, bail + if (m_frames == 0 || fileLoadError) // if still no frames, bail { // sample couldn't be decoded, create buffer containing // one sample-frame - m_data = MM_ALLOC( sampleFrame, 1 ); - memset( m_data, 0, sizeof( *m_data ) ); + m_data = MM_ALLOC(sampleFrame, 1); + memset(m_data, 0, sizeof(*m_data)); m_frames = 1; m_loopStartFrame = m_startFrame = 0; m_loopEndFrame = m_endFrame = 1; } else // otherwise normalize sample rate { - normalizeSampleRate( samplerate, _keep_settings ); + normalizeSampleRate(samplerate, keepSettings); } } else { // neither an audio-file nor a buffer to copy from, so create // buffer containing one sample-frame - m_data = MM_ALLOC( sampleFrame, 1 ); - memset( m_data, 0, sizeof( *m_data ) ); + m_data = MM_ALLOC(sampleFrame, 1); + memset(m_data, 0, sizeof(*m_data)); m_frames = 1; m_loopStartFrame = m_startFrame = 0; m_loopEndFrame = m_endFrame = 1; } - if( lock ) + if (lock) { m_varLock.unlock(); Engine::mixer()->doneChangeInModel(); @@ -276,20 +272,20 @@ void SampleBuffer::update( bool _keep_settings ) emit sampleUpdated(); - if( fileLoadError ) + if (fileLoadError) { - QString title = tr( "Fail to open file" ); - QString message = tr( "Audio files are limited to %1 MB " + QString title = tr("Fail to open file"); + QString message = tr("Audio files are limited to %1 MB " "in size and %2 minutes of playing time" - ).arg( fileSizeMax ).arg( sampleLengthMax ); - if( gui ) + ).arg(fileSizeMax).arg(sampleLengthMax); + if (gui) { - QMessageBox::information( NULL, - title, message, QMessageBox::Ok ); + QMessageBox::information(nullptr, + title, message, QMessageBox::Ok); } else { - fprintf( stderr, "%s\n", message.toUtf8().constData() ); + fprintf(stderr, "%s\n", message.toUtf8().constData()); } } } @@ -298,7 +294,8 @@ void SampleBuffer::update( bool _keep_settings ) void SampleBuffer::convertIntToFloat( int_sample_t * & ibuf, f_cnt_t frames, - int channels) + int channels +) { // following code transforms int-samples into float-samples and does amplifying & reversing const float fac = 1 / OUTPUT_SAMPLE_MULTIPLIER; @@ -321,7 +318,8 @@ void SampleBuffer::convertIntToFloat( void SampleBuffer::directFloatWrite( sample_t * & fbuf, f_cnt_t frames, - int channels) + int channels +) { m_data = MM_ALLOC(sampleFrame, frames); @@ -341,39 +339,36 @@ void SampleBuffer::directFloatWrite( } -void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr, - bool _keep_settings ) +void SampleBuffer::normalizeSampleRate(const sample_rate_t srcSR, bool keepSettings) { - const sample_rate_t old_rate = m_sampleRate; + const sample_rate_t oldRate = m_sampleRate; // do samplerate-conversion to our default-samplerate - if( _src_sr != mixerSampleRate() ) + if (srcSR != mixerSampleRate()) { - SampleBuffer * resampled = resample( _src_sr, - mixerSampleRate() ); + SampleBuffer * resampled = resample(srcSR, mixerSampleRate()); m_sampleRate = mixerSampleRate(); - MM_FREE( m_data ); + MM_FREE(m_data); m_frames = resampled->frames(); - m_data = MM_ALLOC( sampleFrame, m_frames ); - memcpy( m_data, resampled->data(), m_frames * - sizeof( sampleFrame ) ); + m_data = MM_ALLOC(sampleFrame, m_frames); + memcpy(m_data, resampled->data(), m_frames * sizeof(sampleFrame)); delete resampled; } - if( _keep_settings == false ) + if (keepSettings == false) { // update frame-variables m_loopStartFrame = m_startFrame = 0; m_loopEndFrame = m_endFrame = m_frames; } - else if( old_rate != mixerSampleRate() ) + else if (oldRate != mixerSampleRate()) { - auto old_rate_to_new_rate_ratio = static_cast(mixerSampleRate()) / old_rate; + auto oldRateToNewRateRatio = static_cast(mixerSampleRate()) / oldRate; - m_startFrame = qBound(0, f_cnt_t(m_startFrame*old_rate_to_new_rate_ratio), m_frames); - m_endFrame = qBound(m_startFrame, f_cnt_t(m_endFrame*old_rate_to_new_rate_ratio), m_frames); - m_loopStartFrame = qBound(0, f_cnt_t(m_loopStartFrame*old_rate_to_new_rate_ratio), m_frames); - m_loopEndFrame = qBound(m_loopStartFrame, f_cnt_t(m_loopEndFrame*old_rate_to_new_rate_ratio), m_frames); + m_startFrame = qBound(0, f_cnt_t(m_startFrame * oldRateToNewRateRatio), m_frames); + m_endFrame = qBound(m_startFrame, f_cnt_t(m_endFrame * oldRateToNewRateRatio), m_frames); + m_loopStartFrame = qBound(0, f_cnt_t(m_loopStartFrame * oldRateToNewRateRatio), m_frames); + m_loopEndFrame = qBound(m_loopStartFrame, f_cnt_t(m_loopEndFrame * oldRateToNewRateRatio), m_frames); m_sampleRate = mixerSampleRate(); } } @@ -381,53 +376,55 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr, -f_cnt_t SampleBuffer::decodeSampleSF(QString _f, - sample_t * & _buf, - ch_cnt_t & _channels, - sample_rate_t & _samplerate ) +f_cnt_t SampleBuffer::decodeSampleSF( + QString fileName, + sample_t * & buf, + ch_cnt_t & channels, + sample_rate_t & samplerate +) { - SNDFILE * snd_file; - SF_INFO sf_info; - sf_info.format = 0; + SNDFILE * sndFile; + SF_INFO sfInfo; + sfInfo.format = 0; f_cnt_t frames = 0; sf_count_t sfFramesRead; // Use QFile to handle unicode file names on Windows - QFile f(_f); - if (f.open(QIODevice::ReadOnly) && (snd_file = sf_open_fd(f.handle(), SFM_READ, &sf_info, false))) + QFile f(fileName); + if (f.open(QIODevice::ReadOnly) && (sndFile = sf_open_fd(f.handle(), SFM_READ, &sfInfo, false))) { - frames = sf_info.frames; + frames = sfInfo.frames; - _buf = new sample_t[sf_info.channels * frames]; - sfFramesRead = sf_read_float(snd_file, _buf, sf_info.channels * frames); + buf = new sample_t[sfInfo.channels * frames]; + sfFramesRead = sf_read_float(sndFile, buf, sfInfo.channels * frames); - if (sfFramesRead < sf_info.channels * frames) + if (sfFramesRead < sfInfo.channels * frames) { #ifdef DEBUG_LMMS - qDebug( "SampleBuffer::decodeSampleSF(): could not read" - " sample %s: %s", _f, sf_strerror( NULL ) ); + qDebug("SampleBuffer::decodeSampleSF(): could not read" + " sample %s: %s", fileName, sf_strerror(nullptr)); #endif } - _channels = sf_info.channels; - _samplerate = sf_info.samplerate; + channels = sfInfo.channels; + samplerate = sfInfo.samplerate; - sf_close( snd_file ); + sf_close(sndFile); } else { #ifdef DEBUG_LMMS - qDebug( "SampleBuffer::decodeSampleSF(): could not load " - "sample %s: %s", _f, sf_strerror( NULL ) ); + qDebug("SampleBuffer::decodeSampleSF(): could not load " + "sample %s: %s", fileName, sf_strerror(nullptr)); #endif } f.close(); //write down either directly or convert i->f depending on file type - if ( frames > 0 && _buf != NULL ) + if (frames > 0 && buf != nullptr) { - directFloatWrite ( _buf, frames, _channels); + directFloatWrite(buf, frames, channels); } return frames; @@ -440,30 +437,29 @@ f_cnt_t SampleBuffer::decodeSampleSF(QString _f, // callback-functions for reading ogg-file -size_t qfileReadCallback( void * _ptr, size_t _size, size_t _n, void * _udata ) +size_t qfileReadCallback(void * ptr, size_t size, size_t n, void * udata ) { - return static_cast( _udata )->read( (char*) _ptr, - _size * _n ); + return static_cast(udata)->read((char*) ptr, size * n); } -int qfileSeekCallback( void * _udata, ogg_int64_t _offset, int _whence ) +int qfileSeekCallback(void * udata, ogg_int64_t offset, int whence) { - QFile * f = static_cast( _udata ); + QFile * f = static_cast(udata); - if( _whence == SEEK_CUR ) + if (whence == SEEK_CUR) { - f->seek( f->pos() + _offset ); + f->seek(f->pos() + offset); } - else if( _whence == SEEK_END ) + else if (whence == SEEK_END) { - f->seek( f->size() + _offset ); + f->seek(f->size() + offset); } else { - f->seek( _offset ); + f->seek(offset); } return 0; } @@ -471,27 +467,29 @@ int qfileSeekCallback( void * _udata, ogg_int64_t _offset, int _whence ) -int qfileCloseCallback( void * _udata ) +int qfileCloseCallback(void * udata) { - delete static_cast( _udata ); + delete static_cast(udata); return 0; } -long qfileTellCallback( void * _udata ) +long qfileTellCallback(void * udata) { - return static_cast( _udata )->pos(); + return static_cast(udata)->pos(); } -f_cnt_t SampleBuffer::decodeSampleOGGVorbis( QString _f, - int_sample_t * & _buf, - ch_cnt_t & _channels, - sample_rate_t & _samplerate ) +f_cnt_t SampleBuffer::decodeSampleOGGVorbis( + QString fileName, + int_sample_t * & buf, + ch_cnt_t & channels, + sample_rate_t & samplerate +) { static ov_callbacks callbacks = { @@ -505,76 +503,80 @@ f_cnt_t SampleBuffer::decodeSampleOGGVorbis( QString _f, f_cnt_t frames = 0; - QFile * f = new QFile( _f ); - if( f->open( QFile::ReadOnly ) == false ) + QFile * f = new QFile(fileName); + if (f->open(QFile::ReadOnly) == false) { delete f; return 0; } - int err = ov_open_callbacks( f, &vf, NULL, 0, callbacks ); + int err = ov_open_callbacks(f, &vf, nullptr, 0, callbacks); - if( err < 0 ) + if (err < 0) { - switch( err ) + switch (err) { case OV_EREAD: - printf( "SampleBuffer::decodeSampleOGGVorbis():" - " media read error\n" ); + printf("SampleBuffer::decodeSampleOGGVorbis():" + " media read error\n"); break; case OV_ENOTVORBIS: -/* printf( "SampleBuffer::decodeSampleOGGVorbis():" - " not an Ogg Vorbis file\n" );*/ + printf("SampleBuffer::decodeSampleOGGVorbis():" + " not an Ogg Vorbis file\n"); break; case OV_EVERSION: - printf( "SampleBuffer::decodeSampleOGGVorbis():" - " vorbis version mismatch\n" ); + printf("SampleBuffer::decodeSampleOGGVorbis():" + " vorbis version mismatch\n"); break; case OV_EBADHEADER: - printf( "SampleBuffer::decodeSampleOGGVorbis():" - " invalid Vorbis bitstream header\n" ); + printf("SampleBuffer::decodeSampleOGGVorbis():" + " invalid Vorbis bitstream header\n"); break; case OV_EFAULT: - printf( "SampleBuffer::decodeSampleOgg(): " - "internal logic fault\n" ); + printf("SampleBuffer::decodeSampleOgg(): " + "internal logic fault\n"); break; } delete f; return 0; } - ov_pcm_seek( &vf, 0 ); + ov_pcm_seek(&vf, 0); - _channels = ov_info( &vf, -1 )->channels; - _samplerate = ov_info( &vf, -1 )->rate; + channels = ov_info(&vf, -1)->channels; + samplerate = ov_info(&vf, -1)->rate; - ogg_int64_t total = ov_pcm_total( &vf, -1 ); + ogg_int64_t total = ov_pcm_total(&vf, -1); - _buf = new int_sample_t[total * _channels]; + buf = new int_sample_t[total * channels]; int bitstream = 0; - long bytes_read = 0; + long bytesRead = 0; do { - bytes_read = ov_read( &vf, (char *) &_buf[frames * _channels], - ( total - frames ) * _channels * - BYTES_PER_INT_SAMPLE, - isLittleEndian() ? 0 : 1, - BYTES_PER_INT_SAMPLE, 1, &bitstream ); - if( bytes_read < 0 ) + bytesRead = ov_read(&vf, + (char *) &buf[frames * channels], + (total - frames) * channels * BYTES_PER_INT_SAMPLE, + isLittleEndian() ? 0 : 1, + BYTES_PER_INT_SAMPLE, + 1, + &bitstream + ); + + if (bytesRead < 0) { break; } - frames += bytes_read / ( _channels * BYTES_PER_INT_SAMPLE ); + frames += bytesRead / (channels * BYTES_PER_INT_SAMPLE); } - while( bytes_read != 0 && bitstream == 0 ); + while (bytesRead != 0 && bitstream == 0); - ov_clear( &vf ); - // if buffer isn't empty, convert it to float and write it down + ov_clear(&vf); - if ( frames > 0 && _buf != NULL ) + // if buffer isn't empty, convert it to float and write it down + if (frames > 0 && buf != nullptr) { - convertIntToFloat ( _buf, frames, _channels); + convertIntToFloat(buf, frames, channels); } return frames; @@ -584,17 +586,19 @@ f_cnt_t SampleBuffer::decodeSampleOGGVorbis( QString _f, -f_cnt_t SampleBuffer::decodeSampleDS( QString _f, - int_sample_t * & _buf, - ch_cnt_t & _channels, - sample_rate_t & _samplerate ) +f_cnt_t SampleBuffer::decodeSampleDS( + QString fileName, + int_sample_t * & buf, + ch_cnt_t & channels, + sample_rate_t & samplerate +) { DrumSynth ds; - f_cnt_t frames = ds.GetDSFileSamples( _f, _buf, _channels, _samplerate ); + f_cnt_t frames = ds.GetDSFileSamples(fileName, buf, channels, samplerate); - if ( frames > 0 && _buf != NULL ) + if (frames > 0 && buf != nullptr) { - convertIntToFloat ( _buf, frames, _channels); + convertIntToFloat(buf, frames, channels); } return frames; @@ -604,112 +608,114 @@ f_cnt_t SampleBuffer::decodeSampleDS( QString _f, -bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, - const fpp_t _frames, - const float _freq, - const LoopMode _loopmode ) +bool SampleBuffer::play( + sampleFrame * ab, + handleState * state, + const fpp_t frames, + const float freq, + const LoopMode loopMode +) { f_cnt_t startFrame = m_startFrame; f_cnt_t endFrame = m_endFrame; f_cnt_t loopStartFrame = m_loopStartFrame; f_cnt_t loopEndFrame = m_loopEndFrame; - if( endFrame == 0 || _frames == 0 ) + if (endFrame == 0 || frames == 0) { return false; } // variable for determining if we should currently be playing backwards in a ping-pong loop - bool is_backwards = _state->isBackwards(); + bool isBackwards = state->isBackwards(); - const double freq_factor = (double) _freq / (double) m_frequency * + const double freqFactor = (double) freq / (double) m_frequency * m_sampleRate / Engine::mixer()->processingSampleRate(); // calculate how many frames we have in requested pitch - const f_cnt_t total_frames_for_current_pitch = static_cast( ( - endFrame - startFrame ) / - freq_factor ); + const f_cnt_t totalFramesForCurrentPitch = static_cast( + (endFrame - startFrame) / freqFactor + ); - if( total_frames_for_current_pitch == 0 ) + if (totalFramesForCurrentPitch == 0) { return false; } // this holds the index of the first frame to play - f_cnt_t play_frame = qMax(_state->m_frameIndex, startFrame); + f_cnt_t playFrame = qMax(state->m_frameIndex, startFrame); - if( _loopmode == LoopOff ) + if (loopMode == LoopOff) { - if( play_frame >= endFrame || ( endFrame - play_frame ) / freq_factor == 0 ) + if (playFrame >= endFrame || (endFrame - playFrame) / freqFactor == 0) { // the sample is done being played return false; } } - else if( _loopmode == LoopOn ) + else if (loopMode == LoopOn) { - play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame); } else { - play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); + playFrame = getPingPongIndex(playFrame, loopStartFrame, loopEndFrame); } - f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + MARGIN[ _state->interpolationMode() ]; + f_cnt_t fragmentSize = (f_cnt_t)(frames * freqFactor) + MARGIN[state->interpolationMode()]; - sampleFrame * tmp = NULL; + sampleFrame * tmp = nullptr; // check whether we have to change pitch... - if( freq_factor != 1.0 || _state->m_varyingPitch ) + if (freqFactor != 1.0 || state->m_varyingPitch) { - SRC_DATA src_data; + SRC_DATA srcData; // Generate output - src_data.data_in = - getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards, - loopStartFrame, loopEndFrame, endFrame )->data (); - src_data.data_out = _ab->data (); - src_data.input_frames = fragment_size; - src_data.output_frames = _frames; - src_data.src_ratio = 1.0 / freq_factor; - src_data.end_of_input = 0; - int error = src_process( _state->m_resamplingData, - &src_data ); - if( error ) + srcData.data_in = + getSampleFragment(playFrame, fragmentSize, loopMode, &tmp, &isBackwards, + loopStartFrame, loopEndFrame, endFrame )->data(); + srcData.data_out = ab->data(); + srcData.input_frames = fragmentSize; + srcData.output_frames = frames; + srcData.src_ratio = 1.0 / freqFactor; + srcData.end_of_input = 0; + int error = src_process(state->m_resamplingData, &srcData); + if (error) { - printf( "SampleBuffer: error while resampling: %s\n", - src_strerror( error ) ); + printf("SampleBuffer: error while resampling: %s\n", + src_strerror(error)); } - if( src_data.output_frames_gen > _frames ) + if (srcData.output_frames_gen > frames) { - printf( "SampleBuffer: not enough frames: %ld / %d\n", - src_data.output_frames_gen, _frames ); + printf("SampleBuffer: not enough frames: %ld / %d\n", + srcData.output_frames_gen, frames); } // Advance - switch( _loopmode ) + switch (loopMode) { case LoopOff: - play_frame += src_data.input_frames_used; + playFrame += srcData.input_frames_used; break; case LoopOn: - play_frame += src_data.input_frames_used; - play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + playFrame += srcData.input_frames_used; + playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame); break; case LoopPingPong: { - f_cnt_t left = src_data.input_frames_used; - if( _state->isBackwards() ) + f_cnt_t left = srcData.input_frames_used; + if (state->isBackwards()) { - play_frame -= src_data.input_frames_used; - if( play_frame < loopStartFrame ) + playFrame -= srcData.input_frames_used; + if (playFrame < loopStartFrame) { - left -= ( loopStartFrame - play_frame ); - play_frame = loopStartFrame; + left -= (loopStartFrame - playFrame); + playFrame = loopStartFrame; } else left = 0; } - play_frame += left; - play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); + playFrame += left; + playFrame = getPingPongIndex(playFrame, loopStartFrame, loopEndFrame); break; } } @@ -720,52 +726,52 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, // as is into pitched-copy-buffer // Generate output - memcpy( _ab, - getSampleFragment( play_frame, _frames, _loopmode, &tmp, &is_backwards, - loopStartFrame, loopEndFrame, endFrame ), - _frames * BYTES_PER_FRAME ); + memcpy(ab, + getSampleFragment(playFrame, frames, loopMode, &tmp, &isBackwards, + loopStartFrame, loopEndFrame, endFrame), + frames * BYTES_PER_FRAME); // Advance - switch( _loopmode ) + switch (loopMode) { case LoopOff: - play_frame += _frames; + playFrame += frames; break; case LoopOn: - play_frame += _frames; - play_frame = getLoopedIndex( play_frame, loopStartFrame, loopEndFrame ); + playFrame += frames; + playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame); break; case LoopPingPong: { - f_cnt_t left = _frames; - if( _state->isBackwards() ) + f_cnt_t left = frames; + if (state->isBackwards()) { - play_frame -= _frames; - if( play_frame < loopStartFrame ) + playFrame -= frames; + if (playFrame < loopStartFrame) { - left -= ( loopStartFrame - play_frame ); - play_frame = loopStartFrame; + left -= (loopStartFrame - playFrame); + playFrame = loopStartFrame; } else left = 0; } - play_frame += left; - play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame ); + playFrame += left; + playFrame = getPingPongIndex(playFrame, loopStartFrame, loopEndFrame); break; } } } - if( tmp != NULL ) + if (tmp != nullptr) { - MM_FREE( tmp ); + MM_FREE(tmp); } - _state->setBackwards( is_backwards ); - _state->setFrameIndex( play_frame ); + state->setBackwards(isBackwards); + state->setFrameIndex(playFrame); - for( fpp_t i = 0; i < _frames; ++i ) + for (fpp_t i = 0; i < frames; ++i) { - _ab[i][0] *= m_amplification; - _ab[i][1] *= m_amplification; + ab[i][0] *= m_amplification; + ab[i][1] *= m_amplification; } return true; @@ -774,136 +780,141 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, -sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _index, - f_cnt_t _frames, LoopMode _loopmode, sampleFrame * * _tmp, bool * _backwards, - f_cnt_t _loopstart, f_cnt_t _loopend, f_cnt_t _end ) const +sampleFrame * SampleBuffer::getSampleFragment( + f_cnt_t index, + f_cnt_t frames, + LoopMode loopMode, + sampleFrame * * tmp, + bool * backwards, + f_cnt_t loopStart, + f_cnt_t loopEnd, + f_cnt_t end +) const { - if( _loopmode == LoopOff ) + if (loopMode == LoopOff) { - if( _index + _frames <= _end ) + if (index + frames <= end) { - return m_data + _index; + return m_data + index; } } - else if( _loopmode == LoopOn ) + else if (loopMode == LoopOn) { - if( _index + _frames <= _loopend ) + if (index + frames <= loopEnd) { - return m_data + _index; + return m_data + index; } } else { - if( ! *_backwards && _index + _frames < _loopend ) + if (!*backwards && index + frames < loopEnd) { - return m_data + _index; + return m_data + index; } } - *_tmp = MM_ALLOC( sampleFrame, _frames ); + *tmp = MM_ALLOC(sampleFrame, frames); - if( _loopmode == LoopOff ) + if (loopMode == LoopOff) { - f_cnt_t available = _end - _index; - memcpy( *_tmp, m_data + _index, available * BYTES_PER_FRAME ); - memset( *_tmp + available, 0, ( _frames - available ) * - BYTES_PER_FRAME ); + f_cnt_t available = end - index; + memcpy(*tmp, m_data + index, available * BYTES_PER_FRAME); + memset(*tmp + available, 0, (frames - available) * BYTES_PER_FRAME); } - else if( _loopmode == LoopOn ) + else if (loopMode == LoopOn) { - f_cnt_t copied = qMin( _frames, _loopend - _index ); - memcpy( *_tmp, m_data + _index, copied * BYTES_PER_FRAME ); - f_cnt_t loop_frames = _loopend - _loopstart; - while( copied < _frames ) + f_cnt_t copied = qMin(frames, loopEnd - index); + memcpy(*tmp, m_data + index, copied * BYTES_PER_FRAME); + f_cnt_t loopFrames = loopEnd - loopStart; + while (copied < frames) { - f_cnt_t todo = qMin( _frames - copied, loop_frames ); - memcpy( *_tmp + copied, m_data + _loopstart, todo * BYTES_PER_FRAME ); + f_cnt_t todo = qMin(frames - copied, loopFrames); + memcpy(*tmp + copied, m_data + loopStart, todo * BYTES_PER_FRAME); copied += todo; } } else { - f_cnt_t pos = _index; - bool backwards = pos < _loopstart + f_cnt_t pos = index; + bool currentBackwards = pos < loopStart ? false - : *_backwards; + : *backwards; f_cnt_t copied = 0; - if( backwards ) + if (currentBackwards) { - copied = qMin( _frames, pos - _loopstart ); - for( int i=0; i < copied; i++ ) + copied = qMin(frames, pos - loopStart); + for (int i = 0; i < copied; i++) { - (*_tmp)[i][0] = m_data[ pos - i ][0]; - (*_tmp)[i][1] = m_data[ pos - i ][1]; + (*tmp)[i][0] = m_data[pos - i][0]; + (*tmp)[i][1] = m_data[pos - i][1]; } pos -= copied; - if( pos == _loopstart ) backwards = false; + if (pos == loopStart) { currentBackwards = false; } } else { - copied = qMin( _frames, _loopend - pos ); - memcpy( *_tmp, m_data + pos, copied * BYTES_PER_FRAME ); + copied = qMin(frames, loopEnd - pos); + memcpy(*tmp, m_data + pos, copied * BYTES_PER_FRAME); pos += copied; - if( pos == _loopend ) backwards = true; + if (pos == loopEnd) { currentBackwards = true; } } - while( copied < _frames ) + while (copied < frames) { - if( backwards ) + if (currentBackwards) { - f_cnt_t todo = qMin( _frames - copied, pos - _loopstart ); - for ( int i=0; i < todo; i++ ) + f_cnt_t todo = qMin(frames - copied, pos - loopStart); + for (int i = 0; i < todo; i++) { - (*_tmp)[ copied + i ][0] = m_data[ pos - i ][0]; - (*_tmp)[ copied + i ][1] = m_data[ pos - i ][1]; + (*tmp)[copied + i][0] = m_data[pos - i][0]; + (*tmp)[copied + i][1] = m_data[pos - i][1]; } pos -= todo; copied += todo; - if( pos <= _loopstart ) backwards = false; + if (pos <= loopStart) { currentBackwards = false; } } else { - f_cnt_t todo = qMin( _frames - copied, _loopend - pos ); - memcpy( *_tmp + copied, m_data + pos, todo * BYTES_PER_FRAME ); + f_cnt_t todo = qMin(frames - copied, loopEnd - pos); + memcpy(*tmp + copied, m_data + pos, todo * BYTES_PER_FRAME); pos += todo; copied += todo; - if( pos >= _loopend ) backwards = true; + if (pos >= loopEnd) { currentBackwards = true; } } } - *_backwards = backwards; + *backwards = currentBackwards; } - return *_tmp; + return *tmp; } -f_cnt_t SampleBuffer::getLoopedIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const +f_cnt_t SampleBuffer::getLoopedIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const { - if( _index < _endf ) + if (index < endf) { - return _index; + return index; } - return _startf + ( _index - _startf ) - % ( _endf - _startf ); + return startf + (index - startf) % (endf - startf); } -f_cnt_t SampleBuffer::getPingPongIndex( f_cnt_t _index, f_cnt_t _startf, f_cnt_t _endf ) const +f_cnt_t SampleBuffer::getPingPongIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const { - if( _index < _endf ) + if (index < endf) { - return _index; + return index; } - const f_cnt_t looplen = _endf - _startf; - const f_cnt_t looppos = ( _index - _endf ) % ( looplen*2 ); + const f_cnt_t loopLen = endf - startf; + const f_cnt_t loopPos = (index - endf) % (loopLen * 2); - return ( looppos < looplen ) - ? _endf - looppos - : _startf + ( looppos - looplen ); + return (loopPos < loopLen) + ? endf - loopPos + : startf + (loopPos - loopLen); } @@ -911,41 +922,43 @@ void SampleBuffer::visualize( QPainter & p, const QRect & dr, const QRect & clip, - f_cnt_t from_frame, - f_cnt_t to_frame) + f_cnt_t fromFrame, + f_cnt_t toFrame +) { if (m_frames == 0) { return; } - const bool focus_on_range = to_frame <= m_frames && 0 <= from_frame && from_frame < to_frame; - //p.setClipRect( clip ); + const bool focusOnRange = toFrame <= m_frames && 0 <= fromFrame && fromFrame < toFrame; + //p.setClipRect(clip); const int w = dr.width(); const int h = dr.height(); const int yb = h / 2 + dr.y(); - const float y_space = h*0.5f; - const int nb_frames = focus_on_range ? to_frame - from_frame : m_frames; + const float ySpace = h * 0.5f; + const int nbFrames = focusOnRange ? toFrame - fromFrame : m_frames; - const int fpp = qBound(1, nb_frames / w, 20); - QPointF * l = new QPointF[nb_frames / fpp + 1]; - QPointF * r = new QPointF[nb_frames / fpp + 1]; + const int fpp = qBound(1, nbFrames / w, 20); + QPointF * l = new QPointF[nbFrames / fpp + 1]; + QPointF * r = new QPointF[nbFrames / fpp + 1]; int n = 0; const int xb = dr.x(); - const int first = focus_on_range ? from_frame : 0; - const int last = focus_on_range ? to_frame - 1 : m_frames - 1; + const int first = focusOnRange ? fromFrame : 0; + const int last = focusOnRange ? toFrame - 1 : m_frames - 1; for (int frame = first; frame <= last; frame += fpp) { - auto x = xb + ((frame - first) * double(w) / nb_frames); + auto x = xb + ((frame - first) * double(w) / nbFrames); // Partial Y calculation - auto py = y_space * m_amplification; + auto py = ySpace * m_amplification; l[n] = QPointF(x, (yb - (m_data[frame][0] * py))); r[n] = QPointF(x, (yb - (m_data[frame][1] * py))); ++n; } p.setRenderHint(QPainter::Antialiasing); - p.drawPolyline(l, nb_frames / fpp); - p.drawPolyline(r, nb_frames / fpp); + p.drawPolyline(l, nbFrames / fpp); + p.drawPolyline(r, nbFrames / fpp); + delete[] l; delete[] r; } @@ -955,62 +968,62 @@ void SampleBuffer::visualize( QString SampleBuffer::openAudioFile() const { - FileDialog ofd( NULL, tr( "Open audio file" ) ); + FileDialog ofd(nullptr, tr("Open audio file")); QString dir; - if( !m_audioFile.isEmpty() ) + if (!m_audioFile.isEmpty()) { QString f = m_audioFile; - if( QFileInfo( f ).isRelative() ) + if (QFileInfo(f).isRelative()) { f = ConfigManager::inst()->userSamplesDir() + f; - if( QFileInfo( f ).exists() == false ) + if (QFileInfo(f).exists() == false) { f = ConfigManager::inst()->factorySamplesDir() + m_audioFile; } } - dir = QFileInfo( f ).absolutePath(); + dir = QFileInfo(f).absolutePath(); } else { dir = ConfigManager::inst()->userSamplesDir(); } // change dir to position of previously opened file - ofd.setDirectory( dir ); - ofd.setFileMode( FileDialog::ExistingFiles ); + ofd.setDirectory(dir); + ofd.setFileMode(FileDialog::ExistingFiles); // set filters QStringList types; - types << tr( "All Audio-Files (*.wav *.ogg *.ds *.flac *.spx *.voc " - "*.aif *.aiff *.au *.raw)" ) - << tr( "Wave-Files (*.wav)" ) - << tr( "OGG-Files (*.ogg)" ) - << tr( "DrumSynth-Files (*.ds)" ) - << tr( "FLAC-Files (*.flac)" ) - << tr( "SPEEX-Files (*.spx)" ) - //<< tr( "MP3-Files (*.mp3)" ) - //<< tr( "MIDI-Files (*.mid)" ) - << tr( "VOC-Files (*.voc)" ) - << tr( "AIFF-Files (*.aif *.aiff)" ) - << tr( "AU-Files (*.au)" ) - << tr( "RAW-Files (*.raw)" ) - //<< tr( "MOD-Files (*.mod)" ) + types << tr("All Audio-Files (*.wav *.ogg *.ds *.flac *.spx *.voc " + "*.aif *.aiff *.au *.raw)") + << tr("Wave-Files (*.wav)") + << tr("OGG-Files (*.ogg)") + << tr("DrumSynth-Files (*.ds)") + << tr("FLAC-Files (*.flac)") + << tr("SPEEX-Files (*.spx)") + //<< tr("MP3-Files (*.mp3)") + //<< tr("MIDI-Files (*.mid)") + << tr("VOC-Files (*.voc)") + << tr("AIFF-Files (*.aif *.aiff)") + << tr("AU-Files (*.au)") + << tr("RAW-Files (*.raw)") + //<< tr("MOD-Files (*.mod)") ; - ofd.setNameFilters( types ); - if( !m_audioFile.isEmpty() ) + ofd.setNameFilters(types); + if (!m_audioFile.isEmpty()) { // select previously opened file - ofd.selectFile( QFileInfo( m_audioFile ).fileName() ); + ofd.selectFile(QFileInfo(m_audioFile).fileName()); } - if( ofd.exec () == QDialog::Accepted ) + if (ofd.exec () == QDialog::Accepted) { - if( ofd.selectedFiles().isEmpty() ) + if (ofd.selectedFiles().isEmpty()) { return QString(); } - return PathUtil::toShortestRelative( ofd.selectedFiles()[0] ); + return PathUtil::toShortestRelative(ofd.selectedFiles()[0]); } return QString(); @@ -1025,7 +1038,7 @@ QString SampleBuffer::openAndSetAudioFile() if(!fileName.isEmpty()) { - this->setAudioFile( fileName ); + this->setAudioFile(fileName); } return fileName; @@ -1034,16 +1047,16 @@ QString SampleBuffer::openAndSetAudioFile() QString SampleBuffer::openAndSetWaveformFile() { - if( m_audioFile.isEmpty() ) + if (m_audioFile.isEmpty()) { m_audioFile = ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac"; } QString fileName = this->openAudioFile(); - if(!fileName.isEmpty()) + if (!fileName.isEmpty()) { - this->setAudioFile( fileName ); + this->setAudioFile(fileName); } else { @@ -1060,148 +1073,145 @@ QString SampleBuffer::openAndSetWaveformFile() #ifdef LMMS_HAVE_FLAC_STREAM_ENCODER_H FLAC__StreamEncoderWriteStatus flacStreamEncoderWriteCallback( - const FLAC__StreamEncoder * - /*_encoder*/, - const FLAC__byte _buffer[], - unsigned int/* _samples*/, - unsigned int _bytes, - unsigned int/* _current_frame*/, - void * _client_data ) + const FLAC__StreamEncoder * /*encoder*/, + const FLAC__byte buffer[], + unsigned int /*samples*/, + unsigned int bytes, + unsigned int /*currentFrame*/, + void * clientData +) { -/* if( _bytes == 0 ) +/* if (bytes == 0) { return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; }*/ - return ( static_cast( _client_data )->write( - (const char *) _buffer, _bytes ) == - (int) _bytes ) ? - FLAC__STREAM_ENCODER_WRITE_STATUS_OK : - FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + return (static_cast(clientData)->write( + (const char *) buffer, bytes) == (int) bytes) + ? FLAC__STREAM_ENCODER_WRITE_STATUS_OK + : FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; } -void flacStreamEncoderMetadataCallback( const FLAC__StreamEncoder *, - const FLAC__StreamMetadata * _metadata, - void * _client_data ) +void flacStreamEncoderMetadataCallback( + const FLAC__StreamEncoder *, + const FLAC__StreamMetadata * metadata, + void * clientData +) { - QBuffer * b = static_cast( _client_data ); - b->seek( 0 ); - b->write( (const char *) _metadata, sizeof( *_metadata ) ); + QBuffer * b = static_cast(clientData); + b->seek(0); + b->write((const char *) metadata, sizeof(*metadata)); } #endif -QString & SampleBuffer::toBase64( QString & _dst ) const +QString & SampleBuffer::toBase64(QString & dst) const { #ifdef LMMS_HAVE_FLAC_STREAM_ENCODER_H const f_cnt_t FRAMES_PER_BUF = 1152; - FLAC__StreamEncoder * flac_enc = FLAC__stream_encoder_new(); - FLAC__stream_encoder_set_channels( flac_enc, DEFAULT_CHANNELS ); - FLAC__stream_encoder_set_blocksize( flac_enc, FRAMES_PER_BUF ); -/* FLAC__stream_encoder_set_do_exhaustive_model_search( flac_enc, true ); - FLAC__stream_encoder_set_do_mid_side_stereo( flac_enc, true );*/ - FLAC__stream_encoder_set_sample_rate( flac_enc, - Engine::mixer()->sampleRate() ); - QBuffer ba_writer; - ba_writer.open( QBuffer::WriteOnly ); - - FLAC__stream_encoder_set_write_callback( flac_enc, - flacStreamEncoderWriteCallback ); - FLAC__stream_encoder_set_metadata_callback( flac_enc, - flacStreamEncoderMetadataCallback ); - FLAC__stream_encoder_set_client_data( flac_enc, &ba_writer ); - if( FLAC__stream_encoder_init( flac_enc ) != FLAC__STREAM_ENCODER_OK ) + FLAC__StreamEncoder * flacEnc = FLAC__stream_encoder_new(); + FLAC__stream_encoder_set_channels(flacEnc, DEFAULT_CHANNELS); + FLAC__stream_encoder_set_blocksize(flacEnc, FRAMES_PER_BUF); +/* FLAC__stream_encoder_set_do_exhaustive_model_search(flacEnc, true); + FLAC__stream_encoder_set_do_mid_side_stereo(flacEnc, true);*/ + FLAC__stream_encoder_set_sample_rate(flacEnc, + Engine::mixer()->sampleRate()); + + QBuffer baWriter; + baWriter.open(QBuffer::WriteOnly); + + FLAC__stream_encoder_set_write_callback(flacEnc, + flacStreamEncoderWriteCallback); + FLAC__stream_encoder_set_metadata_callback(flacEnc, + flacStreamEncoderMetadataCallback); + FLAC__stream_encoder_set_client_data(flacEnc, &baWriter); + + if (FLAC__stream_encoder_init(flacEnc) != FLAC__STREAM_ENCODER_OK) { - printf( "error within FLAC__stream_encoder_init()!\n" ); + printf("Error within FLAC__stream_encoder_init()!\n"); } - f_cnt_t frame_cnt = 0; - while( frame_cnt < m_frames ) + + f_cnt_t frameCnt = 0; + + while (frameCnt < m_frames) { - f_cnt_t remaining = qMin( FRAMES_PER_BUF, - m_frames - frame_cnt ); + f_cnt_t remaining = qMin(FRAMES_PER_BUF, m_frames - frameCnt); FLAC__int32 buf[FRAMES_PER_BUF * DEFAULT_CHANNELS]; - for( f_cnt_t f = 0; f < remaining; ++f ) + for (f_cnt_t f = 0; f < remaining; ++f) { - for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch) { buf[f*DEFAULT_CHANNELS+ch] = (FLAC__int32)( - Mixer::clip( m_data[f+frame_cnt][ch] ) * - OUTPUT_SAMPLE_MULTIPLIER ); + Mixer::clip(m_data[f+frameCnt][ch]) * + OUTPUT_SAMPLE_MULTIPLIER); } } - FLAC__stream_encoder_process_interleaved( flac_enc, buf, - remaining ); - frame_cnt += remaining; + FLAC__stream_encoder_process_interleaved(flacEnc, buf, remaining); + frameCnt += remaining; } - FLAC__stream_encoder_finish( flac_enc ); - FLAC__stream_encoder_delete( flac_enc ); - printf("%d %d\n", frame_cnt, (int)ba_writer.size() ); - ba_writer.close(); - - base64::encode( ba_writer.buffer().data(), ba_writer.buffer().size(), - _dst ); + FLAC__stream_encoder_finish(flacEnc); + FLAC__stream_encoder_delete(flacEnc); + printf("%d %d\n", frameCnt, (int)baWriter.size()); + baWriter.close(); + base64::encode(baWriter.buffer().data(), baWriter.buffer().size(), dst); #else /* LMMS_HAVE_FLAC_STREAM_ENCODER_H */ - base64::encode( (const char *) m_data, - m_frames * sizeof( sampleFrame ), _dst ); + base64::encode((const char *) m_data, + m_frames * sizeof(sampleFrame), dst); #endif /* LMMS_HAVE_FLAC_STREAM_ENCODER_H */ - return _dst; + return dst; } -SampleBuffer * SampleBuffer::resample( const sample_rate_t _src_sr, - const sample_rate_t _dst_sr ) +SampleBuffer * SampleBuffer::resample(const sample_rate_t srcSR, const sample_rate_t dstSR ) { sampleFrame * data = m_data; const f_cnt_t frames = m_frames; - const f_cnt_t dst_frames = static_cast( frames / - (float) _src_sr * (float) _dst_sr ); - SampleBuffer * dst_sb = new SampleBuffer( dst_frames ); - sampleFrame * dst_buf = dst_sb->m_origData; + const f_cnt_t dstFrames = static_cast((frames / (float) srcSR) * (float) dstSR); + SampleBuffer * dstSB = new SampleBuffer(dstFrames); + sampleFrame * dstBuf = dstSB->m_origData; // yeah, libsamplerate, let's rock with sinc-interpolation! int error; SRC_STATE * state; - if( ( state = src_new( SRC_SINC_MEDIUM_QUALITY, - DEFAULT_CHANNELS, &error ) ) != NULL ) + if ((state = src_new(SRC_SINC_MEDIUM_QUALITY, DEFAULT_CHANNELS, &error)) != nullptr) { - SRC_DATA src_data; - src_data.end_of_input = 1; - src_data.data_in = data->data (); - src_data.data_out = dst_buf->data (); - src_data.input_frames = frames; - src_data.output_frames = dst_frames; - src_data.src_ratio = (double) _dst_sr / _src_sr; - if( ( error = src_process( state, &src_data ) ) ) + SRC_DATA srcData; + srcData.end_of_input = 1; + srcData.data_in = data->data(); + srcData.data_out = dstBuf->data(); + srcData.input_frames = frames; + srcData.output_frames = dstFrames; + srcData.src_ratio = (double) dstSR / srcSR; + if ((error = src_process(state, &srcData))) { - printf( "SampleBuffer: error while resampling: %s\n", - src_strerror( error ) ); + printf("SampleBuffer: error while resampling: %s\n", src_strerror(error)); } - src_delete( state ); + src_delete(state); } else { - printf( "Error: src_new() failed in sample_buffer.cpp!\n" ); + printf("Error: src_new() failed in sample_buffer.cpp!\n"); } - dst_sb->update(); - return dst_sb; + dstSB->update(); + return dstSB; } -void SampleBuffer::setAudioFile( const QString & _audio_file ) +void SampleBuffer::setAudioFile(const QString & audioFile) { - m_audioFile = PathUtil::toShortestRelative( _audio_file ); + m_audioFile = PathUtil::toShortestRelative(audioFile); update(); } @@ -1211,30 +1221,29 @@ void SampleBuffer::setAudioFile( const QString & _audio_file ) struct flacStreamDecoderClientData { - QBuffer * read_buffer; - QBuffer * write_buffer; + QBuffer * readBuffer; + QBuffer * writeBuffer; } ; FLAC__StreamDecoderReadStatus flacStreamDecoderReadCallback( - const FLAC__StreamDecoder * - /*_decoder*/, - FLAC__byte * _buffer, - unsigned int * _bytes, - void * _client_data ) + const FLAC__StreamDecoder * /*decoder*/, + FLAC__byte * buffer, + unsigned int * bytes, + void * clientData +) { int res = static_cast( - _client_data )->read_buffer->read( - (char *) _buffer, *_bytes ); + clientData)->readBuffer->read((char *) buffer, *bytes); - if( res > 0 ) + if (res > 0) { - *_bytes = res; + *bytes = res; return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; - } - *_bytes = 0; + + *bytes = 0; return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; } @@ -1242,116 +1251,116 @@ FLAC__StreamDecoderReadStatus flacStreamDecoderReadCallback( FLAC__StreamDecoderWriteStatus flacStreamDecoderWriteCallback( - const FLAC__StreamDecoder * - /*_decoder*/, - const FLAC__Frame * _frame, - const FLAC__int32 * const _buffer[], - void * _client_data ) + const FLAC__StreamDecoder * /*decoder*/, + const FLAC__Frame * frame, + const FLAC__int32 * const buffer[], + void * clientData +) { - if( _frame->header.channels != 2 ) + if (frame->header.channels != 2) { - printf( "channels != 2 in " - "flacStreamDecoderWriteCallback()\n" ); + printf("channels != 2 in flacStreamDecoderWriteCallback()\n"); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } - if( _frame->header.bits_per_sample != 16 ) + if (frame->header.bits_per_sample != 16) { - printf( "bits_per_sample != 16 in " - "flacStreamDecoderWriteCallback()\n" ); + printf("bits_per_sample != 16 in flacStreamDecoderWriteCallback()\n"); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } - const f_cnt_t frames = _frame->header.blocksize; - for( f_cnt_t frame = 0; frame < frames; ++frame ) + const f_cnt_t numberOfFrames = frame->header.blocksize; + for (f_cnt_t f = 0; f < numberOfFrames; ++f) { - sampleFrame sframe = { _buffer[0][frame] / - OUTPUT_SAMPLE_MULTIPLIER, - _buffer[1][frame] / - OUTPUT_SAMPLE_MULTIPLIER - } ; + sampleFrame sframe = { buffer[0][f] / OUTPUT_SAMPLE_MULTIPLIER, + buffer[1][f] / OUTPUT_SAMPLE_MULTIPLIER + } ; static_cast( - _client_data )->write_buffer->write( - (const char *) sframe, sizeof( sframe ) ); + clientData )->writeBuffer->write( + (const char *) sframe, sizeof(sframe)); } return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } -void flacStreamDecoderMetadataCallback( const FLAC__StreamDecoder *, - const FLAC__StreamMetadata *, - void * /*_client_data*/ ) +void flacStreamDecoderMetadataCallback( + const FLAC__StreamDecoder *, + const FLAC__StreamMetadata *, + void * /*clientData*/ +) { printf("stream decoder metadata callback\n"); -/* QBuffer * b = static_cast( _client_data ); - b->seek( 0 ); - b->write( (const char *) _metadata, sizeof( *_metadata ) );*/ +/* QBuffer * b = static_cast(clientData); + b->seek(0); + b->write((const char *) metadata, sizeof(*metadata));*/ } -void flacStreamDecoderErrorCallback( const FLAC__StreamDecoder *, - FLAC__StreamDecoderErrorStatus _status, - void * /*_client_data*/ ) +void flacStreamDecoderErrorCallback( + const FLAC__StreamDecoder *, + FLAC__StreamDecoderErrorStatus status, + void * /*clientData*/ +) { - printf("error callback! %d\n", _status); + printf("error callback! %d\n", status); // what to do now?? } #endif -void SampleBuffer::loadFromBase64( const QString & _data ) +void SampleBuffer::loadFromBase64(const QString & data) { - char * dst = NULL; + char * dst = nullptr; int dsize = 0; - base64::decode( _data, &dst, &dsize ); + base64::decode(data, &dst, &dsize); #ifdef LMMS_HAVE_FLAC_STREAM_DECODER_H - QByteArray orig_data = QByteArray::fromRawData( dst, dsize ); - QBuffer ba_reader( &orig_data ); - ba_reader.open( QBuffer::ReadOnly ); + QByteArray origData = QByteArray::fromRawData(dst, dsize); + QBuffer baReader(&origData); + baReader.open(QBuffer::ReadOnly); - QBuffer ba_writer; - ba_writer.open( QBuffer::WriteOnly ); + QBuffer baWriter; + baWriter.open(QBuffer::WriteOnly); - flacStreamDecoderClientData cdata = { &ba_reader, &ba_writer } ; + flacStreamDecoderClientData cdata = { &baReader, &baWriter } ; - FLAC__StreamDecoder * flac_dec = FLAC__stream_decoder_new(); + FLAC__StreamDecoder * flacDec = FLAC__stream_decoder_new(); - FLAC__stream_decoder_set_read_callback( flac_dec, - flacStreamDecoderReadCallback ); - FLAC__stream_decoder_set_write_callback( flac_dec, - flacStreamDecoderWriteCallback ); - FLAC__stream_decoder_set_error_callback( flac_dec, - flacStreamDecoderErrorCallback ); - FLAC__stream_decoder_set_metadata_callback( flac_dec, - flacStreamDecoderMetadataCallback ); - FLAC__stream_decoder_set_client_data( flac_dec, &cdata ); + FLAC__stream_decoder_set_read_callback(flacDec, + flacStreamDecoderReadCallback); + FLAC__stream_decoder_set_write_callback(flacDec, + flacStreamDecoderWriteCallback); + FLAC__stream_decoder_set_error_callback(flacDec, + flacStreamDecoderErrorCallback); + FLAC__stream_decoder_set_metadata_callback(flacDec, + flacStreamDecoderMetadataCallback); + FLAC__stream_decoder_set_client_data(flacDec, &cdata); - FLAC__stream_decoder_init( flac_dec ); + FLAC__stream_decoder_init(flacDec); - FLAC__stream_decoder_process_until_end_of_stream( flac_dec ); + FLAC__stream_decoder_process_until_end_of_stream(flacDec); - FLAC__stream_decoder_finish( flac_dec ); - FLAC__stream_decoder_delete( flac_dec ); + FLAC__stream_decoder_finish(flacDec); + FLAC__stream_decoder_delete(flacDec); - ba_reader.close(); + baReader.close(); - orig_data = ba_writer.buffer(); - printf("%d\n", (int) orig_data.size() ); + origData = baWriter.buffer(); + printf("%d\n", (int) origData.size()); - m_origFrames = orig_data.size() / sizeof( sampleFrame ); - MM_FREE( m_origData ); - m_origData = MM_ALLOC( sampleFrame, m_origFrames ); - memcpy( m_origData, orig_data.data(), orig_data.size() ); + m_origFrames = origData.size() / sizeof(sampleFrame); + MM_FREE(m_origData); + m_origData = MM_ALLOC(sampleFrame, m_origFrames); + memcpy(m_origData, origData.data(), origData.size()); #else /* LMMS_HAVE_FLAC_STREAM_DECODER_H */ - m_origFrames = dsize / sizeof( sampleFrame ); - MM_FREE( m_origData ); - m_origData = MM_ALLOC( sampleFrame, m_origFrames ); - memcpy( m_origData, dst, dsize ); + m_origFrames = dsize / sizeof(sampleFrame); + MM_FREE(m_origData); + m_origData = MM_ALLOC(sampleFrame, m_origFrames); + memcpy(m_origData, dst, dsize); #endif @@ -1364,37 +1373,37 @@ void SampleBuffer::loadFromBase64( const QString & _data ) -void SampleBuffer::setStartFrame( const f_cnt_t _s ) +void SampleBuffer::setStartFrame(const f_cnt_t s) { - m_startFrame = _s; + m_startFrame = s; } -void SampleBuffer::setEndFrame( const f_cnt_t _e ) +void SampleBuffer::setEndFrame(const f_cnt_t e) { - m_endFrame = _e; + m_endFrame = e; } -void SampleBuffer::setAmplification( float _a ) +void SampleBuffer::setAmplification(float a) { - m_amplification = _a; + m_amplification = a; emit sampleUpdated(); } -void SampleBuffer::setReversed( bool _on ) +void SampleBuffer::setReversed(bool on) { Engine::mixer()->requestChangeInModel(); m_varLock.lockForWrite(); - if (m_reversed != _on) { std::reverse(m_data, m_data + m_frames); } - m_reversed = _on; + if (m_reversed != on) { std::reverse(m_data, m_data + m_frames); } + m_reversed = on; m_varLock.unlock(); Engine::mixer()->doneChangeInModel(); emit sampleUpdated(); @@ -1404,24 +1413,17 @@ void SampleBuffer::setReversed( bool _on ) - - - - - - - -SampleBuffer::handleState::handleState( bool _varying_pitch, int interpolation_mode ) : - m_frameIndex( 0 ), - m_varyingPitch( _varying_pitch ), - m_isBackwards( false ) +SampleBuffer::handleState::handleState(bool varyingPitch, int interpolationMode) : + m_frameIndex(0), + m_varyingPitch(varyingPitch), + m_isBackwards(false) { int error; - m_interpolationMode = interpolation_mode; + m_interpolationMode = interpolationMode; - if( ( m_resamplingData = src_new( interpolation_mode, DEFAULT_CHANNELS, &error ) ) == NULL ) + if ((m_resamplingData = src_new(interpolationMode, DEFAULT_CHANNELS, &error)) == nullptr) { - qDebug( "Error: src_new() failed in sample_buffer.cpp!\n" ); + qDebug("Error: src_new() failed in sample_buffer.cpp!\n"); } } @@ -1430,5 +1432,5 @@ SampleBuffer::handleState::handleState( bool _varying_pitch, int interpolation_m SampleBuffer::handleState::~handleState() { - src_delete( m_resamplingData ); + src_delete(m_resamplingData); } From 2f664490920179f0e5efb4406f6b0b8431947887 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Tue, 8 Dec 2020 00:12:04 +0100 Subject: [PATCH 176/180] Implement Lv2 Options (#5761) Implement `LV2_OPTIONS__options` feature and some buf-size properties. The code currently assumes that the LMMS buffersize never changes, which is currently true in the LMMS code base. --- include/Lv2Manager.h | 2 + include/Lv2Options.h | 104 ++++++++++++++++++++++++++++++++++ include/Lv2Proc.h | 5 +- include/Lv2UridCache.h | 18 ++++++ src/core/CMakeLists.txt | 1 + src/core/lv2/Lv2Manager.cpp | 22 +++++++ src/core/lv2/Lv2Options.cpp | 93 ++++++++++++++++++++++++++++++ src/core/lv2/Lv2Proc.cpp | 52 +++++++++++++++-- src/core/lv2/Lv2UridCache.cpp | 15 +++++ 9 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 include/Lv2Options.h create mode 100644 src/core/lv2/Lv2Options.cpp diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 585fdafc57d..dbea510ce39 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -130,6 +130,8 @@ class Lv2Manager return m_supportedFeatureURIs; } bool isFeatureSupported(const char* featName) const; + AutoLilvNodes findNodes(const LilvNode *subject, + const LilvNode *predicate, const LilvNode *object); static const std::set& getPluginBlacklist() { diff --git a/include/Lv2Options.h b/include/Lv2Options.h new file mode 100644 index 00000000000..1453de2ea10 --- /dev/null +++ b/include/Lv2Options.h @@ -0,0 +1,104 @@ +/* + * Lv2Options.h - Lv2Options class + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2OPTIONS_H +#define LV2OPTIONS_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include +#include +#include +#include + +#include "Engine.h" +#include "Lv2Manager.h" +#include "Lv2UridCache.h" + +/** + Option container + + References all available options for a plugin and maps them to their URIDs. + This class is used per Lv2 processor (justification in Lv2Proc::initMOptions()) + + The public member functions should be called in descending order: + + 1. supportOption: set all supported option URIDs + 2. initOption: initialize options with values + 3. createOptionVectors: create the option vectors required for + the feature + 4. access the latter using feature() +*/ +class Lv2Options +{ +public: + //! Return if an option is supported by LMMS + static bool isOptionSupported(LV2_URID key); + //! Mark option as supported + static void supportOption(LV2_URID key); + + //! Initialize an option + template + void initOption(Lv2UridCache::Id key, Arg&& value, + LV2_Options_Context context = LV2_OPTIONS_INSTANCE, + std::uint32_t subject = 0) + { + const Lv2UridCache& cache = Engine::getLv2Manager()->uridCache(); + initOption(cache[key], sizeof(Opt), cache[Lv2UridCache::IdForType::value], + std::make_shared(std::forward(value)), context, subject); + } + //! Fill m_options and m_optionPointers with all options + void createOptionVectors(); + //! Return the feature + const LV2_Options_Option* feature() const + { + return m_options.data(); + } + +private: + //! Initialize an option internally + void initOption(LV2_URID key, + uint32_t size, + LV2_URID type, + std::shared_ptr value, + LV2_Options_Context context = LV2_OPTIONS_INSTANCE, + uint32_t subject = 0); + //! options that are supported by every processor + static std::set s_supportedOptions; + //! options + data, ordered by URID + std::map m_optionByUrid; + //! option storage + std::vector m_options; + //! option value storage + std::map> m_optionValues; +}; + +#endif // LMMS_HAVE_LV2 + +#endif // LV2OPTIONS_H diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 312f9cf34f2..a80c10e3f18 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -35,6 +35,7 @@ #include "Lv2Basics.h" #include "Lv2Features.h" +#include "Lv2Options.h" #include "LinkedModelGroups.h" #include "MidiEvent.h" #include "Plugin.h" @@ -168,6 +169,7 @@ class Lv2Proc : public LinkedModelGroup const LilvPlugin* m_plugin; LilvInstance* m_instance; Lv2Features m_features; + Lv2Options m_options; // full list of ports std::vector> m_ports; @@ -187,11 +189,12 @@ class Lv2Proc : public LinkedModelGroup ringbuffer_reader_t m_midiInputReader; // other - static std::size_t minimumEvbufSize() { return 1 << 15; /* ardour uses this*/ } + static int32_t defaultEvbufSize() { return 1 << 15; /* ardour uses this*/ } //! models for the controls, sorted by port symbols std::map m_connectedModels; + void initMOptions(); //!< initialize m_options void initPluginSpecificFeatures(); //! load a file in the plugin, but don't do anything in LMMS diff --git a/include/Lv2UridCache.h b/include/Lv2UridCache.h index 81dd1346cf7..1921bdfd70b 100644 --- a/include/Lv2UridCache.h +++ b/include/Lv2UridCache.h @@ -29,6 +29,7 @@ #ifdef LMMS_HAVE_LV2 +#include #include //! Cached URIDs for fast access (for use in real-time code) @@ -37,16 +38,33 @@ class Lv2UridCache public: enum class Id //!< ID for m_uridCache array { + // keep it alphabetically (except "size" at the end) + atom_Float, + atom_Int, + bufsz_minBlockLength, + bufsz_maxBlockLength, + bufsz_nominalBlockLength, + bufsz_sequenceSize, midi_MidiEvent, + param_sampleRate, + // exception to alphabetic ordering - keep at the end: size }; + + template + struct IdForType; + //! Return URID for a cache ID uint32_t operator[](Id id) const; Lv2UridCache(class UridMap& mapper); + private: uint32_t m_cache[static_cast(Id::size)]; }; +template<> struct Lv2UridCache::IdForType { static constexpr auto value = Id::atom_Float; }; +template<> struct Lv2UridCache::IdForType { static constexpr auto value = Id::atom_Int; }; + #endif // LMMS_HAVE_LV2 #endif // LV2URIDCACHE_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b1e31c6930b..017ebba1426 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -100,6 +100,7 @@ set(LMMS_SRCS core/lv2/Lv2Ports.cpp core/lv2/Lv2Proc.cpp core/lv2/Lv2Manager.cpp + core/lv2/Lv2Options.cpp core/lv2/Lv2SubPluginFeatures.cpp core/lv2/Lv2UridCache.cpp core/lv2/Lv2UridMap.cpp diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 635b5462c44..57f04f6808c 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include "Plugin.h" #include "PluginFactory.h" #include "Lv2ControlBase.h" +#include "Lv2Options.h" #include "PluginIssue.h" @@ -67,6 +69,17 @@ Lv2Manager::Lv2Manager() : m_supportedFeatureURIs.insert(LV2_URID__map); m_supportedFeatureURIs.insert(LV2_URID__unmap); + m_supportedFeatureURIs.insert(LV2_OPTIONS__options); + + auto supportOpt = [this](Lv2UridCache::Id id) + { + Lv2Options::supportOption(uridCache()[id]); + }; + supportOpt(Lv2UridCache::Id::param_sampleRate); + supportOpt(Lv2UridCache::Id::bufsz_maxBlockLength); + supportOpt(Lv2UridCache::Id::bufsz_minBlockLength); + supportOpt(Lv2UridCache::Id::bufsz_nominalBlockLength); + supportOpt(Lv2UridCache::Id::bufsz_sequenceSize); } @@ -205,6 +218,15 @@ bool Lv2Manager::isFeatureSupported(const char *featName) const +AutoLilvNodes Lv2Manager::findNodes(const LilvNode *subject, + const LilvNode *predicate, const LilvNode *object) +{ + return AutoLilvNodes(lilv_world_find_nodes (m_world, subject, predicate, object)); +} + + + + // unused + untested yet bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { diff --git a/src/core/lv2/Lv2Options.cpp b/src/core/lv2/Lv2Options.cpp new file mode 100644 index 00000000000..e941165f090 --- /dev/null +++ b/src/core/lv2/Lv2Options.cpp @@ -0,0 +1,93 @@ +/* + * Lv2Options.cpp - Lv2Options implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Options.h" + +#ifdef LMMS_HAVE_LV2 + +#include + + +std::set Lv2Options::s_supportedOptions; + + + + +bool Lv2Options::isOptionSupported(LV2_URID key) +{ + return s_supportedOptions.find(key) != s_supportedOptions.end(); +} + + + + +void Lv2Options::supportOption(LV2_URID key) +{ + const auto result = s_supportedOptions.insert(key); + Q_ASSERT(result.second); +} + + + + +void Lv2Options::createOptionVectors() +{ + // create vector of options + for(LV2_URID urid : s_supportedOptions) + { + auto itr = m_optionByUrid.find(urid); + Q_ASSERT(itr != m_optionByUrid.end()); + m_options.push_back(itr->second); + } + LV2_Options_Option nullOption; + nullOption.key = 0; + nullOption.value = nullptr; + m_options.push_back(nullOption); +} + + + + +void Lv2Options::initOption(LV2_URID key, uint32_t size, LV2_URID type, + std::shared_ptr value, + LV2_Options_Context context, uint32_t subject) +{ + Q_ASSERT(isOptionSupported(key)); + + LV2_Options_Option opt; + opt.key = key; + opt.context = context; + opt.subject = subject; + opt.size = size; + opt.type = type; + opt.value = value.get(); + + const auto optResult = m_optionByUrid.emplace(key, opt); + const auto valResult = m_optionValues.emplace(key, std::move(value)); + Q_ASSERT(optResult.second); + Q_ASSERT(valResult.second); +} + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index bbfc7065ca0..1ae5221507e 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -128,6 +128,23 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, } } + Lv2Manager* mgr = Engine::getLv2Manager(); + AutoLilvNode requiredOptionNode(mgr->uri(LV2_OPTIONS__requiredOption)); + AutoLilvNodes requiredOptions = mgr->findNodes(lilv_plugin_get_uri (plugin), requiredOptionNode.get(), nullptr); + if (requiredOptions) + { + LILV_FOREACH(nodes, i, requiredOptions.get()) + { + const char* ro = lilv_node_as_uri (lilv_nodes_get (requiredOptions.get(), i)); + if (!Lv2Options::isOptionSupported(mgr->uridMap().map(ro))) + { + // yes, this is not a Lv2 feature, + // but it's a feature in abstract sense + issues.emplace_back(featureNotSupported, ro); + } + } + } + return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2) ? Plugin::Undefined : (audioChannels[inCount] > 0) @@ -422,11 +439,38 @@ bool Lv2Proc::hasNoteInput() const +void Lv2Proc::initMOptions() +{ + /* + sampleRate: + LMMS can in theory inform plugins of a new sample rate. + However, Lv2 plugins seem to not allow sample rate changes + (not even through LV2_Options_Interface) - it's assumed to be + fixed after being passed via LV2_Descriptor::instantiate. + So, if the sampleRate would change, the plugin will need to + re-initialize, and this code section will be + executed again, creating a new option vector. + */ + float sampleRate = Engine::mixer()->processingSampleRate(); + int32_t blockLength = Engine::mixer()->framesPerPeriod(); + int32_t sequenceSize = defaultEvbufSize(); + + using Id = Lv2UridCache::Id; + m_options.initOption(Id::param_sampleRate, sampleRate); + m_options.initOption(Id::bufsz_maxBlockLength, blockLength); + m_options.initOption(Id::bufsz_minBlockLength, blockLength); + m_options.initOption(Id::bufsz_nominalBlockLength, blockLength); + m_options.initOption(Id::bufsz_sequenceSize, sequenceSize); + m_options.createOptionVectors(); +} + + + + void Lv2Proc::initPluginSpecificFeatures() { - // nothing yet - // it would look like this: - // m_features[LV2_URID__map] = m_uridMapFeature + initMOptions(); + m_features[LV2_OPTIONS__options] = const_cast(m_options.feature()); } @@ -558,7 +602,7 @@ void Lv2Proc::createPort(std::size_t portNum) } } - int minimumSize = minimumEvbufSize(); + int minimumSize = defaultEvbufSize(); Lv2Manager* mgr = Engine::getLv2Manager(); diff --git a/src/core/lv2/Lv2UridCache.cpp b/src/core/lv2/Lv2UridCache.cpp index 5887a8e3906..3392f9bd2f7 100644 --- a/src/core/lv2/Lv2UridCache.cpp +++ b/src/core/lv2/Lv2UridCache.cpp @@ -26,11 +26,19 @@ #ifdef LMMS_HAVE_LV2 +#include +#include #include +#include #include #include "Lv2UridMap.h" +// support newer URIs on old systems +#ifndef LV2_BUF_SIZE__nominalBlockLength +#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" +#endif + uint32_t Lv2UridCache::operator[](Lv2UridCache::Id id) const { Q_ASSERT(id != Id::size); @@ -47,7 +55,14 @@ Lv2UridCache::Lv2UridCache(UridMap &mapper) m_cache[static_cast(id)] = mapper.map(uridStr); }; + init(Id::atom_Float, LV2_ATOM__Float); + init(Id::atom_Int, LV2_ATOM__Int); + init(Id::bufsz_minBlockLength, LV2_BUF_SIZE__minBlockLength); + init(Id::bufsz_maxBlockLength, LV2_BUF_SIZE__maxBlockLength); + init(Id::bufsz_nominalBlockLength, LV2_BUF_SIZE__nominalBlockLength); + init(Id::bufsz_sequenceSize, LV2_BUF_SIZE__sequenceSize); init(Id::midi_MidiEvent, LV2_MIDI__MidiEvent); + init(Id::param_sampleRate, LV2_PARAMETERS__sampleRate); for(uint32_t urid : m_cache) { Q_ASSERT(urid != noIdYet); } } From 040fb488675d094b1a9602a97caab76f11510bae Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Mon, 7 Dec 2020 20:25:00 -0300 Subject: [PATCH 177/180] Update code style for BBTrackContainer (#5812) --- include/BBTrackContainer.h | 15 +++-- src/core/BBTrackContainer.cpp | 114 +++++++++++++++++----------------- 2 files changed, 63 insertions(+), 66 deletions(-) diff --git a/include/BBTrackContainer.h b/include/BBTrackContainer.h index 1d8f73901e0..79bf2e31565 100644 --- a/include/BBTrackContainer.h +++ b/include/BBTrackContainer.h @@ -38,8 +38,7 @@ class LMMS_EXPORT BBTrackContainer : public TrackContainer BBTrackContainer(); virtual ~BBTrackContainer(); - virtual bool play( TimePos _start, const fpp_t _frames, - const f_cnt_t _frame_base, int _tco_num = -1 ); + virtual bool play(TimePos start, const fpp_t frames, const f_cnt_t frameBase, int tcoNum = -1); void updateAfterTrackAdd() override; @@ -48,19 +47,19 @@ class LMMS_EXPORT BBTrackContainer : public TrackContainer return "bbtrackcontainer"; } - bar_t lengthOfBB( int _bb ) const; + bar_t lengthOfBB(int bb) const; inline bar_t lengthOfCurrentBB() { - return lengthOfBB( currentBB() ); + return lengthOfBB(currentBB()); } int numOfBBs() const; - void removeBB( int _bb ); + void removeBB(int bb); - void swapBB( int _bb1, int _bb2 ); + void swapBB(int bb1, int bb2); - void updateBBTrack( TrackContentObject * _tco ); + void updateBBTrack(TrackContentObject * tco); void fixIncorrectPositions(); - void createTCOsForBB( int _bb ); + void createTCOsForBB(int bb); AutomatedValueMap automatedValuesAt(TimePos time, int tcoNum) const override; diff --git a/src/core/BBTrackContainer.cpp b/src/core/BBTrackContainer.cpp index a374ad29af6..f818684ec4e 100644 --- a/src/core/BBTrackContainer.cpp +++ b/src/core/BBTrackContainer.cpp @@ -32,15 +32,15 @@ BBTrackContainer::BBTrackContainer() : TrackContainer(), - m_bbComboBoxModel( this ) + m_bbComboBoxModel(this) { - connect( &m_bbComboBoxModel, SIGNAL( dataChanged() ), - this, SLOT( currentBBChanged() ) ); + connect(&m_bbComboBoxModel, SIGNAL(dataChanged()), + this, SLOT(currentBBChanged())); // we *always* want to receive updates even in case BB actually did // not change upon setCurrentBB()-call - connect( &m_bbComboBoxModel, SIGNAL( dataUnchanged() ), - this, SLOT( currentBBChanged() ) ); - setType( BBContainer ); + connect(&m_bbComboBoxModel, SIGNAL(dataUnchanged()), + this, SLOT(currentBBChanged())); + setType(BBContainer); } @@ -53,27 +53,27 @@ BBTrackContainer::~BBTrackContainer() -bool BBTrackContainer::play( TimePos _start, fpp_t _frames, - f_cnt_t _offset, int _tco_num ) +bool BBTrackContainer::play(TimePos start, fpp_t frames, f_cnt_t offset, int tcoNum) { - bool played_a_note = false; - if( lengthOfBB( _tco_num ) <= 0 ) + bool notePlayed = false; + + if (lengthOfBB(tcoNum) <= 0) { return false; } - _start = _start % ( lengthOfBB( _tco_num ) * TimePos::ticksPerBar() ); + start = start % (lengthOfBB(tcoNum) * TimePos::ticksPerBar()); TrackList tl = tracks(); - for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) + for (Track * t : tl) { - if( ( *it )->play( _start, _frames, _offset, _tco_num ) ) + if (t->play(start, frames, offset, tcoNum)) { - played_a_note = true; + notePlayed = true; } } - return played_a_note; + return notePlayed; } @@ -81,7 +81,7 @@ bool BBTrackContainer::play( TimePos _start, fpp_t _frames, void BBTrackContainer::updateAfterTrackAdd() { - if( numOfBBs() == 0 && !Engine::getSong()->isLoadingProject() ) + if (numOfBBs() == 0 && !Engine::getSong()->isLoadingProject()) { Engine::getSong()->addBBTrack(); } @@ -90,21 +90,21 @@ void BBTrackContainer::updateAfterTrackAdd() -bar_t BBTrackContainer::lengthOfBB( int _bb ) const +bar_t BBTrackContainer::lengthOfBB(int bb) const { - TimePos max_length = TimePos::ticksPerBar(); + TimePos maxLength = TimePos::ticksPerBar(); const TrackList & tl = tracks(); - for (Track* t : tl) + for (Track * t : tl) { - // Don't create TCOs here if not exist - if (_bb < t->numOfTCOs()) + // Don't create TCOs here if they don't exist + if (bb < t->numOfTCOs()) { - max_length = qMax(max_length, t->getTCO( _bb )->length()); + maxLength = qMax(maxLength, t->getTCO(bb)->length()); } } - return max_length.nextFullBar(); + return maxLength.nextFullBar(); } @@ -112,35 +112,35 @@ bar_t BBTrackContainer::lengthOfBB( int _bb ) const int BBTrackContainer::numOfBBs() const { - return Engine::getSong()->countTracks( Track::BBTrack ); + return Engine::getSong()->countTracks(Track::BBTrack); } -void BBTrackContainer::removeBB( int _bb ) +void BBTrackContainer::removeBB(int bb) { TrackList tl = tracks(); - for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) + for (Track * t : tl) { - delete ( *it )->getTCO( _bb ); - ( *it )->removeBar( _bb * DefaultTicksPerBar ); + delete t->getTCO(bb); + t->removeBar(bb * DefaultTicksPerBar); } - if( _bb <= currentBB() ) + if (bb <= currentBB()) { - setCurrentBB( qMax( currentBB() - 1, 0 ) ); + setCurrentBB(qMax(currentBB() - 1, 0)); } } -void BBTrackContainer::swapBB( int _bb1, int _bb2 ) +void BBTrackContainer::swapBB(int bb1, int bb2) { TrackList tl = tracks(); - for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) + for (Track * t : tl) { - ( *it )->swapPositionOfTCOs( _bb1, _bb2 ); + t->swapPositionOfTCOs(bb1, bb2); } updateComboBox(); } @@ -148,11 +148,10 @@ void BBTrackContainer::swapBB( int _bb1, int _bb2 ) -void BBTrackContainer::updateBBTrack( TrackContentObject * _tco ) +void BBTrackContainer::updateBBTrack(TrackContentObject * tco) { - BBTrack * t = BBTrack::findBBTrack( _tco->startPosition() / - DefaultTicksPerBar ); - if( t != NULL ) + BBTrack * t = BBTrack::findBBTrack(tco->startPosition() / DefaultTicksPerBar); + if (t != NULL) { t->dataChanged(); } @@ -164,11 +163,11 @@ void BBTrackContainer::updateBBTrack( TrackContentObject * _tco ) void BBTrackContainer::fixIncorrectPositions() { TrackList tl = tracks(); - for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) + for (Track * t : tl) { - for( int i = 0; i < numOfBBs(); ++i ) + for (int i = 0; i < numOfBBs(); ++i) { - ( *it )->getTCO( i )->movePosition( TimePos( i, 0 ) ); + t->getTCO(i)->movePosition(TimePos(i, 0)); } } } @@ -178,7 +177,7 @@ void BBTrackContainer::fixIncorrectPositions() void BBTrackContainer::play() { - if( Engine::getSong()->playMode() != Song::Mode_PlayBB ) + if (Engine::getSong()->playMode() != Song::Mode_PlayBB) { Engine::getSong()->playBB(); } @@ -201,16 +200,16 @@ void BBTrackContainer::stop() void BBTrackContainer::updateComboBox() { - const int cur_bb = currentBB(); + const int curBB = currentBB(); m_bbComboBoxModel.clear(); - for( int i = 0; i < numOfBBs(); ++i ) + for (int i = 0; i < numOfBBs(); ++i) { - BBTrack * bbt = BBTrack::findBBTrack( i ); - m_bbComboBoxModel.addItem( bbt->name() ); + BBTrack * bbt = BBTrack::findBBTrack(i); + m_bbComboBoxModel.addItem(bbt->name()); } - setCurrentBB( cur_bb ); + setCurrentBB(curBB); } @@ -218,14 +217,13 @@ void BBTrackContainer::updateComboBox() void BBTrackContainer::currentBBChanged() { - // now update all track-labels (the current one has to become white, - // the others gray) + // now update all track-labels (the current one has to become white, the others gray) TrackList tl = Engine::getSong()->tracks(); - for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) + for (Track * t : tl) { - if( ( *it )->type() == Track::BBTrack ) + if (t->type() == Track::BBTrack) { - ( *it )->dataChanged(); + t->dataChanged(); } } } @@ -233,12 +231,12 @@ void BBTrackContainer::currentBBChanged() -void BBTrackContainer::createTCOsForBB( int _bb ) +void BBTrackContainer::createTCOsForBB(int bb) { TrackList tl = tracks(); - for( int i = 0; i < tl.size(); ++i ) + for (Track * t : tl) { - tl[i]->createTCOsForBB( _bb ); + t->createTCOsForBB(bb); } } @@ -247,11 +245,11 @@ AutomatedValueMap BBTrackContainer::automatedValuesAt(TimePos time, int tcoNum) Q_ASSERT(tcoNum >= 0); Q_ASSERT(time.getTicks() >= 0); - auto length_bars = lengthOfBB(tcoNum); - auto length_ticks = length_bars * TimePos::ticksPerBar(); - if (time > length_ticks) + auto lengthBars = lengthOfBB(tcoNum); + auto lengthTicks = lengthBars * TimePos::ticksPerBar(); + if (time > lengthTicks) { - time = length_ticks; + time = lengthTicks; } return TrackContainer::automatedValuesAt(time + (TimePos::ticksPerBar() * tcoNum), tcoNum); From d3cd704396d9d16417c8ec18e3c35a63c9a3c699 Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 8 Dec 2020 15:55:43 +0100 Subject: [PATCH 178/180] Temporary PR-Freeze message --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b1bea70a0c6..6b23673d746 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [![Join the chat at Discord](https://img.shields.io/badge/chat-on%20discord-7289DA.svg)](https://discord.gg/3sc5su7) [![Localise on transifex](https://img.shields.io/badge/localise-on_transifex-green.svg)](https://www.transifex.com/lmms/lmms/) +**A soft PR-Freeze is currently underway to prepare for refactoring ([#5592](https://github.com/LMMS/lmms/issues/5592)). Please do not open non-essential PRs at this time.** + What is LMMS? -------------- From 2cb797353b77303d77620d756489956d19a4751e Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Thu, 10 Dec 2020 03:44:29 +0100 Subject: [PATCH 179/180] Blacklist some calf plugins Plugins broken, crash on sound. See: https://github.com/calf-studio-gear/calf/issues/278 --- src/core/lv2/Lv2Manager.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 57f04f6808c..ed2d0505340 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -52,7 +52,15 @@ const std::set Lv2Manager::pluginBlacklist = { // github.com/calf-studio-gear/calf, #278 "http://calf.sourceforge.net/plugins/Analyzer", - "http://calf.sourceforge.net/plugins/BassEnhancer" + "http://calf.sourceforge.net/plugins/BassEnhancer", + "http://calf.sourceforge.net/plugins/CompensationDelay", + "http://calf.sourceforge.net/plugins/Crusher", + "http://calf.sourceforge.net/plugins/Exciter", + "http://calf.sourceforge.net/plugins/Saturator", + "http://calf.sourceforge.net/plugins/StereoTools", + "http://calf.sourceforge.net/plugins/TapeSimulator", + "http://calf.sourceforge.net/plugins/TransientDesigner", + "http://calf.sourceforge.net/plugins/Vinyl" }; From 28ee260e2813e1ea368e914b4b111139d72ba278 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 10 Dec 2020 22:46:03 -0300 Subject: [PATCH 180/180] Mixer refactor (#4894) Co-authored-by: Kevin Zander --- include/Mixer.h | 51 ++++++----- src/core/Mixer.cpp | 209 +++++++++++++++++++++++---------------------- 2 files changed, 131 insertions(+), 129 deletions(-) diff --git a/include/Mixer.h b/include/Mixer.h index d9b60d188f5..c561aa3c94d 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -98,9 +98,9 @@ class LMMS_EXPORT Mixer : public QObject Interpolation interpolation; Oversampling oversampling; - qualitySettings( Mode _m ) + qualitySettings(Mode m) { - switch( _m ) + switch (m) { case Mode_Draft: interpolation = Interpolation_Linear; @@ -118,9 +118,9 @@ class LMMS_EXPORT Mixer : public QObject } } - qualitySettings( Interpolation _i, Oversampling _o ) : - interpolation( _i ), - oversampling( _o ) + qualitySettings(Interpolation i, Oversampling o) : + interpolation(i), + oversampling(o) { } @@ -186,14 +186,14 @@ class LMMS_EXPORT Mixer : public QObject // audio-port-stuff - inline void addAudioPort( AudioPort * _port ) + inline void addAudioPort(AudioPort * port) { requestChangeInModel(); - m_audioPorts.push_back( _port ); + m_audioPorts.push_back(port); doneChangeInModel(); } - void removeAudioPort( AudioPort * _port ); + void removeAudioPort(AudioPort * port); // MIDI-client-stuff @@ -218,7 +218,7 @@ class LMMS_EXPORT Mixer : public QObject return m_playHandles; } - void removePlayHandlesOfTypes( Track * _track, const quint8 types ); + void removePlayHandlesOfTypes(Track * track, const quint8 types); // methods providing information for other classes @@ -255,23 +255,23 @@ class LMMS_EXPORT Mixer : public QObject return m_masterGain; } - inline void setMasterGain( const float _mo ) + inline void setMasterGain(const float mo) { - m_masterGain = _mo; + m_masterGain = mo; } - static inline sample_t clip( const sample_t _s ) + static inline sample_t clip(const sample_t s) { - if( _s > 1.0f ) + if (s > 1.0f) { return 1.0f; } - else if( _s < -1.0f ) + else if (s < -1.0f) { return -1.0f; } - return _s; + return s; } @@ -281,7 +281,7 @@ class LMMS_EXPORT Mixer : public QObject sample_t left; sample_t right; }; - StereoSample getPeakValues(sampleFrame * _ab, const f_cnt_t _frames) const; + StereoSample getPeakValues(sampleFrame * ab, const f_cnt_t _frames) const; bool criticalXRuns() const; @@ -308,7 +308,7 @@ class LMMS_EXPORT Mixer : public QObject return hasFifoWriter() ? m_fifo->read() : renderNextBuffer(); } - void changeQuality( const struct qualitySettings & _qs ); + void changeQuality(const struct qualitySettings & qs); inline bool isMetronomeActive() const { return m_metronomeActive; } inline void setMetronomeActive(bool value = true) { m_metronomeActive = value; } @@ -333,7 +333,7 @@ class LMMS_EXPORT Mixer : public QObject class fifoWriter : public QThread { public: - fifoWriter( Mixer * _mixer, fifo * _fifo ); + fifoWriter(Mixer * mixer, fifo * _fifo); void finish(); @@ -353,7 +353,7 @@ class LMMS_EXPORT Mixer : public QObject Mixer( bool renderOnly ); virtual ~Mixer(); - void startProcessing( bool _needs_fifo = true ); + void startProcessing(bool needsFifo = true); void stopProcessing(); @@ -363,6 +363,10 @@ class LMMS_EXPORT Mixer : public QObject const surroundSampleFrame * renderNextBuffer(); + void swapBuffers(); + + void handleMetronome(); + void clearInternal(); //! Called by the audio thread to give control to other threads, @@ -381,13 +385,8 @@ class LMMS_EXPORT Mixer : public QObject int m_inputBufferRead; int m_inputBufferWrite; - surroundSampleFrame * m_readBuf; - surroundSampleFrame * m_writeBuf; - - QVector m_bufferPool; - int m_readBuffer; - int m_writeBuffer; - int m_poolDepth; + surroundSampleFrame * m_outputBufferRead; + surroundSampleFrame * m_outputBufferWrite; // worker thread stuff QVector m_workers; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 573951ea966..4e113aa2e46 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -74,8 +74,8 @@ Mixer::Mixer( bool renderOnly ) : m_framesPerPeriod( DEFAULT_BUFFER_SIZE ), m_inputBufferRead( 0 ), m_inputBufferWrite( 1 ), - m_readBuf( NULL ), - m_writeBuf( NULL ), + m_outputBufferRead(nullptr), + m_outputBufferWrite(nullptr), m_workers(), m_numWorkers( QThread::idealThreadCount()-1 ), m_newPlayHandles( PlayHandle::MaxNumber ), @@ -136,15 +136,12 @@ Mixer::Mixer( bool renderOnly ) : // now that framesPerPeriod is fixed initialize global BufferManager BufferManager::init( m_framesPerPeriod ); - for( int i = 0; i < 3; i++ ) - { - m_readBuf = (surroundSampleFrame*) - MemoryHelper::alignedMalloc( m_framesPerPeriod * - sizeof( surroundSampleFrame ) ); + int outputBufferSize = m_framesPerPeriod * sizeof(surroundSampleFrame); + m_outputBufferRead = static_cast(MemoryHelper::alignedMalloc(outputBufferSize)); + m_outputBufferWrite = static_cast(MemoryHelper::alignedMalloc(outputBufferSize)); - BufferManager::clear( m_readBuf, m_framesPerPeriod ); - m_bufferPool.push_back( m_readBuf ); - } + BufferManager::clear(m_outputBufferRead, m_framesPerPeriod); + BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod); for( int i = 0; i < m_numWorkers+1; ++i ) { @@ -155,10 +152,6 @@ Mixer::Mixer( bool renderOnly ) : } m_workers.push_back( wt ); } - - m_poolDepth = 2; - m_readBuffer = 0; - m_writeBuffer = 1; } @@ -189,10 +182,8 @@ Mixer::~Mixer() delete m_midiClient; delete m_audioDev; - for( int i = 0; i < 3; i++ ) - { - MemoryHelper::alignedFree( m_bufferPool[i] ); - } + MemoryHelper::alignedFree(m_outputBufferRead); + MemoryHelper::alignedFree(m_outputBufferWrite); for( int i = 0; i < 2; ++i ) { @@ -220,9 +211,9 @@ void Mixer::initDevices() -void Mixer::startProcessing( bool _needs_fifo ) +void Mixer::startProcessing(bool needsFifo) { - if( _needs_fifo ) + if (needsFifo) { m_fifoWriter = new fifoWriter( this, m_fifo ); m_fifoWriter->start( QThread::HighPriority ); @@ -345,42 +336,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer() s_renderingThread = true; - static Song::PlayPos last_metro_pos = -1; - - Song *song = Engine::getSong(); - - Song::PlayModes currentPlayMode = song->playMode(); - Song::PlayPos p = song->getPlayPos( currentPlayMode ); - - bool playModeSupportsMetronome = currentPlayMode == Song::Mode_PlayPattern || - currentPlayMode == Song::Mode_PlaySong || - currentPlayMode == Song::Mode_PlayBB; - - if( playModeSupportsMetronome && m_metronomeActive && !song->isExporting() && - !song->isPaused() && p != last_metro_pos && - // Stop crash with metronome if empty project - Engine::getSong()->countTracks() ) - { - tick_t ticksPerBar = TimePos::ticksPerBar(); - if ( p.getTicks() % ( ticksPerBar / 1 ) == 0 ) - { - addPlayHandle( new SamplePlayHandle( "misc/metronome02.ogg" ) ); - } - else if ( p.getTicks() % ( ticksPerBar / - song->getTimeSigModel().getNumerator() ) == 0 ) - { - addPlayHandle( new SamplePlayHandle( "misc/metronome01.ogg" ) ); - } - last_metro_pos = p; - } - - // swap buffer - m_inputBufferWrite = ( m_inputBufferWrite + 1 ) % 2; - m_inputBufferRead = ( m_inputBufferRead + 1 ) % 2; - - // clear new write buffer - m_inputBufferFrames[ m_inputBufferWrite ] = 0; - if( m_clearSignal ) { m_clearSignal = false; @@ -409,22 +364,16 @@ const surroundSampleFrame * Mixer::renderNextBuffer() it_rem = m_playHandlesToRemove.erase( it_rem ); } - // rotate buffers - m_writeBuffer = ( m_writeBuffer + 1 ) % m_poolDepth; - m_readBuffer = ( m_readBuffer + 1 ) % m_poolDepth; - - m_writeBuf = m_bufferPool[m_writeBuffer]; - m_readBuf = m_bufferPool[m_readBuffer]; - - // clear last audio-buffer - BufferManager::clear( m_writeBuf, m_framesPerPeriod ); + swapBuffers(); // prepare master mix (clear internal buffers etc.) FxMixer * fxMixer = Engine::fxMixer(); fxMixer->prepareMasterMix(); + handleMetronome(); + // create play-handles for new notes, samples etc. - song->processNextBuffer(); + Engine::getSong()->processNextBuffer(); // add all play-handles that have to be added for( LocklessListElement * e = m_newPlayHandles.popList(); e; ) @@ -471,10 +420,10 @@ const surroundSampleFrame * Mixer::renderNextBuffer() // STAGE 3: do master mix in FX mixer - fxMixer->masterMix( m_writeBuf ); + fxMixer->masterMix(m_outputBufferWrite); - emit nextAudioBuffer( m_readBuf ); + emit nextAudioBuffer(m_outputBufferRead); runChangesInModel(); @@ -487,12 +436,71 @@ const surroundSampleFrame * Mixer::renderNextBuffer() m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod ); - return m_readBuf; + return m_outputBufferRead; } +void Mixer::swapBuffers() +{ + m_inputBufferWrite = (m_inputBufferWrite + 1) % 2; + m_inputBufferRead = (m_inputBufferRead + 1) % 2; + m_inputBufferFrames[m_inputBufferWrite] = 0; + + std::swap(m_outputBufferRead, m_outputBufferWrite); + BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod); +} + + + + +void Mixer::handleMetronome() +{ + static tick_t lastMetroTicks = -1; + + Song * song = Engine::getSong(); + Song::PlayModes currentPlayMode = song->playMode(); + + bool metronomeSupported = + currentPlayMode == Song::Mode_PlayPattern + || currentPlayMode == Song::Mode_PlaySong + || currentPlayMode == Song::Mode_PlayBB; + + if (!metronomeSupported || !m_metronomeActive || song->isExporting()) + { + return; + } + + // stop crash with metronome if empty project + if (song->countTracks() == 0) + { + return; + } + + tick_t ticks = song->getPlayPos(currentPlayMode).getTicks(); + tick_t ticksPerBar = TimePos::ticksPerBar(); + int numerator = song->getTimeSigModel().getNumerator(); + + if (ticks == lastMetroTicks) + { + return; + } + + if (ticks % (ticksPerBar / 1) == 0) + { + addPlayHandle(new SamplePlayHandle("misc/metronome02.ogg")); + } + else if (ticks % (ticksPerBar / numerator) == 0) + { + addPlayHandle(new SamplePlayHandle("misc/metronome01.ogg")); + } + + lastMetroTicks = ticks; +} + + + void Mixer::clear() { m_clearSignal = true; @@ -520,29 +528,28 @@ void Mixer::clearNewPlayHandles() void Mixer::clearInternal() { // TODO: m_midiClient->noteOffAll(); - for( PlayHandleList::Iterator it = m_playHandles.begin(); it != m_playHandles.end(); ++it ) + for (auto ph : m_playHandles) { - // we must not delete instrument-play-handles as they exist - // during the whole lifetime of an instrument - if( ( *it )->type() != PlayHandle::TypeInstrumentPlayHandle ) + if (ph->type() != PlayHandle::TypeInstrumentPlayHandle) { - m_playHandlesToRemove.push_back( *it ); + m_playHandlesToRemove.push_back(ph); } + } } -Mixer::StereoSample Mixer::getPeakValues(sampleFrame * _ab, const f_cnt_t _frames) const +Mixer::StereoSample Mixer::getPeakValues(sampleFrame * ab, const f_cnt_t frames) const { sample_t peakLeft = 0.0f; sample_t peakRight = 0.0f; - for( f_cnt_t f = 0; f < _frames; ++f ) + for (f_cnt_t f = 0; f < frames; ++f) { - float const absLeft = qAbs( _ab[f][0] ); - float const absRight = qAbs( _ab[f][1] ); + float const absLeft = qAbs(ab[f][0]); + float const absRight = qAbs(ab[f][1]); if (absLeft > peakLeft) { peakLeft = absLeft; @@ -560,12 +567,12 @@ Mixer::StereoSample Mixer::getPeakValues(sampleFrame * _ab, const f_cnt_t _frame -void Mixer::changeQuality( const struct qualitySettings & _qs ) +void Mixer::changeQuality(const struct qualitySettings & qs) { // don't delete the audio-device stopProcessing(); - m_qualitySettings = _qs; + m_qualitySettings = qs; m_audioDev->applyQualitySettings(); emit sampleRateChanged(); @@ -649,15 +656,14 @@ void Mixer::restoreAudioDevice() -void Mixer::removeAudioPort( AudioPort * _port ) +void Mixer::removeAudioPort(AudioPort * port) { requestChangeInModel(); - QVector::Iterator it = std::find( m_audioPorts.begin(), - m_audioPorts.end(), - _port ); - if( it != m_audioPorts.end() ) + + QVector::Iterator it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port); + if (it != m_audioPorts.end()) { - m_audioPorts.erase( it ); + m_audioPorts.erase(it); } doneChangeInModel(); } @@ -682,22 +688,21 @@ bool Mixer::addPlayHandle( PlayHandle* handle ) } -void Mixer::removePlayHandle( PlayHandle * _ph ) +void Mixer::removePlayHandle(PlayHandle * ph) { requestChangeInModel(); // check thread affinity as we must not delete play-handles // which were created in a thread different than mixer thread - if( _ph->affinityMatters() && - _ph->affinity() == QThread::currentThread() ) + if (ph->affinityMatters() && ph->affinity() == QThread::currentThread()) { - _ph->audioPort()->removePlayHandle( _ph ); + ph->audioPort()->removePlayHandle(ph); bool removedFromList = false; // Check m_newPlayHandles first because doing it the other way around // creates a race condition for( LocklessListElement * e = m_newPlayHandles.first(), * ePrev = NULL; e; ePrev = e, e = e->next ) { - if( e->value == _ph ) + if (e->value == ph) { if( ePrev ) { @@ -713,11 +718,10 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) } } // Now check m_playHandles - PlayHandleList::Iterator it = std::find( m_playHandles.begin(), - m_playHandles.end(), _ph ); - if( it != m_playHandles.end() ) + PlayHandleList::Iterator it = std::find(m_playHandles.begin(), m_playHandles.end(), ph); + if (it != m_playHandles.end()) { - m_playHandles.erase( it ); + m_playHandles.erase(it); removedFromList = true; } // Only deleting PlayHandles that were actually found in the list @@ -725,16 +729,16 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) // (See tobydox's 2008 commit 4583e48) if ( removedFromList ) { - if( _ph->type() == PlayHandle::TypeNotePlayHandle ) + if (ph->type() == PlayHandle::TypeNotePlayHandle) { - NotePlayHandleManager::release( (NotePlayHandle*) _ph ); + NotePlayHandleManager::release(dynamic_cast(ph)); } - else delete _ph; + else { delete ph; } } } else { - m_playHandlesToRemove.push_back( _ph ); + m_playHandlesToRemove.push_back(ph); } doneChangeInModel(); } @@ -742,13 +746,13 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) -void Mixer::removePlayHandlesOfTypes( Track * _track, const quint8 types ) +void Mixer::removePlayHandlesOfTypes(Track * track, const quint8 types) { requestChangeInModel(); PlayHandleList::Iterator it = m_playHandles.begin(); while( it != m_playHandles.end() ) { - if( ( *it )->isFromTrack( _track ) && ( ( *it )->type() & types ) ) + if ((*it)->isFromTrack(track) && ((*it)->type() & types)) { ( *it )->audioPort()->removePlayHandle( ( *it ) ); if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) @@ -780,7 +784,7 @@ void Mixer::requestChangeInModel() m_doChangesMutex.lock(); m_waitChangesMutex.lock(); - if ( m_isProcessing && !m_waitingForWrite && !m_changesSignal ) + if (m_isProcessing && !m_waitingForWrite && !m_changesSignal) { m_changesSignal = true; m_changesRequestCondition.wait( &m_waitChangesMutex ); @@ -1282,4 +1286,3 @@ void Mixer::fifoWriter::write( surroundSampleFrame * buffer ) m_mixer->m_waitingForWrite = false; m_mixer->m_doChangesMutex.unlock(); } -