diff --git a/ChangeLog b/ChangeLog index 5021e1d74..876ee30cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,82 @@ +2005-12-11 Tobias Doerffel + + * src/core/lmms_main_win.cpp: + * src/lib/mmp.cpp: + - default file-extension is now "mmp" (MultiMedia Project) instead of + "xml" for being able to associate mmp-files with LMMS in + file-managers etc. - futhermore LMMS-file-browser is much faster now + because it does not have to examine each file + - file-extension for song-templates is now "mpt" (MultiMedia Project + Template) + + * include/cpuload_widget.h: + * src/widgets/cpuload_widget.cpp: + added cool CPU-load-widget displaying LMMS's current CPU-usage + + * src/core/note_play_handle.cpp: + always check validity of sub-notes as they might not be known to mixer + and therefore not invalidated in certain situations which made LMMS + crashing (e.g. when deleting a channel-track which was current playing + some arpeggio-notes) + + * include/pattern.h: + * src/tracks/pattern.cpp: + start separate thread for freezing pattern as this doesn't block the + GUI-thread and user is now able to cancel pattern-freezing (which was + not possible after last rewrite a few days ago) + + * include/timeline.h: + * src/core/timeline.cpp: + use timer for periodically screen-updates - locking QApplication-mutex + isn't neccessary anymore which makes LMMS not to hang when creating + new song while playing etc. + + * src/core/piano_roll.cpp: + when settings time-line-position, call setTact( 0 ) and + setTact64th( 0 )-members instead of pos() = 0 (the latter one leads + to crashes) + + * plugins/audio_file_processor/audio_file_processor.cpp: + update start- and end-frames of sample-buffer when loading settings + +2005-12-10 Tobias Doerffel + + * include/lmms_main_win.h: + * include/song_editor.h: + * src/core/lmms_main_win.cpp: + * src/core/song_editor.cpp: + * resources/toolbar_bg.png: + * resources/main_toolbar_bg.png: + heavy improvements on toolbars + + * include/nstate_button.h: + * src/widgets/nstate_button.cpp: + base-class is now toolButton instead of QPushButton + + * include/tool_button.h: + * src/widgets/tool_button.cpp: + - moved implemenation of toolButton into cpp-file + - added highlighting during mouse-over + - do not connect clicked()-signal if receiver and/or slot are NULL + +2005-12-09 Tobias Doerffel + + * include/channel_track.h: + * src/core/midi_tab_widget.cpp: + * src/core/preset_preview_play_handle.cpp: + added support for saving and restoring MIDI-connections of a channel + in ordinary channel-preset-data + + * include/channel_track.h: + * include/midi_tab_widget.h: + * src/core/midi_tab_widget.cpp: + * src/tracks/channel_track.cpp: + dropped support for switchable MIDI-event-routing as it is done per + default and actually makes no sense... + + * src/midi/midi_port.cpp: + do not process MIDI-out-event if channel = -1 + 2005-12-08 Tobias Doerffel * src/core/midi_tab_widget.cpp: diff --git a/Makefile.am b/Makefile.am index 1d7dc8a12..a26130e7f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ lmms_MOC = \ ./bb_track.moc \ ./channel_track.moc \ ./config_mgr.moc \ + ./cpuload_widget.moc \ ./envelope_and_lfo_widget.moc \ ./envelope_tab_widget.moc \ ./export_project_dialog.moc \ @@ -156,6 +157,7 @@ lmms_SOURCES = \ $(srcdir)/src/tracks/channel_track.cpp \ $(srcdir)/src/tracks/pattern.cpp \ $(srcdir)/src/tracks/sample_track.cpp \ + $(srcdir)/src/widgets/cpuload_widget.cpp \ $(srcdir)/src/widgets/group_box.cpp \ $(srcdir)/src/widgets/kmultitabbar.cpp \ $(srcdir)/src/widgets/knob.cpp \ @@ -171,6 +173,7 @@ lmms_SOURCES = \ $(srcdir)/src/widgets/tab_widget.cpp \ $(srcdir)/src/widgets/text_float.cpp \ $(srcdir)/src/widgets/tempo_sync_knob.cpp \ + $(srcdir)/src/widgets/tool_button.cpp \ $(srcdir)/src/widgets/tooltip.cpp \ $(srcdir)/src/widgets/visualization_widget.cpp \ $(srcdir)/include/pch.h \ @@ -272,6 +275,7 @@ lmms_SOURCES = \ $(srcdir)/include/audio_port.h \ $(srcdir)/include/qxembed.h \ $(srcdir)/include/tool_button.h \ + $(srcdir)/include/cpuload_widget.h \ $(srcdir)/include/midi_alsa_seq.h diff --git a/TODO b/TODO index a6f1c3e75..221aeab00 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,7 @@ to be done as soon as possible: - do not allow to connect output-port of channel to own input-port! -- save connections in midi-tab-widget - add note-len- and note-alignment-selectbox to piano-roll -- make it possible in bb-editor to add single beats to beat-patterns - fix audio/midi-settings stuff/translation - tooltips for controls in MIDI-tab - sample-track: sane bg and wave-color diff --git a/configure.in b/configure.in index 228710dbd..fbdd38840 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.1.1-cvs20051208, tobydox/at/users.sourceforge.net) -AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051208) +AC_INIT(lmms, 0.1.1-cvs20051211, tobydox/at/users.sourceforge.net) +AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051211) AM_CONFIG_HEADER(config.h) @@ -383,7 +383,6 @@ AC_CONFIG_FILES([Makefile plugins/vestige/Makefile presets/Makefile presets/AudioFileProcessor/Makefile - presets/MIDI-Out/Makefile presets/PluckedStringSynth/Makefile presets/TripleOscillator/Makefile presets/VeSTige/Makefile diff --git a/include/cpuload_widget.h b/include/cpuload_widget.h new file mode 100644 index 000000000..42ce339c4 --- /dev/null +++ b/include/cpuload_widget.h @@ -0,0 +1,79 @@ +/* + * cpuload_widget.h - widget for displaying CPU-load (partly based on + * Hydrogen's CPU-load-widget) + * + * Copyright (c) 2005 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _CPULOAD_WIDGET_H +#define _CPULOAD_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + +#include "types.h" + + +class cpuloadWidget : public QWidget +{ + Q_OBJECT +public: + cpuloadWidget( QWidget * _parent ); + ~cpuloadWidget(); + + +protected: + void paintEvent( QPaintEvent * _ev ); + + +protected slots: + void updateCpuLoad(); + + +private: + Uint8 m_currentLoad; + + QPixmap m_temp; + QPixmap m_background; + QPixmap m_leds; + + bool m_changed; + + QTimer m_updateTimer; + +} ; + + +#endif diff --git a/include/midi_tab_widget.h b/include/midi_tab_widget.h index 8bf3e5eb0..076643b1a 100644 --- a/include/midi_tab_widget.h +++ b/include/midi_tab_widget.h @@ -92,7 +92,7 @@ private: lcdSpinBox * m_outputChannelSpinBox; ledCheckBox * m_receiveCheckBox; ledCheckBox * m_sendCheckBox; - ledCheckBox * m_routeCheckBox; + QMenu * m_readablePorts; QMenu * m_writeablePorts; diff --git a/include/mixer.h b/include/mixer.h index 8c6c31238..d5719d407 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -125,6 +125,10 @@ public: return( m_framesPerAudioBuffer ); } + inline Uint8 cpuLoad( void ) const + { + return( m_cpuLoad ); + } inline bool highQuality( void ) const { @@ -295,16 +299,13 @@ private: surroundSampleFrame * m_curBuf; surroundSampleFrame * m_nextBuf; -/* bool m_discardCurBuf;*/ - + Uint8 m_cpuLoad; playHandleVector m_playHandles; playHandleVector m_playHandlesToRemove; Uint8 m_qualityLevel; - volatile float m_masterGain; - -/* volatile bool m_quit;*/ + float m_masterGain; audioDevice * m_audioDev; @@ -317,7 +318,6 @@ private: QMutex m_mixMutex; -/* QMutex m_devMutex;*/ friend class lmmsMainWin; diff --git a/include/mmp.h b/include/mmp.h index 6ce08a14d..690936d7a 100644 --- a/include/mmp.h +++ b/include/mmp.h @@ -49,6 +49,7 @@ public: { UNKNOWN, SONG_PROJECT, + SONG_PROJECT_TEMPLATE, CHANNEL_SETTINGS, EFFECT_SETTINGS, VIDEO_PROJECT, // will come later... diff --git a/include/nstate_button.h b/include/nstate_button.h index 7e2a60db5..b27e81f0d 100644 --- a/include/nstate_button.h +++ b/include/nstate_button.h @@ -34,22 +34,21 @@ #ifdef QT4 -#include #include #include #include #else -#include #include #include #include #endif +#include "tool_button.h" -class nStateButton : public QPushButton +class nStateButton : public toolButton { Q_OBJECT public: @@ -77,7 +76,6 @@ signals: protected: -/* virtual void paintEvent( QPaintEvent * _pe );*/ virtual void mousePressEvent( QMouseEvent * _me ); diff --git a/include/pattern.h b/include/pattern.h index 130cb9248..2dd27eed4 100644 --- a/include/pattern.h +++ b/include/pattern.h @@ -35,6 +35,7 @@ #include #include #include +#include #else @@ -42,6 +43,7 @@ #include #include #include +#include #endif @@ -51,16 +53,18 @@ #include "mixer.h" -class channelTrack; -class sampleBuffer; class QProgressBar; class QPushButton; class QPixmap; +class channelTrack; +class patternFreezeThread; +class sampleBuffer; -const int MAX_BEATS_PER_TACT = 16; -const int MAIN_BEATS_PER_TACT = 4; +const int DEFAULT_STEPS_PER_TACT = 16; +const int BEATS_PER_TACT = 4; + class pattern : public trackContentObject @@ -76,6 +80,9 @@ public: pattern( const pattern & _pat_to_copy ) FASTCALL; virtual ~pattern(); + void init( void ); + + virtual void FASTCALL movePosition( const midiTime & _pos ); @@ -130,6 +137,8 @@ public: return( m_frozenPattern != NULL ); } + // if channel-track recognizes that this pattern is frozen, it calls + // this instead of playing all the notes void FASTCALL playFrozenData( sampleFrame * _ab, Uint32 _start_frame, Uint32 _frames ); @@ -150,6 +159,7 @@ public: protected slots: void openInPianoRoll( bool _c ); void openInPianoRoll( void ); + void clear( void ); void resetName( void ); void changeName( void ); @@ -157,6 +167,9 @@ protected slots: void unfreeze( void ); void abortFreeze( void ); + void addSteps( int _n ); + void removeSteps( int _n ); + protected: void paintEvent( QPaintEvent * _pe ); @@ -174,19 +187,30 @@ private: static QPixmap * s_stepBtnOffLight; static QPixmap * s_frozen; - static void initPixmaps( void ); - + // general stuff channelTrack * m_channelTrack; + patternTypes m_patternType; QString m_name; - noteVector m_notes; + // data-stuff + noteVector m_notes; + int m_steps; + + // pattern freezing QMutex m_frozenPatternMutex; sampleBuffer * m_frozenPattern; bool m_freezing; volatile bool m_freezeAborted; + + // as in Qt4 QThread is inherits from QObject and our base + // trackContentObject is a QWidget (=QObject), we cannot inherit from + // QThread. That's why we have to put pattern-freezing into separate + // thread-class -> patternFreezeThread + friend class patternFreezeThread; + } ; @@ -196,7 +220,7 @@ class patternFreezeStatusDialog : public QDialog { Q_OBJECT public: - patternFreezeStatusDialog(); + patternFreezeStatusDialog( QThread * _thread ); ~patternFreezeStatusDialog(); void FASTCALL setProgress( int _p ); @@ -208,12 +232,17 @@ protected: protected slots: void cancelBtnClicked( void ); + void updateProgress( void ); private: QProgressBar * m_progressBar; QPushButton * m_cancelBtn; + QThread * m_freezeThread; + + int m_progress; + signals: void aborted( void ); @@ -221,4 +250,25 @@ signals: } ; + + + +class patternFreezeThread : public QThread +{ +public: + patternFreezeThread( pattern * _pattern ); + virtual ~patternFreezeThread(); + + +protected: + virtual void run( void ); + + +private: + pattern * m_pattern; + patternFreezeStatusDialog * m_statusDlg; + +} ; + + #endif diff --git a/include/song_editor.h b/include/song_editor.h index d4d3e3131..c21aebf75 100644 --- a/include/song_editor.h +++ b/include/song_editor.h @@ -56,7 +56,6 @@ class pattern; class projectNotes; class timeLine; class toolButton; -class visualizationWidget; const int MIN_BPM = 10; @@ -283,8 +282,6 @@ private: QSlider * m_masterVolumeSlider; QSlider * m_masterPitchSlider; - visualizationWidget * m_masterOutputGraph; - toolButton * m_addBBTrackButton; toolButton * m_addSampleTrackButton; diff --git a/include/timeline.h b/include/timeline.h index d3d602eb6..fb8d8939d 100644 --- a/include/timeline.h +++ b/include/timeline.h @@ -119,6 +119,7 @@ public slots: void toggleAutoScroll( int _n ); void toggleLoopPoints( int _n ); void toggleBehaviourAtStop( int _n ); + void checkForUpdatedPosition( void ); protected: @@ -144,6 +145,8 @@ private: loopPointStates m_loopPoints; behaviourAtStopStates m_behaviourAtStop; + bool m_changedPosition; + int m_xOffset; int m_posMarkerX; float m_ppt; diff --git a/include/tool_button.h b/include/tool_button.h index 30b3344b6..3c99ad6de 100644 --- a/include/tool_button.h +++ b/include/tool_button.h @@ -26,44 +26,60 @@ #ifndef _TOOL_BUTTON_H #define _TOOL_BUTTON_H -#ifdef HAVE_CONFIG_H -#include -#endif - #include "qt3support.h" #ifdef QT4 -#include +#include +#include #else -#include +#include +#include #endif -#include "tooltip.h" - -class toolButton : public QPushButton +class toolButton : public QToolButton { public: toolButton( const QPixmap & _pixmap, const QString & _tooltip, QObject * _receiver, const char * _slot, - QWidget * _parent ) : - QPushButton( _parent ) + QWidget * _parent ); + + inline toolButton( QWidget * _parent ) : + QToolButton( _parent ), + m_colorStandard( s_stdColor ), + m_colorHighlighted( s_hlColor ) { - connect( this, SIGNAL( clicked() ), _receiver, _slot ); - toolTip::add( this, _tooltip ); - setPaletteBackgroundColor( QColor( 224, 224, 224 ) ); - setFixedSize( 30, 30 ); - setPixmap( _pixmap ); } - ~toolButton() + ~toolButton(); + + inline void setStandardColor( const QColor & _color ) { + m_colorStandard = _color; } + inline void setHighlightedColor( const QColor & _color ) + { + m_colorHighlighted = _color; + } + + +protected: + virtual void enterEvent( QEvent * _ev ); + virtual void leaveEvent( QEvent * _ev ); + + +private: + static const QColor s_stdColor; + static const QColor s_hlColor; + + QColor m_colorStandard; + QColor m_colorHighlighted; + } ; #endif diff --git a/plugins/Makefile.am b/plugins/Makefile.am index e689b77d3..8f5fbb81a 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -2,6 +2,6 @@ if VST_SUPPORT VESTIGE_SUBDIR=vestige endif -SUBDIRS = audio_file_processor midi_out plucked_string_synth triple_oscillator $(VESTIGE_SUBDIR) +SUBDIRS = audio_file_processor plucked_string_synth triple_oscillator $(VESTIGE_SUBDIR) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 2ca187420..5865cc4dc 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -487,7 +487,7 @@ void audioFileProcessor::sampleUpdated( void ) { m_graph = QPixmap( 245, 75 ); copyBlt( &m_graph, 0, 0, s_artwork, 2, 172, m_graph.width(), - m_graph.height() ); + m_graph.height() ); QPainter p( &m_graph ); m_sampleBuffer.drawWaves( p, QRect( 2, 2, m_graph.width() - 4, m_graph.height() - 4 ), @@ -541,20 +541,20 @@ void audioFileProcessor::ampKnobChanged( float _val ) void audioFileProcessor::setStartAndEndKnob( float _s, float _e ) { - // because the signal-handlers of valuechanges of start- and end-knob +/* // because the signal-handlers of valuechanges of start- and end-knob // do range checking, depending on value of the other knob, we have to // disconnect the signal-handlers, set then the values, connect again // and then let the changes take effect... m_startKnob->disconnect(); - m_endKnob->disconnect(); + m_endKnob->disconnect();*/ m_startKnob->setValue( _s ); m_endKnob->setValue( _e ); - connect( m_startKnob, SIGNAL( valueChanged( float ) ), this, +/* connect( m_startKnob, SIGNAL( valueChanged( float ) ), this, SLOT( startKnobChanged( float ) ) ); connect( m_endKnob, SIGNAL( valueChanged( float ) ), this, - SLOT( endKnobChanged( float ) ) ); - m_startKnob->setValue( _s ); - m_endKnob->setValue( _e ); + SLOT( endKnobChanged( float ) ) );*/ + startKnobChanged( _s ); + endKnobChanged( _e ); } diff --git a/resources/cpuload_bg.png b/resources/cpuload_bg.png new file mode 100644 index 000000000..fafb19320 Binary files /dev/null and b/resources/cpuload_bg.png differ diff --git a/resources/cpuload_leds.png b/resources/cpuload_leds.png new file mode 100644 index 000000000..232080a61 Binary files /dev/null and b/resources/cpuload_leds.png differ diff --git a/resources/main_toolbar_bg.png b/resources/main_toolbar_bg.png new file mode 100644 index 000000000..070f0bcd6 Binary files /dev/null and b/resources/main_toolbar_bg.png differ diff --git a/resources/toolbar_bg.png b/resources/toolbar_bg.png index 25ec99d72..dc9476bd7 100644 Binary files a/resources/toolbar_bg.png and b/resources/toolbar_bg.png differ diff --git a/src/audio/audio_alsa.cpp b/src/audio/audio_alsa.cpp index 6a78c218b..641630f14 100644 --- a/src/audio/audio_alsa.cpp +++ b/src/audio/audio_alsa.cpp @@ -193,7 +193,7 @@ void audioALSA::stopProcessing( void ) if( running() ) { m_quit = TRUE; - wait( 500 ); + wait( 1000 ); terminate(); } } diff --git a/src/audio/audio_oss.cpp b/src/audio/audio_oss.cpp index f86a5b31e..3686b8c3c 100644 --- a/src/audio/audio_oss.cpp +++ b/src/audio/audio_oss.cpp @@ -296,7 +296,7 @@ void audioOSS::stopProcessing( void ) if( running() ) { m_quit = TRUE; - wait( 500 ); + wait( 1000 ); terminate(); } } diff --git a/src/core/file_browser.cpp b/src/core/file_browser.cpp index 85f3dc878..cb9d6027a 100644 --- a/src/core/file_browser.cpp +++ b/src/core/file_browser.cpp @@ -1,5 +1,6 @@ /* - * file_browser.cpp - implementation of the project-, preset- and sample-file-browser + * file_browser.cpp - implementation of the project-, preset- and + * sample-file-browser * * Copyright (c) 2004-2005 Tobias Doerffel * @@ -718,7 +719,7 @@ void fileItem::determineFileType( void ) #else QString ext = QFileInfo( fullName() ).extension( FALSE ).toLower(); #endif - if( ext == "mmp" ) + if( ext == "mmp" || ext == "mpt" ) { m_type = SONG_FILE; } diff --git a/src/core/midi_tab_widget.cpp b/src/core/midi_tab_widget.cpp index 3499bd3b1..6af44b0f7 100644 --- a/src/core/midi_tab_widget.cpp +++ b/src/core/midi_tab_widget.cpp @@ -67,7 +67,7 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track, { m_setupTabWidget = new tabWidget( tr( "MIDI-SETUP FOR THIS CHANNEL" ), this ); - m_setupTabWidget->setGeometry( 4, 5, 238, 180 ); + m_setupTabWidget->setGeometry( 4, 5, 238, 160 ); m_inputChannelSpinBox = new lcdSpinBox( 0, MIDI_CHANNEL_COUNT, 3, @@ -107,14 +107,6 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track, m_outputChannelSpinBox, SLOT( setEnabled( bool ) ) ); - m_routeCheckBox = new ledCheckBox( tr( "SEND RECEIVED MIDI-EVENTS" ), - m_setupTabWidget ); - m_routeCheckBox->setChecked( - m_channelTrack->midiEventRoutingEnabled() ); - m_routeCheckBox->move( 10, 150 ); - connect( m_sendCheckBox, SIGNAL( toggled( bool ) ), - m_channelTrack, SLOT( toggleMidiEventRouting( bool ) ) ); - midiPort::modes m = m_midiPort->mode(); m_receiveCheckBox->setChecked( m == midiPort::INPUT || m == midiPort::DUPLEX ); @@ -183,8 +175,44 @@ void midiTabWidget::saveSettings( QDomDocument & _doc, QDomElement & _parent ) m_receiveCheckBox->isChecked() ) ); mw_de.setAttribute( "send", QString::number( m_sendCheckBox->isChecked() ) ); - mw_de.setAttribute( "route", QString::number( - m_routeCheckBox->isChecked() ) ); + + if( m_readablePorts != NULL && m_receiveCheckBox->isChecked() == TRUE ) + { + QString rp; + for( csize i = 0; i < m_readablePorts->count(); ++i ) + { + int id = m_readablePorts->idAt( i ); + if( m_readablePorts->isItemChecked( id ) ) + { + rp += m_readablePorts->text( id ) + ","; + } + } + // cut off comma + if( rp.length() > 0 ) + { + rp.truncate( rp.length() - 1 ); + } + mw_de.setAttribute( "inports", rp ); + } + + if( m_writeablePorts != NULL && m_sendCheckBox->isChecked() == TRUE ) + { + QString wp; + for( csize i = 0; i < m_writeablePorts->count(); ++i ) + { + int id = m_writeablePorts->idAt( i ); + if( m_writeablePorts->isItemChecked( id ) ) + { + wp += m_writeablePorts->text( id ) + ","; + } + } + // cut off comma + if( wp.length() > 0 ) + { + wp.truncate( wp.length() - 1 ); + } + mw_de.setAttribute( "outports", wp ); + } _parent.appendChild( mw_de ); } @@ -200,7 +228,40 @@ void midiTabWidget::loadSettings( const QDomElement & _this ) ).toInt() ); m_receiveCheckBox->setChecked( _this.attribute( "receive" ).toInt() ); m_sendCheckBox->setChecked( _this.attribute( "send" ).toInt() ); - m_routeCheckBox->setChecked( _this.attribute( "route" ).toInt() ); + + // restore connections + + QStringList rp = QStringList::split( ',', _this.attribute( + "inports" ) ); + if( m_readablePorts != NULL && m_receiveCheckBox->isChecked() == TRUE ) + { + for( csize i = 0; i < m_readablePorts->count(); ++i ) + { + int id = m_readablePorts->idAt( i ); + if( m_readablePorts->isItemChecked( id ) != + ( rp.find( m_readablePorts->text( id ) ) != + rp.end() ) ) + { + activatedReadablePort( id ); + } + } + } + + QStringList wp = QStringList::split( ',', _this.attribute( + "outports" ) ); + if( m_writeablePorts != NULL && m_sendCheckBox->isChecked() == TRUE ) + { + for( csize i = 0; i < m_writeablePorts->count(); ++i ) + { + int id = m_writeablePorts->idAt( i ); + if( m_writeablePorts->isItemChecked( id ) != + ( wp.find( m_writeablePorts->text( id ) ) != + wp.end() ) ) + { + activatedWriteablePort( id ); + } + } + } } diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index ce3a6b1f2..6c8b999a6 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -23,6 +23,12 @@ */ +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + #include "mixer.h" #include "play_handle.h" #include "song_editor.h" @@ -53,6 +59,39 @@ Uint32 SAMPLE_RATES[QUALITY_LEVELS] = { 44100, 88200 } ; + +class microTimer +{ +public: + microTimer( void ) + { + reset(); + } + + ~microTimer() + { + } + + void reset( void ) + { + gettimeofday( &begin, NULL ); + } + + Uint32 elapsed( void ) const + { + struct timeval now; + gettimeofday( &now, NULL ); + return( ( now.tv_sec - begin.tv_sec ) * 1000 * 1000 + + ( now.tv_usec - begin.tv_usec ) ); + } + +private: + struct timeval begin; + +} ; + + + mixer * mixer::s_instanceOfMe = NULL; @@ -61,6 +100,7 @@ mixer::mixer() : m_framesPerAudioBuffer( DEFAULT_BUFFER_SIZE ), m_curBuf( NULL ), m_nextBuf( NULL ), + m_cpuLoad( 0 ), m_qualityLevel( DEFAULT_QUALITY_LEVEL ), m_masterGain( 1.0f ), m_audioDev( NULL ), @@ -125,6 +165,12 @@ void mixer::stopProcessing( void ) const surroundSampleFrame * mixer::renderNextBuffer( void ) { + microTimer timer; + + // now we have to make sure no other thread does anything bad + // while we're acting... + m_mixMutex.lock(); + // remove all play-handles that have to be deleted and delete // them if they still exist... // maybe this algorithm could be optimized... @@ -147,10 +193,6 @@ const surroundSampleFrame * mixer::renderNextBuffer( void ) m_playHandlesToRemove.begin() ); } - // now we have to make sure no other thread does anything bad - // while we're acting... - m_mixMutex.lock(); - // now swap the buffers... current buffer becomes next (last) // buffer and the next buffer becomes current (first) buffer qSwap( m_curBuf, m_nextBuf ); @@ -201,6 +243,10 @@ const surroundSampleFrame * mixer::renderNextBuffer( void ) // and trigger LFOs envelopeAndLFOWidget::triggerLFO(); + const float new_cpu_load = timer.elapsed() / 10000.0f * sampleRate() / + m_framesPerAudioBuffer; + m_cpuLoad = tLimit( (int) ( new_cpu_load + m_cpuLoad ) / 2, 0, 100 ); + return( m_curBuf ); } diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp index 60dd3ca74..360f6cba4 100644 --- a/src/core/note_play_handle.cpp +++ b/src/core/note_play_handle.cpp @@ -57,9 +57,10 @@ notePlayHandle::notePlayHandle( channelTrack * _chnl_trk, Uint32 _frames_ahead, m_channelTrack->processOutEvent( midiEvent( NOTE_ON, m_channelTrack->m_midiPort->outputChannel(), key(), + tLimit( (Uint16) ( ( getVolume() / 100.0f ) * ( m_channelTrack->getVolume() / 100.0f ) * - 127 ) ), + 127 ), 0, 127 ) ), midiTime::fromFrames( m_framesAhead, songEditor::inst()->framesPerTact() ) ); } @@ -202,6 +203,13 @@ void notePlayHandle::checkValidity( void ) { m_channelTrack = NULL; } + // sub-notes might not be registered at mixer (for example arpeggio- + // notes), so they wouldn't invalidate them-selves + for( notePlayHandleVector::iterator it = m_subNotes.begin(); + it != m_subNotes.end(); ++it ) + { + ( *it )->checkValidity(); + } } diff --git a/src/core/piano_roll.cpp b/src/core/piano_roll.cpp index cf115a011..2b8be0164 100644 --- a/src/core/piano_roll.cpp +++ b/src/core/piano_roll.cpp @@ -125,7 +125,7 @@ pianoRoll::pianoRollKeyTypes pianoRoll::prKeyOrder[] = } ; -const int DEFAULT_PR_PPT = KEY_LINE_HEIGHT * MAX_BEATS_PER_TACT; +const int DEFAULT_PR_PPT = KEY_LINE_HEIGHT * DEFAULT_STEPS_PER_TACT; pianoRoll::pianoRoll( void ) : @@ -779,9 +779,9 @@ void pianoRoll::paintEvent( QPaintEvent * ) // draw vertical raster int tact_16th = m_currentPosition / 4; int offset = ( m_currentPosition % 4 ) * m_ppt / - MAX_BEATS_PER_TACT / 4; + DEFAULT_STEPS_PER_TACT / 4; for( int x = WHITE_KEY_WIDTH - offset; x < width(); - x += m_ppt/MAX_BEATS_PER_TACT, ++tact_16th ) + x += m_ppt / DEFAULT_STEPS_PER_TACT, ++tact_16th ) { if( x >= WHITE_KEY_WIDTH ) { @@ -1800,8 +1800,9 @@ void pianoRoll::keyPressEvent( QKeyEvent * _ke ) { if( ( m_timeLine->pos() -= 16 ) < 0 ) { - m_timeLine->pos() = 0; - } + m_timeLine->pos().setTact( 0 ); + m_timeLine->pos().setTact64th( 0 ); + } m_timeLine->updatePosition(); break; } @@ -1890,7 +1891,8 @@ void pianoRoll::keyPressEvent( QKeyEvent * _ke ) break; case Qt::Key_Home: - m_timeLine->pos() = 0; + m_timeLine->pos().setTact( 0 ); + m_timeLine->pos().setTact64th( 0 ); m_timeLine->updatePosition(); break; @@ -1920,7 +1922,7 @@ void pianoRoll::wheelEvent( QWheelEvent * _we ) if( _we->delta() > 0 ) { m_ppt = tMin( m_ppt * 2, KEY_LINE_HEIGHT * - MAX_BEATS_PER_TACT * 8 ); + DEFAULT_STEPS_PER_TACT * 8 ); } else if( m_ppt >= 72 ) { diff --git a/src/core/preset_preview_play_handle.cpp b/src/core/preset_preview_play_handle.cpp index 463e66af4..929517362 100644 --- a/src/core/preset_preview_play_handle.cpp +++ b/src/core/preset_preview_play_handle.cpp @@ -44,6 +44,8 @@ #include "track_container.h" #include "mmp.h" #include "debug.h" +#include "midi_port.h" + // invisible track-container which is needed as parents for preview-channels @@ -126,12 +128,15 @@ presetPreviewPlayHandle::presetPreviewPlayHandle( s_globalChannelTrack->loadTrackSpecificSettings( mmp.content(). firstChild(). toElement() ); + // make sure, our preset-preview-track does not appear in any MIDI- + // devices list, so just disable receiving/sending MIDI-events at all + s_globalChannelTrack->m_midiPort->setMode( midiPort::DUMMY ); + // create temporary note note n( 0, 0, static_cast( A ), static_cast( DEFAULT_OCTAVE-1 ), 100 ); // create note-play-handle for it m_previewNote = new notePlayHandle( s_globalChannelTrack, 0, ~0, &n ); - //m_previewNote->setFrames( mixer::inst()->sampleRate() ); s_globalPreviewNote = m_previewNote; @@ -151,7 +156,6 @@ presetPreviewPlayHandle::~presetPreviewPlayHandle() } delete m_previewNote; s_globalDataMutex->unlock(); - //blindTrackContainer::inst()->removeTrack( m_channelTrack ); } diff --git a/src/core/song_editor.cpp b/src/core/song_editor.cpp index bef7b4ca9..6b6a8e8fc 100644 --- a/src/core/song_editor.cpp +++ b/src/core/song_editor.cpp @@ -46,10 +46,8 @@ #include #include #include -#include #include #include -#include #else @@ -64,7 +62,6 @@ #include #include #include -#include #endif @@ -90,6 +87,7 @@ #include "lcd_spinbox.h" #include "tooltip.h" #include "tool_button.h" +#include "cpuload_widget.h" #include "debug.h" @@ -157,42 +155,13 @@ songEditor::songEditor() : connect( tl, SIGNAL( positionChanged( const midiTime & ) ), this, SLOT( updatePosition( const midiTime & ) ) ); - // create toolbar - m_toolBar = new QWidget( cw ); - m_toolBar->setFixedHeight( 32 ); - m_toolBar->move( 0, 0 ); - m_toolBar->setPaletteBackgroundPixmap( embed::getIconPixmap( - "toolbar_bg" ) ); - QHBoxLayout * tb_layout = new QHBoxLayout( m_toolBar ); + // add some essential widgets to global tool-bar + QWidget * tb = lmmsMainWin::inst()->toolBar(); + lmmsMainWin::inst()->addSpacingToToolBar( 10 ); -#ifdef QT4 - containerWidget()->setParent( cw ); -#else - containerWidget()->reparent( cw, 0, QPoint( 0, 0 ) ); -#endif - containerWidget()->move( 0, m_toolBar->height() + tl->height() ); - - - QToolBar * main_tb = lmmsMainWin::inst()->mainToolBar(); - - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 10, 1 ); - - - QLabel * bpm_label = new QLabel( main_tb ); - bpm_label->setPixmap( embed::getIconPixmap( "clock" ) ); - - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 8, 1 ); - - - m_bpmSpinBox = new lcdSpinBox( MIN_BPM, MAX_BPM, 3, main_tb ); -#ifdef QT4 - main_tb->addWidget( m_bpmSpinBox ); - main_tb->addWidget( bpm_label ); -#endif + m_bpmSpinBox = new lcdSpinBox( MIN_BPM, MAX_BPM, 3, tb ); m_bpmSpinBox->setLabel( tr( "TEMPO/BPM" ) ); connect( m_bpmSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( setBPM( int ) ) ); @@ -210,30 +179,37 @@ songEditor::songEditor() : "should be played within a minute (or how many tacts " "should be played within four minutes)." ) ); - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 10, 1 ); + int col = lmmsMainWin::inst()->addWidgetToToolBar( m_bpmSpinBox, 0 ); - main_tb->addSeparator(); + toolButton * hq_btn = new toolButton( embed::getIconPixmap( "hq_mode" ), + tr( "High quality mode" ), + NULL, NULL, tb ); + hq_btn->setToggleButton( TRUE ); + connect( hq_btn, SIGNAL( toggled( bool ) ), mixer::inst(), + SLOT( setHighQuality( bool ) ) ); + hq_btn->setFixedWidth( 42 ); + lmmsMainWin::inst()->addWidgetToToolBar( hq_btn, 1, col ); - QLabel * master_vol_lbl = new QLabel( main_tb ); + lmmsMainWin::inst()->addSpacingToToolBar( 10 ); + + + + QLabel * master_vol_lbl = new QLabel( tb ); master_vol_lbl->setPixmap( embed::getIconPixmap( "master_volume" ) ); #ifdef QT4 - m_masterVolumeSlider = new QSlider( Qt::Vertical, main_tb ); + m_masterVolumeSlider = new QSlider( Qt::Vertical, tb ); m_masterVolumeSlider->setRange( 0, 200 ); m_masterVolumeSlider->setPageStep( 10 ); m_masterVolumeSlider->setValue( 100 ); m_masterVolumeSlider->setTickPosition( QSlider::TicksLeft ); - main_tb->addWidget( master_vol_lbl ); - main_tb->addWidget( m_masterVolumeSlider ); #else - m_masterVolumeSlider = new QSlider( 0, 200, 10, 100, Qt::Vertical, - main_tb ); + m_masterVolumeSlider = new QSlider( 0, 200, 10, 100, Qt::Vertical, tb ); m_masterVolumeSlider->setTickPosition( QSlider::Left ); #endif - m_masterVolumeSlider->setFixedSize( 26, 48 ); + m_masterVolumeSlider->setFixedSize( 26, 60 ); m_masterVolumeSlider->setTickInterval( 50 ); toolTip::add( m_masterVolumeSlider, tr( "master output volume" ) ); @@ -246,27 +222,26 @@ songEditor::songEditor() : connect( m_masterVolumeSlider, SIGNAL( sliderReleased() ), this, SLOT( masterVolumeReleased() ) ); + lmmsMainWin::inst()->addWidgetToToolBar( master_vol_lbl ); + lmmsMainWin::inst()->addWidgetToToolBar( m_masterVolumeSlider ); - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 10, 1 ); - QLabel * master_pitch_lbl = new QLabel( main_tb ); + lmmsMainWin::inst()->addSpacingToToolBar( 10 ); + + QLabel * master_pitch_lbl = new QLabel( tb ); master_pitch_lbl->setPixmap( embed::getIconPixmap( "master_pitch" ) ); #ifdef QT4 - m_masterPitchSlider = new QSlider( Qt::Vertical, main_tb ); + m_masterPitchSlider = new QSlider( Qt::Vertical, tb ); m_masterPitchSlider->setRange( -12, 12 ); m_masterPitchSlider->setPageStep( 1 ); m_masterPitchSlider->setValue( 0 ); m_masterPitchSlider->setTickPosition( QSlider::TicksLeft ); - main_tb->addWidget( master_pitch_lbl ); - main_tb->addWidget( m_masterPitchSlider ); #else - m_masterPitchSlider = new QSlider( -12, 12, 1, 0, Qt::Vertical, - main_tb); + m_masterPitchSlider = new QSlider( -12, 12, 1, 0, Qt::Vertical, tb ); m_masterPitchSlider->setTickPosition( QSlider::Left ); #endif - m_masterPitchSlider->setFixedSize( 26, 48 ); + m_masterPitchSlider->setFixedSize( 26, 60 ); m_masterPitchSlider->setTickInterval( 12 ); toolTip::add( m_masterPitchSlider, tr( "master pitch" ) ); connect( m_masterPitchSlider, SIGNAL( valueChanged( int ) ), this, @@ -278,35 +253,45 @@ songEditor::songEditor() : connect( m_masterPitchSlider, SIGNAL( sliderReleased() ), this, SLOT( masterPitchReleased() ) ); - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 5, 1 ); + lmmsMainWin::inst()->addWidgetToToolBar( master_pitch_lbl ); + lmmsMainWin::inst()->addWidgetToToolBar( m_masterPitchSlider ); - main_tb->addSeparator(); + lmmsMainWin::inst()->addSpacingToToolBar( 10 ); + + // create widget for visualization- and cpu-load-widget + QWidget * vc_w = new QWidget( tb ); + QVBoxLayout * vcw_layout = new QVBoxLayout( vc_w ); + + vcw_layout->addStretch(); + vcw_layout->addWidget( new visualizationWidget( + embed::getIconPixmap( "output_graph" ), vc_w ) ); + + vcw_layout->addWidget( new cpuloadWidget( vc_w ) ); + vcw_layout->addStretch(); + + lmmsMainWin::inst()->addWidgetToToolBar( vc_w ); + + + // create own toolbar + m_toolBar = new QWidget( cw ); + m_toolBar->setFixedHeight( 32 ); + m_toolBar->move( 0, 0 ); + m_toolBar->setPaletteBackgroundPixmap( embed::getIconPixmap( + "toolbar_bg" ) ); + + QHBoxLayout * tb_layout = new QHBoxLayout( m_toolBar ); - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 5, 1 ); - m_masterOutputGraph = new visualizationWidget( embed::getIconPixmap( - "output_graph" ), main_tb ); #ifdef QT4 - main_tb->addWidget( m_masterOutputGraph ); + containerWidget()->setParent( cw ); +#else + containerWidget()->reparent( cw, 0, QPoint( 0, 0 ) ); #endif - // spacer-item - ( new QWidget( main_tb ) )->setFixedSize( 5, 1 ); - - main_tb->addSeparator(); - - QToolButton * hq = new QToolButton( - embed::getIconPixmap( "hq_mode" ), - tr( "High quality mode" ), - QString::null, NULL, NULL, - main_tb ); - hq->setToggleButton( TRUE ); - connect( hq, SIGNAL( toggled( bool ) ), mixer::inst(), - SLOT( setHighQuality( bool ) ) ); + containerWidget()->move( 0, m_toolBar->height() + tl->height() ); + // fill own tool-bar m_playButton = new toolButton( embed::getIconPixmap( "play" ), tr( "Play song (Space)" ), this, SLOT( play() ), m_toolBar ); @@ -817,7 +802,8 @@ void songEditor::doActions( void ) } // a second switch for saving pos when starting to play - // anything + // anything (need pos for restoring it later in certain + // timeline-modes) switch( m_actions.front() ) { case ACT_PLAY_SONG: @@ -1241,7 +1227,7 @@ void songEditor::addSampleTrack( void ) float songEditor::framesPerTact( void ) const { - return( mixer::inst()->sampleRate() * 60.0f * MAIN_BEATS_PER_TACT / + return( mixer::inst()->sampleRate() * 60.0f * BEATS_PER_TACT / m_bpmSpinBox->value() ); } @@ -1316,7 +1302,6 @@ void songEditor::clearProject( void ) // access non existing data (as you can see in the next lines, // all data is cleared!) stop(); - doActions(); } // make sure all running notes are cleared, otherwise the whole @@ -1324,6 +1309,7 @@ void songEditor::clearProject( void ) //mixer::inst()->clear(); while( mixer::inst()->haveNoRunningNotes() == FALSE ) { +/* qApp->processEvents();*/ } trackVector tv = tracks(); diff --git a/src/core/timeline.cpp b/src/core/timeline.cpp index 731a3397b..ce3e089fb 100644 --- a/src/core/timeline.cpp +++ b/src/core/timeline.cpp @@ -32,18 +32,19 @@ #include #include #include +#include #else #include #include #include +#include #endif #include "timeline.h" -#include "nstate_button.h" #include "embed.h" #include "templates.h" #include "nstate_button.h" @@ -63,6 +64,7 @@ timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt, m_autoScroll( AUTOSCROLL_ENABLED ), m_loopPoints( LOOP_POINTS_DISABLED ), m_behaviourAtStop( BACK_TO_ZERO ), + m_changedPosition( TRUE ), m_xOffset( _xoff ), m_posMarkerX( 0 ), m_ppt( _ppt ), @@ -109,6 +111,10 @@ timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt, m_pos.m_timeLine = this; updatePosition(); + QTimer * update_timer = new QTimer( this ); + connect( update_timer, SIGNAL( timeout() ), this, + SLOT( checkForUpdatedPosition() ) ); + update_timer->start( 50 ); } @@ -173,13 +179,7 @@ void timeLine::updatePosition( const midiTime & ) if( new_x != m_posMarkerX ) { m_posMarkerX = new_x; -#ifndef QT4 - qApp->lock(); -#endif - paintEvent( NULL ); -#ifndef QT4 - qApp->unlock(); -#endif + m_changedPosition = TRUE; if( m_autoScroll == AUTOSCROLL_ENABLED ) { emit positionChanged( m_pos ); @@ -215,6 +215,18 @@ void timeLine::toggleBehaviourAtStop( int _n ) +void timeLine::checkForUpdatedPosition( void ) +{ + if( m_changedPosition == TRUE ) + { + repaint(); + m_changedPosition = FALSE; + } +} + + + + void timeLine::paintEvent( QPaintEvent * ) { #ifdef QT4 diff --git a/src/lib/mmp.cpp b/src/lib/mmp.cpp index 6b9de75a6..2f81bba99 100644 --- a/src/lib/mmp.cpp +++ b/src/lib/mmp.cpp @@ -47,10 +47,12 @@ -multimediaProject::typeDescStruct multimediaProject::s_types[multimediaProject::PROJ_TYPE_COUNT] = +multimediaProject::typeDescStruct + multimediaProject::s_types[multimediaProject::PROJ_TYPE_COUNT] = { { multimediaProject::UNKNOWN, "unknown" }, { multimediaProject::SONG_PROJECT, "song" }, + { multimediaProject::SONG_PROJECT_TEMPLATE, "songtemplate" }, { multimediaProject::CHANNEL_SETTINGS, "channelsettings" }, { multimediaProject::EFFECT_SETTINGS, "effectsettings" }, { multimediaProject::VIDEO_PROJECT, "video" }, @@ -176,9 +178,19 @@ bool multimediaProject::writeFile( const QString & _fn, bool _overwrite_check ) fn += ".cs.xml"; } } - else if( fn.section( '.',-1 ) != "xml" ) + else if( type() == SONG_PROJECT ) { - fn += ".xml"; + if( fn.section( '.',-1 ) != "mmp" ) + { + fn += ".mmp"; + } + } + else if( type() == SONG_PROJECT_TEMPLATE ) + { + if( fn.section( '.',-1 ) != "mpt" ) + { + fn += ".mpt"; + } } diff --git a/src/midi/midi_alsa_raw.cpp b/src/midi/midi_alsa_raw.cpp index e95653d1d..0831f61e5 100644 --- a/src/midi/midi_alsa_raw.cpp +++ b/src/midi/midi_alsa_raw.cpp @@ -89,7 +89,7 @@ midiALSARaw::~midiALSARaw() if( running() ) { m_quit = TRUE; - wait( 500 ); + wait( 1000 ); terminate(); snd_rawmidi_close( m_input ); diff --git a/src/midi/midi_alsa_seq.cpp b/src/midi/midi_alsa_seq.cpp index c46d84952..6aa489144 100644 --- a/src/midi/midi_alsa_seq.cpp +++ b/src/midi/midi_alsa_seq.cpp @@ -114,7 +114,7 @@ midiALSASeq::~midiALSASeq() if( running() ) { m_quit = TRUE; - wait( 500 ); + wait( 1000 ); terminate(); snd_seq_stop_queue( m_seqHandle, m_queueID, NULL ); diff --git a/src/midi/midi_oss.cpp b/src/midi/midi_oss.cpp index 74c2899ec..9bff255c4 100644 --- a/src/midi/midi_oss.cpp +++ b/src/midi/midi_oss.cpp @@ -84,7 +84,7 @@ midiOSS::~midiOSS() if( running() ) { m_quit = TRUE; - wait( 500 ); + wait( 1000 ); terminate(); } } diff --git a/src/midi/midi_port.cpp b/src/midi/midi_port.cpp index cdefd1358..6039c4c06 100644 --- a/src/midi/midi_port.cpp +++ b/src/midi/midi_port.cpp @@ -85,7 +85,7 @@ void midiPort::processOutEvent( const midiEvent & _me, const midiTime & _time ) { // mask event if( ( mode() == OUTPUT || mode() == DUPLEX ) && - ( outputChannel() == _me.m_channel || outputChannel() == -1 ) ) + ( outputChannel() == _me.m_channel && outputChannel() != -1 ) ) { m_midiClient->processOutEvent( _me, _time, this ); } diff --git a/src/tracks/pattern.cpp b/src/tracks/pattern.cpp index 38a77b83a..53736b118 100644 --- a/src/tracks/pattern.cpp +++ b/src/tracks/pattern.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #else @@ -43,6 +44,10 @@ #include #include #include +#include + +#define addSeparator insertSeparator +#define addMenu insertItem #endif @@ -75,35 +80,13 @@ pattern::pattern ( channelTrack * _channel_track ) : m_channelTrack( _channel_track ), m_patternType( BEAT_PATTERN ), m_name( _channel_track->name() ), + m_steps( DEFAULT_STEPS_PER_TACT ), m_frozenPatternMutex(), m_frozenPattern( NULL ), m_freezing( FALSE ), m_freezeAborted( FALSE ) { - initPixmaps(); - - setFixedHeight( s_patternBg->height() + 4 ); - -#ifndef QT4 - // set background-mode for flicker-free redraw - setBackgroundMode( Qt::NoBackground ); -#endif - - if( m_patternType == BEAT_PATTERN ) - { - for( int i = 0; i < MAX_BEATS_PER_TACT; ++i ) - { - m_notes.push_back( new note( midiTime( 0 ), - midiTime( i*4 ) ) ); - } - } - - changeLength( length() ); - - setAutoResizeEnabled( FALSE ); - - toolTip::add( this, - tr( "double-click to open this pattern in piano-roll" ) ); + init(); } @@ -114,27 +97,18 @@ pattern::pattern( const pattern & _pat_to_copy ) : m_channelTrack( _pat_to_copy.m_channelTrack ), m_patternType( _pat_to_copy.m_patternType ), m_name( "" ), + m_steps( _pat_to_copy.m_steps ), m_frozenPatternMutex(), m_frozenPattern( NULL ), m_freezeAborted( FALSE ) { - initPixmaps(); - for( noteVector::const_iterator it = _pat_to_copy.m_notes.begin(); it != _pat_to_copy.m_notes.end(); ++it ) { m_notes.push_back( new note( **it ) ); } - setFixedHeight( s_patternBg->height() + 4 ); - -#ifndef QT4 - // set background-mode for flicker-free redraw - setBackgroundMode( Qt::NoBackground ); -#endif - - changeLength( length() ); - setAutoResizeEnabled( FALSE ); + init(); } @@ -162,7 +136,7 @@ pattern::~pattern() -void pattern::initPixmaps( void ) +void pattern::init( void ) { if( s_patternBg == NULL ) { @@ -188,6 +162,21 @@ void pattern::initPixmaps( void ) { s_frozen = new QPixmap( embed::getIconPixmap( "frozen" ) ); } + + ensureBeatNotes(); + +#ifndef QT4 + // set background-mode for flicker-free redraw + setBackgroundMode( Qt::NoBackground ); +#endif + + setFixedHeight( s_patternBg->height() + 4 ); + changeLength( length() ); + + setAutoResizeEnabled( FALSE ); + + toolTip::add( this, + tr( "double-click to open this pattern in piano-roll" ) ); } @@ -222,32 +211,47 @@ void pattern::constructContextMenu( QMenu * _cm ) _cm->insertSeparator( 1 ); #endif -#ifdef QT4 _cm->addSeparator(); -#else - _cm->insertSeparator(); -#endif + _cm->addAction( embed::getIconPixmap( "edit_erase" ), tr( "Clear all notes" ), this, SLOT( clear() ) ); -#ifdef QT4 _cm->addSeparator(); -#else - _cm->insertSeparator(); -#endif + _cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ), this, SLOT( resetName() ) ); _cm->addAction( embed::getIconPixmap( "rename" ), tr( "Change name" ), this, SLOT( changeName() ) ); -#ifdef QT4 _cm->addSeparator(); -#else - _cm->insertSeparator(); -#endif + _cm->addAction( embed::getIconPixmap( "freeze" ), ( m_frozenPattern != NULL )? tr( "Refreeze" ) : tr( "Freeze" ), this, SLOT( freeze() ) ); _cm->addAction( embed::getIconPixmap( "unfreeze" ), tr( "Unfreeze" ), this, SLOT( unfreeze() ) ); + + _cm->addSeparator(); + + QMenu * add_step_menu = new QMenu( this ); + QMenu * remove_step_menu = new QMenu( this ); + for( int i = 1; i <= 16; i *= 2 ) + { + const QString label = ( i == 1 ) ? + tr( "1 step" ) : + tr( "%1 steps" ).arg( i ); + + int menu_id = add_step_menu->addAction( label, this, + SLOT( addSteps( int ) ) ); + add_step_menu->setItemParameter( menu_id, i ); + menu_id = remove_step_menu->addAction( label, this, + SLOT( removeSteps( int ) ) ); + remove_step_menu->setItemParameter( menu_id, i ); + } + + _cm->addMenu( embed::getIconPixmap( "step_btn_add" ), + tr( "Add steps" ), add_step_menu ); + _cm->addMenu( embed::getIconPixmap( "step_btn_remove" ), + tr( "Remove steps" ), remove_step_menu ); + } @@ -256,13 +260,14 @@ void pattern::constructContextMenu( QMenu * _cm ) void pattern::ensureBeatNotes( void ) { // make sure, that all step-note exist - for( int i = 0; i < MAX_BEATS_PER_TACT; ++i ) + for( int i = 0; i < m_steps; ++i ) { bool found = FALSE; for( noteVector::iterator it = m_notes.begin(); it != m_notes.end(); ++it ) { - if( ( *it )->pos() == i * 4 && ( *it )->length() <= 0 ) + if( ( *it )->pos() == i * BEATS_PER_TACT && + ( *it )->length() <= 0 ) { found = TRUE; break; @@ -270,7 +275,8 @@ void pattern::ensureBeatNotes( void ) } if( found == FALSE ) { - addNote( note( midiTime( 0 ), midiTime( i * 4 ) ) ); + addNote( note( midiTime( 0 ), midiTime( i * + BEATS_PER_TACT ) ) ); } } } @@ -383,45 +389,47 @@ void pattern::paintEvent( QPaintEvent * ) } } } - else if( m_patternType == pattern::BEAT_PATTERN && ppt >= 192 ) + else if( m_patternType == pattern::BEAT_PATTERN && + ( ppt >= 192 || m_steps != DEFAULT_STEPS_PER_TACT ) ) { QPixmap stepon; QPixmap stepoff; QPixmap stepoffl; + int steps = length() / BEATS_PER_TACT; #ifdef QT4 - stepon = s_stepBtnOn->scaled( width() / 16, + stepon = s_stepBtnOn->scaled( width() / steps, s_stepBtnOn->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); - stepoff = s_stepBtnOff->scaled( width() / 16, + stepoff = s_stepBtnOff->scaled( width() / steps, s_stepBtnOff->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); - stepoffl = s_stepBtnOffLight->scaled( width() / 16, + stepoffl = s_stepBtnOffLight->scaled( width() / steps, s_stepBtnOffLight->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); #else stepon.convertFromImage( s_stepBtnOn->convertToImage().scale( - width() / 16, s_stepBtnOn->height() ) ); + width() / steps, s_stepBtnOn->height() ) ); stepoff.convertFromImage( s_stepBtnOff->convertToImage().scale( - width() / 16, s_stepBtnOff->height() ) ); + width() / steps, s_stepBtnOff->height() ) ); stepoffl.convertFromImage( s_stepBtnOffLight->convertToImage(). - scale( width() / 16, s_stepBtnOffLight->height() ) ); + scale( width() / steps, + s_stepBtnOffLight->height() ) ); #endif for( noteVector::iterator it = m_notes.begin(); it != m_notes.end(); ++it ) { Sint16 no = it - m_notes.begin(); Sint16 x = TCO_BORDER_WIDTH + static_cast( no * - ppt / - m_notes.size() ); + width() / steps ); Sint16 y = height() - s_stepBtnOn->height() - 1; if( ( *it )->length() < 0 ) { p.drawPixmap( x, y, stepon ); } - else if( ( no / 4 ) % 2 ) + else if( ( no / BEATS_PER_TACT ) % 2 ) { p.drawPixmap( x, y, stepoff ); } @@ -459,11 +467,18 @@ void pattern::mousePressEvent( QMouseEvent * _me ) return; } - if( m_patternType == pattern::BEAT_PATTERN && pixelsPerTact() >= 192 && + if( m_patternType == pattern::BEAT_PATTERN && + ( pixelsPerTact() >= 192 || + m_steps != DEFAULT_STEPS_PER_TACT ) && _me->y() > height() - s_stepBtnOn->height() ) { - note * n = m_notes[( _me->x() - TCO_BORDER_WIDTH ) * 16 / - width() ]; + int step = ( _me->x() - TCO_BORDER_WIDTH ) * + length() / BEATS_PER_TACT / width(); + if( step >= m_steps ) + { + return; + } + note * n = m_notes[step]; if( n->length() < 0 ) { n->setLength( 0 ); @@ -491,7 +506,8 @@ void pattern::mouseDoubleClickEvent( QMouseEvent * _me ) } if( m_patternType == pattern::MELODY_PATTERN || !( m_patternType == pattern::BEAT_PATTERN && - pixelsPerTact() >= 192 && + ( pixelsPerTact() >= 192 || + m_steps != DEFAULT_STEPS_PER_TACT ) && _me->y() > height() - s_stepBtnOn->height() ) ) { openInPianoRoll(); @@ -593,59 +609,8 @@ void pattern::freeze( void ) unfreeze(); } - // create and install audio-sample-recorder - bool b; - // we cannot create local copy, because at a later stage - // mixer::restoreAudioDevice(...) deletes old audio-dev and thus - // audioSampleRecorder would be destroyed two times... - audioSampleRecorder * freeze_recorder = new audioSampleRecorder( - mixer::inst()->sampleRate(), DEFAULT_CHANNELS, b ); - mixer::inst()->setAudioDevice( freeze_recorder, - mixer::inst()->highQuality() ); + new patternFreezeThread( this ); - // prepare stuff for playing correct things later - songEditor::inst()->playPattern( this, FALSE ); - songEditor::playPos & ppp = songEditor::inst()->getPlayPos( - songEditor::PLAY_PATTERN ); - ppp.setTact( 0 ); - ppp.setTact64th( 0 ); - ppp.setCurrentFrame( 0 ); - ppp.m_timeLineUpdate = FALSE; - - // create status-dialog - patternFreezeStatusDialog status_dlg; - status_dlg.show(); - connect( &status_dlg, SIGNAL( aborted() ), - this, SLOT( abortFreeze() ) ); - - m_freezeAborted = FALSE; - m_freezing = TRUE; - - // now render everything - while( ppp < length() && m_freezeAborted == FALSE ) - { - freeze_recorder->processNextBuffer(); - status_dlg.setProgress( ppp * 100 / length() ); - qApp->processEvents(); - } - - m_freezing = FALSE; - - // reset song-editor settings - songEditor::inst()->stop(); - songEditor::inst()->getPlayPos( songEditor::PLAY_PATTERN - ).m_timeLineUpdate = TRUE; - - // create final sample-buffer if freezing was successful - if( m_freezeAborted == FALSE ) - { - m_frozenPatternMutex.lock(); - freeze_recorder->createSampleBuffer( &m_frozenPattern ); - m_frozenPatternMutex.unlock(); - } - - // restore original audio-device - mixer::inst()->restoreAudioDevice(); } @@ -673,6 +638,41 @@ void pattern::abortFreeze( void ) +void pattern::addSteps( int _n ) +{ + m_steps += _n; + ensureBeatNotes(); + update(); +} + + + + +void pattern::removeSteps( int _n ) +{ + if( _n < m_steps ) + { + for( int i = m_steps - _n; i < m_steps; ++i ) + { + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + if( ( *it )->pos() == i * BEATS_PER_TACT && + ( *it )->length() <= 0 ) + { + removeNote( *it ); + break; + } + } + } + m_steps -= _n; + update(); + } +} + + + + void pattern::playFrozenData( sampleFrame * _ab, Uint32 _start_frame, Uint32 _frames ) { @@ -692,9 +692,12 @@ midiTime pattern::length( void ) const { if( m_patternType == BEAT_PATTERN ) { - // TODO: remove this limitation later by adding - // "Add step to pattern"-option - return( 64 ); + if( m_steps % DEFAULT_STEPS_PER_TACT == 0 ) + { + return( m_steps * BEATS_PER_TACT ); + } + return( ( m_steps / DEFAULT_STEPS_PER_TACT + 1 ) * + DEFAULT_STEPS_PER_TACT * BEATS_PER_TACT ); } Sint32 max_length = 0; @@ -920,6 +923,13 @@ void pattern::loadSettings( const QDomElement & _this ) } node = node.nextSibling(); } + + m_steps = _this.attribute( "steps" ).toInt(); + if( m_steps == 0 ) + { + m_steps = DEFAULT_STEPS_PER_TACT; + } + ensureBeatNotes(); /* if( _this.attribute( "frozen" ).toInt() ) { @@ -931,8 +941,15 @@ void pattern::loadSettings( const QDomElement & _this ) -patternFreezeStatusDialog::patternFreezeStatusDialog( void ) : - QDialog() + + + + + + +patternFreezeStatusDialog::patternFreezeStatusDialog( QThread * _thread ) : + QDialog(), + m_freezeThread( _thread ) { setWindowTitle( tr( "Freezing pattern..." ) ); #if QT_VERSION >= 0x030200 @@ -954,6 +971,17 @@ patternFreezeStatusDialog::patternFreezeStatusDialog( void ) : m_cancelBtn->show(); connect( m_cancelBtn, SIGNAL( clicked() ), this, SLOT( cancelBtnClicked() ) ); + show(); + + QTimer * update_timer = new QTimer( this ); + connect( update_timer, SIGNAL( timeout() ), + this, SLOT( updateProgress() ) ); + update_timer->start( 100 ); + + setWFlags( getWFlags() | Qt::WDestructiveClose ); + + connect( this, SIGNAL( aborted() ), this, SLOT( reject() ) ); + } @@ -961,6 +989,8 @@ patternFreezeStatusDialog::patternFreezeStatusDialog( void ) : patternFreezeStatusDialog::~patternFreezeStatusDialog() { + m_freezeThread->wait(); + delete m_freezeThread; } @@ -969,11 +999,7 @@ patternFreezeStatusDialog::~patternFreezeStatusDialog() void patternFreezeStatusDialog::setProgress( int _p ) { -#ifdef QT4 - m_progressBar->setValue( _p ); -#else - m_progressBar->setProgress( _p ); -#endif + m_progress = _p; } @@ -991,6 +1017,109 @@ void patternFreezeStatusDialog::closeEvent( QCloseEvent * _ce ) void patternFreezeStatusDialog::cancelBtnClicked( void ) { emit( aborted() ); + done( -1 ); +} + + + + +void patternFreezeStatusDialog::updateProgress( void ) +{ + if( m_progress < 0 ) + { + done( 0 ); + } + else + { +#ifdef QT4 + m_progressBar->setValue( m_progress ); +#else + m_progressBar->setProgress( m_progress ); +#endif + } +} + + + + + + + + +patternFreezeThread::patternFreezeThread( pattern * _pattern ) : + QThread(), + m_pattern( _pattern ) +{ + m_statusDlg = new patternFreezeStatusDialog( this ); + QObject::connect( m_statusDlg, SIGNAL( aborted() ), + m_pattern, SLOT( abortFreeze() ) ); + + start(); +} + + + + +patternFreezeThread::~patternFreezeThread() +{ +} + + + + +void patternFreezeThread::run( void ) +{ + // create and install audio-sample-recorder + bool b; + // we cannot create local copy, because at a later stage + // mixer::restoreAudioDevice(...) deletes old audio-dev and thus + // audioSampleRecorder would be destroyed two times... + audioSampleRecorder * freeze_recorder = new audioSampleRecorder( + mixer::inst()->sampleRate(), DEFAULT_CHANNELS, b ); + mixer::inst()->setAudioDevice( freeze_recorder, + mixer::inst()->highQuality() ); + + // prepare stuff for playing correct things later + songEditor::inst()->playPattern( m_pattern, FALSE ); + songEditor::playPos & ppp = songEditor::inst()->getPlayPos( + songEditor::PLAY_PATTERN ); + ppp.setTact( 0 ); + ppp.setTact64th( 0 ); + ppp.setCurrentFrame( 0 ); + ppp.m_timeLineUpdate = FALSE; + + // create status-dialog + m_pattern->m_freezeAborted = FALSE; + m_pattern->m_freezing = TRUE; + + // now render everything + while( ppp < m_pattern->length() && + m_pattern->m_freezeAborted == FALSE ) + { + freeze_recorder->processNextBuffer(); + m_statusDlg->setProgress( ppp * 100 / m_pattern->length() ); + } + + m_pattern->m_freezing = FALSE; + + // reset song-editor settings + songEditor::inst()->stop(); + ppp.m_timeLineUpdate = TRUE; + + // create final sample-buffer if freezing was successful + if( m_pattern->m_freezeAborted == FALSE ) + { + m_pattern->m_frozenPatternMutex.lock(); + freeze_recorder->createSampleBuffer( + &m_pattern->m_frozenPattern ); + m_pattern->m_frozenPatternMutex.unlock(); + } + + // restore original audio-device + mixer::inst()->restoreAudioDevice(); + + m_statusDlg->setProgress( -1 ); // we're finished + } diff --git a/src/widgets/cpuload_widget.cpp b/src/widgets/cpuload_widget.cpp new file mode 100644 index 000000000..31c63ebe2 --- /dev/null +++ b/src/widgets/cpuload_widget.cpp @@ -0,0 +1,96 @@ +/* + * cpuload_widget.cpp - widget for displaying CPU-load (partly based on + * Hydrogen's CPU-load-widget) + * + * Copyright (c) 2005 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "cpuload_widget.h" +#include "embed.h" +#include "mixer.h" + + +cpuloadWidget::cpuloadWidget( QWidget * _parent ) : + QWidget( _parent ), + m_currentLoad( 0 ), + m_temp(), + m_background( embed::getIconPixmap( "cpuload_bg" ) ), + m_leds( embed::getIconPixmap( "cpuload_leds" ) ), + m_changed( TRUE ), + m_updateTimer() +{ + setFixedSize( m_background.width(), m_background.height() ); + + m_temp.resize( width(), height() ); + + connect( &m_updateTimer, SIGNAL( timeout() ), + this, SLOT( updateCpuLoad() ) ); + m_updateTimer.start( 100 ); // update player control at 10 fps + +#ifndef QT4 + setBackgroundMode( NoBackground ); +#endif +} + + + + +cpuloadWidget::~cpuloadWidget() +{ +} + + + + +void cpuloadWidget::paintEvent( QPaintEvent * ) +{ + if( m_changed == TRUE ) + { + m_changed = FALSE; + + // background + bitBlt( &m_temp, 0, 0, &m_background, 0, 0, width(), height(), + CopyROP ); + + // leds + bitBlt( &m_temp, 23, 3, &m_leds, 0, 0, + ( m_leds.width() * m_currentLoad / 300 ) * 3, + m_leds.height(), CopyROP ); + } + bitBlt( this, 0, 0, &m_temp, 0, 0, width(), height(), CopyROP ); +} + + + + +void cpuloadWidget::updateCpuLoad() +{ + // smooth load-values a bit + m_currentLoad = ( m_currentLoad + mixer::inst()->cpuLoad() ) / 2; + m_changed = TRUE; + update(); +} + + + +#include "cpuload_widget.moc" + diff --git a/src/widgets/nstate_button.cpp b/src/widgets/nstate_button.cpp index 1a4d50701..6ddd662c3 100644 --- a/src/widgets/nstate_button.cpp +++ b/src/widgets/nstate_button.cpp @@ -27,13 +27,8 @@ #ifdef QT4 -#include #include -#else - -#include - #endif @@ -44,7 +39,7 @@ nStateButton::nStateButton( QWidget * _parent ) : - QPushButton( _parent ), + toolButton( _parent ), m_generalToolTip( "" ), m_curState( -1 ) { @@ -103,38 +98,13 @@ void nStateButton::changeState( int _n ) -/* -void nStateButton::paintEvent( QPaintEvent * ) -{ -#ifdef QT4 - QPainter p( this ); -#else - QPixmap draw_pm( rect().size() ); - draw_pm.fill( this, rect().topLeft() ); - - QPainter p( &draw_pm, this ); -#endif - - if( m_curState >= 0 && m_curState < (int) m_states.size() ) - { - p.drawPixmap( 0, 0, *m_states[m_curState].first ); - } -#ifndef QT4 - // and blit all the drawn stuff on the screen... - bitBlt( this, rect().topLeft(), &draw_pm ); -#endif -} -*/ - - - void nStateButton::mousePressEvent( QMouseEvent * _me ) { if( _me->button() == Qt::LeftButton && m_states.size() ) { changeState( ( ++m_curState ) % m_states.size() ); } - QPushButton::mousePressEvent( _me ); + toolButton::mousePressEvent( _me ); }