diff --git a/include/SampleTCO.h b/include/SampleTCO.h new file mode 100644 index 000000000..616dba044 --- /dev/null +++ b/include/SampleTCO.h @@ -0,0 +1,92 @@ +/* + * SampleTCO.h + * + * Copyright (c) 2005-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 SAMPLE_TCO_H +#define SAMPLE_TCO_H + +#include "SampleBuffer.h" +#include "SampleTrack.h" +#include "TrackContentObject.h" + + +class SampleTCO : public TrackContentObject +{ + Q_OBJECT + mapPropertyFromModel(bool,isRecord,setRecord,m_recordModel); +public: + SampleTCO( Track * _track ); + SampleTCO( const SampleTCO& orig ); + virtual ~SampleTCO(); + + SampleTCO& operator=( const SampleTCO& that ) = delete; + + void changeLength( const TimePos & _length ) override; + const QString & sampleFile() const; + + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override + { + return "sampletco"; + } + + SampleBuffer* sampleBuffer() + { + return m_sampleBuffer; + } + + TimePos sampleLength() const; + void setSampleStartFrame( f_cnt_t startFrame ); + void setSamplePlayLength( f_cnt_t length ); + TrackContentObjectView * createView( TrackView * _tv ) override; + + + bool isPlaying() const; + void setIsPlaying(bool isPlaying); + +public slots: + void setSampleBuffer( SampleBuffer* sb ); + void setSampleFile( const QString & _sf ); + void updateLength(); + void toggleRecord(); + void playbackPositionChanged(); + void updateTrackTcos(); + + +private: + SampleBuffer* m_sampleBuffer; + BoolModel m_recordModel; + bool m_isPlaying; + + friend class SampleTCOView; + + +signals: + void sampleChanged(); + void wasReversed(); +} ; + + + +#endif \ No newline at end of file diff --git a/include/SampleTCOView.h b/include/SampleTCOView.h new file mode 100644 index 000000000..eab8f2733 --- /dev/null +++ b/include/SampleTCOView.h @@ -0,0 +1,65 @@ +/* + * SampleTCOView.h + * + * Copyright (c) 2005-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 SAMPLE_TCO_VIEW_H +#define SAMPLE_TCO_VIEW_H + +#include "SampleTCOView.h" + +#include "SampleTCO.h" +#include "TrackContentObjectView.h" + +class SampleTCOView : public TrackContentObjectView +{ + Q_OBJECT + +public: + SampleTCOView( SampleTCO * _tco, TrackView * _tv ); + virtual ~SampleTCOView() = default; + +public slots: + void updateSample(); + void reverseSample(); + + + +protected: + void contextMenuEvent( QContextMenuEvent * _cme ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; + void mouseDoubleClickEvent( QMouseEvent * ) override; + void paintEvent( QPaintEvent * ) override; + + +private: + SampleTCO * m_tco; + QPixmap m_paintPixmap; + bool splitTCO( const TimePos pos ) override; +} ; + + + +#endif \ No newline at end of file diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 039aa5936..c2d984434 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -25,116 +25,14 @@ #ifndef SAMPLE_TRACK_H #define SAMPLE_TRACK_H -#include #include #include "AudioPort.h" #include "FadeButton.h" #include "FxMixer.h" -#include "FxLineLcdSpinBox.h" +#include "SampleTCO.h" +#include "SampleTrackView.h" #include "Track.h" -#include "TrackContentObjectView.h" -#include "TrackView.h" - -class EffectRackView; -class Knob; -class SampleBuffer; -class SampleTrackWindow; -class TrackLabelButton; -class QLineEdit; - - -class SampleTCO : public TrackContentObject -{ - Q_OBJECT - mapPropertyFromModel(bool,isRecord,setRecord,m_recordModel); -public: - SampleTCO( Track * _track ); - SampleTCO( const SampleTCO& orig ); - virtual ~SampleTCO(); - - SampleTCO& operator=( const SampleTCO& that ) = delete; - - void changeLength( const TimePos & _length ) override; - const QString & sampleFile() const; - - void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; - void loadSettings( const QDomElement & _this ) override; - inline QString nodeName() const override - { - return "sampletco"; - } - - SampleBuffer* sampleBuffer() - { - return m_sampleBuffer; - } - - TimePos sampleLength() const; - void setSampleStartFrame( f_cnt_t startFrame ); - void setSamplePlayLength( f_cnt_t length ); - TrackContentObjectView * createView( TrackView * _tv ) override; - - - bool isPlaying() const; - void setIsPlaying(bool isPlaying); - -public slots: - void setSampleBuffer( SampleBuffer* sb ); - void setSampleFile( const QString & _sf ); - void updateLength(); - void toggleRecord(); - void playbackPositionChanged(); - void updateTrackTcos(); - - -private: - SampleBuffer* m_sampleBuffer; - BoolModel m_recordModel; - bool m_isPlaying; - - friend class SampleTCOView; - - -signals: - void sampleChanged(); - void wasReversed(); - -} ; - - - -class SampleTCOView : public TrackContentObjectView -{ - Q_OBJECT - -public: - SampleTCOView( SampleTCO * _tco, TrackView * _tv ); - virtual ~SampleTCOView() = default; - -public slots: - void updateSample(); - void reverseSample(); - - - -protected: - void contextMenuEvent( QContextMenuEvent * _cme ) override; - void mousePressEvent( QMouseEvent * _me ) override; - void mouseReleaseEvent( QMouseEvent * _me ) override; - void dragEnterEvent( QDragEnterEvent * _dee ) override; - void dropEvent( QDropEvent * _de ) override; - void mouseDoubleClickEvent( QMouseEvent * ) override; - void paintEvent( QPaintEvent * ) override; - - -private: - SampleTCO * m_tco; - QPixmap m_paintPixmap; - bool splitTCO( const TimePos pos ) override; -} ; - - class SampleTrack : public Track @@ -204,124 +102,4 @@ private: -class SampleTrackView : public TrackView -{ - Q_OBJECT -public: - SampleTrackView( SampleTrack* Track, TrackContainerView* tcv ); - virtual ~SampleTrackView(); - - SampleTrackWindow * getSampleTrackWindow() - { - return m_window; - } - - SampleTrack * model() - { - return castModel(); - } - - const SampleTrack * model() const - { - return castModel(); - } - - - QMenu * createFxMenu( QString title, QString newFxLabel ) override; - - -public slots: - void showEffects(); - void updateIndicator(); - - -protected: - void modelChanged() override; - QString nodeName() const override - { - return "SampleTrackView"; - } - - void dragEnterEvent(QDragEnterEvent *dee) override; - void dropEvent(QDropEvent *de) override; - -private slots: - void assignFxLine( int channelIndex ); - void createFxLine(); - - -private: - SampleTrackWindow * m_window; - Knob * m_volumeKnob; - Knob * m_panningKnob; - FadeButton * m_activityIndicator; - - TrackLabelButton * m_tlb; - - FadeButton * getActivityIndicator() override - { - return m_activityIndicator; - } - - friend class SampleTrackWindow; - -} ; - - - -class SampleTrackWindow : public QWidget, public ModelView, public SerializingObjectHook -{ - Q_OBJECT -public: - SampleTrackWindow(SampleTrackView * tv); - virtual ~SampleTrackWindow(); - - SampleTrack * model() - { - return castModel(); - } - - const SampleTrack * model() const - { - return castModel(); - } - - void setSampleTrackView(SampleTrackView * tv); - - SampleTrackView *sampleTrackView() - { - return m_stv; - } - - -public slots: - void textChanged(const QString & new_name); - void toggleVisibility(bool on); - void updateName(); - - -protected: - // capture close-events for toggling sample-track-button - void closeEvent(QCloseEvent * ce) override; - - void saveSettings(QDomDocument & doc, QDomElement & element) override; - void loadSettings(const QDomElement & element) override; - -private: - void modelChanged() override; - - SampleTrack * m_track; - SampleTrackView * m_stv; - - // widgets on the top of an sample-track-window - QLineEdit * m_nameLineEdit; - Knob * m_volumeKnob; - Knob * m_panningKnob; - FxLineLcdSpinBox * m_effectChannelNumber; - - EffectRackView * m_effectRack; - -} ; - - -#endif +#endif \ No newline at end of file diff --git a/include/SampleTrackView.h b/include/SampleTrackView.h new file mode 100644 index 000000000..4942cd18d --- /dev/null +++ b/include/SampleTrackView.h @@ -0,0 +1,102 @@ +/* + * SampleTrackView.h + * + * Copyright (c) 2005-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 SAMPLE_TRACK_VIEW_H +#define SAMPLE_TRACK_VIEW_H + +#include "SampleTrack.h" + +#include "TrackView.h" + +class Knob; +class SampleTrack; +class SampleTrackWindow; +class TrackLabelButton; + + +class SampleTrackView : public TrackView +{ + Q_OBJECT +public: + SampleTrackView( SampleTrack* Track, TrackContainerView* tcv ); + virtual ~SampleTrackView(); + + SampleTrackWindow * getSampleTrackWindow() + { + return m_window; + } + + SampleTrack * model() + { + return castModel(); + } + + const SampleTrack * model() const + { + return castModel(); + } + + + QMenu * createFxMenu( QString title, QString newFxLabel ) override; + + +public slots: + void showEffects(); + void updateIndicator(); + + +protected: + void modelChanged() override; + QString nodeName() const override + { + return "SampleTrackView"; + } + + void dragEnterEvent(QDragEnterEvent *dee) override; + void dropEvent(QDropEvent *de) override; + +private slots: + void assignFxLine( int channelIndex ); + void createFxLine(); + + +private: + SampleTrackWindow * m_window; + Knob * m_volumeKnob; + Knob * m_panningKnob; + FadeButton * m_activityIndicator; + + TrackLabelButton * m_tlb; + + FadeButton * getActivityIndicator() override + { + return m_activityIndicator; + } + + friend class SampleTrackWindow; +} ; + + + +#endif \ No newline at end of file diff --git a/include/SampleTrackWindow.h b/include/SampleTrackWindow.h new file mode 100644 index 000000000..e73a66c2c --- /dev/null +++ b/include/SampleTrackWindow.h @@ -0,0 +1,92 @@ +/* + * SampleTrackWindow.h + * + * Copyright (c) 2005-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 SAMPLE_TRACK_WINDOW_H +#define SAMPLE_TRACK_WINDOW_H + +#include "FxLineLcdSpinBox.h" + +#include + +#include "EffectRackView.h" +#include "SampleTrack.h" + + +class SampleTrackWindow : public QWidget, public ModelView, public SerializingObjectHook +{ + Q_OBJECT +public: + SampleTrackWindow(SampleTrackView * tv); + virtual ~SampleTrackWindow(); + + SampleTrack * model() + { + return castModel(); + } + + const SampleTrack * model() const + { + return castModel(); + } + + void setSampleTrackView(SampleTrackView * tv); + + SampleTrackView *sampleTrackView() + { + return m_stv; + } + + +public slots: + void textChanged(const QString & new_name); + void toggleVisibility(bool on); + void updateName(); + + +protected: + // capture close-events for toggling sample-track-button + void closeEvent(QCloseEvent * ce) override; + + void saveSettings(QDomDocument & doc, QDomElement & element) override; + void loadSettings(const QDomElement & element) override; + +private: + void modelChanged() override; + + SampleTrack * m_track; + SampleTrackView * m_stv; + + // widgets on the top of an sample-track-window + QLineEdit * m_nameLineEdit; + Knob * m_volumeKnob; + Knob * m_panningKnob; + FxLineLcdSpinBox * m_effectChannelNumber; + + EffectRackView * m_effectRack; +} ; + + + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 29d6b3148..c211d48a7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -66,6 +66,7 @@ set(LMMS_SRCS core/SampleBuffer.cpp core/SamplePlayHandle.cpp core/SampleRecordHandle.cpp + core/SampleTCO.cpp core/SerializingObject.cpp core/Song.cpp core/TempoSyncKnobModel.cpp diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index 77a254cce..018629357 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -28,7 +28,7 @@ #include "Engine.h" #include "InstrumentTrack.h" #include "Mixer.h" -#include "SampleTrack.h" +#include "SampleTCO.h" diff --git a/src/core/SampleTCO.cpp b/src/core/SampleTCO.cpp new file mode 100644 index 000000000..888fb3cb9 --- /dev/null +++ b/src/core/SampleTCO.cpp @@ -0,0 +1,316 @@ +/* + * SampleTCO.cpp + * + * Copyright (c) 2005-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 "SampleTCO.h" + +#include + +#include "SampleTCOView.h" +#include "TimeLineWidget.h" + +SampleTCO::SampleTCO( Track * _track ) : + TrackContentObject( _track ), + m_sampleBuffer( new SampleBuffer ), + m_isPlaying( false ) +{ + saveJournallingState( false ); + setSampleFile( "" ); + restoreJournallingState(); + + // we need to receive bpm-change-events, because then we have to + // change length of this TCO + connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), + this, SLOT( updateLength() ), Qt::DirectConnection ); + connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ), + this, SLOT( updateLength() ) ); + + //care about positionmarker + TimeLineWidget * timeLine = Engine::getSong()->getPlayPos( Engine::getSong()->Mode_PlaySong ).m_timeLine; + if( timeLine ) + { + connect( timeLine, SIGNAL( positionMarkerMoved() ), this, SLOT( playbackPositionChanged() ) ); + } + //playbutton clicked or space key / on Export Song set isPlaying to false + connect( Engine::getSong(), SIGNAL( playbackStateChanged() ), + this, SLOT( playbackPositionChanged() ), Qt::DirectConnection ); + //care about loops + connect( Engine::getSong(), SIGNAL( updateSampleTracks() ), + this, SLOT( playbackPositionChanged() ), Qt::DirectConnection ); + //care about mute TCOs + connect( this, SIGNAL( dataChanged() ), this, SLOT( playbackPositionChanged() ) ); + //care about mute track + connect( getTrack()->getMutedModel(), SIGNAL( dataChanged() ), + this, SLOT( playbackPositionChanged() ), Qt::DirectConnection ); + //care about TCO position + connect( this, SIGNAL( positionChanged() ), this, SLOT( updateTrackTcos() ) ); + + switch( getTrack()->trackContainer()->type() ) + { + case TrackContainer::BBContainer: + setAutoResize( true ); + break; + + case TrackContainer::SongContainer: + // move down + default: + setAutoResize( false ); + break; + } + updateTrackTcos(); +} + +SampleTCO::SampleTCO(const SampleTCO& orig) : + SampleTCO(orig.getTrack()) +{ + // TODO: This creates a new SampleBuffer for the new TCO, eating up memory + // & eventually causing performance issues. Letting tracks share buffers + // when they're identical would fix this, but isn't possible right now. + *m_sampleBuffer = *orig.m_sampleBuffer; + m_isPlaying = orig.m_isPlaying; +} + + + + +SampleTCO::~SampleTCO() +{ + SampleTrack * sampletrack = dynamic_cast( getTrack() ); + if ( sampletrack ) + { + sampletrack->updateTcos(); + } + Engine::mixer()->requestChangeInModel(); + sharedObject::unref( m_sampleBuffer ); + Engine::mixer()->doneChangeInModel(); +} + + + + +void SampleTCO::changeLength( const TimePos & _length ) +{ + TrackContentObject::changeLength( qMax( static_cast( _length ), 1 ) ); +} + + + + +const QString & SampleTCO::sampleFile() const +{ + return m_sampleBuffer->audioFile(); +} + + + +void SampleTCO::setSampleBuffer( SampleBuffer* sb ) +{ + Engine::mixer()->requestChangeInModel(); + sharedObject::unref( m_sampleBuffer ); + Engine::mixer()->doneChangeInModel(); + m_sampleBuffer = sb; + updateLength(); + + emit sampleChanged(); +} + + + +void SampleTCO::setSampleFile( const QString & _sf ) +{ + int length; + if ( _sf.isEmpty() ) + { //When creating an empty sample pattern make it a bar long + float nom = Engine::getSong()->getTimeSigModel().getNumerator(); + float den = Engine::getSong()->getTimeSigModel().getDenominator(); + length = DefaultTicksPerBar * ( nom / den ); + } + else + { //Otherwise set it to the sample's length + m_sampleBuffer->setAudioFile( _sf ); + length = sampleLength(); + } + changeLength(length); + + setStartTimeOffset( 0 ); + + emit sampleChanged(); + emit playbackPositionChanged(); +} + + + + +void SampleTCO::toggleRecord() +{ + m_recordModel.setValue( !m_recordModel.value() ); + emit dataChanged(); +} + + + + +void SampleTCO::playbackPositionChanged() +{ + Engine::mixer()->removePlayHandlesOfTypes( getTrack(), PlayHandle::TypeSamplePlayHandle ); + SampleTrack * st = dynamic_cast( getTrack() ); + st->setPlayingTcos( false ); +} + + + + +void SampleTCO::updateTrackTcos() +{ + SampleTrack * sampletrack = dynamic_cast( getTrack() ); + if( sampletrack) + { + sampletrack->updateTcos(); + } +} + + + + +bool SampleTCO::isPlaying() const +{ + return m_isPlaying; +} + + + + +void SampleTCO::setIsPlaying(bool isPlaying) +{ + m_isPlaying = isPlaying; +} + + + + +void SampleTCO::updateLength() +{ + emit sampleChanged(); +} + + + + +TimePos SampleTCO::sampleLength() const +{ + return (int)( m_sampleBuffer->frames() / Engine::framesPerTick() ); +} + + + + +void SampleTCO::setSampleStartFrame(f_cnt_t startFrame) +{ + m_sampleBuffer->setStartFrame( startFrame ); +} + + + + +void SampleTCO::setSamplePlayLength(f_cnt_t length) +{ + m_sampleBuffer->setEndFrame( length ); +} + + + + +void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + if( _this.parentNode().nodeName() == "clipboard" ) + { + _this.setAttribute( "pos", -1 ); + } + else + { + _this.setAttribute( "pos", startPosition() ); + } + _this.setAttribute( "len", length() ); + _this.setAttribute( "muted", isMuted() ); + _this.setAttribute( "src", sampleFile() ); + _this.setAttribute( "off", startTimeOffset() ); + if( sampleFile() == "" ) + { + QString s; + _this.setAttribute( "data", m_sampleBuffer->toBase64( s ) ); + } + + _this.setAttribute( "sample_rate", m_sampleBuffer->sampleRate()); + if( usesCustomClipColor() ) + { + _this.setAttribute( "color", color().name() ); + } + if (m_sampleBuffer->reversed()) + { + _this.setAttribute("reversed", "true"); + } + // TODO: start- and end-frame +} + + + + +void SampleTCO::loadSettings( const QDomElement & _this ) +{ + if( _this.attribute( "pos" ).toInt() >= 0 ) + { + movePosition( _this.attribute( "pos" ).toInt() ); + } + setSampleFile( _this.attribute( "src" ) ); + if( sampleFile().isEmpty() && _this.hasAttribute( "data" ) ) + { + m_sampleBuffer->loadFromBase64( _this.attribute( "data" ) ); + } + changeLength( _this.attribute( "len" ).toInt() ); + 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( "color" ) ) + { + useCustomClipColor( true ); + setColor( _this.attribute( "color" ) ); + } + + if(_this.hasAttribute("reversed")) + { + m_sampleBuffer->setReversed(true); + emit wasReversed(); // tell SampleTCOView to update the view + } +} + + + + +TrackContentObjectView * SampleTCO::createView( TrackView * _tv ) +{ + return new SampleTCOView( this, _tv ); +} \ No newline at end of file diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index daee5ada7..d5556d03a 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -33,6 +33,9 @@ SET(LMMS_SRCS gui/PianoView.cpp gui/PluginBrowser.cpp gui/RowTableView.cpp + gui/SampleTCOView.cpp + gui/SampleTrackView.cpp + gui/SampleTrackWindow.cpp gui/SetupDialog.cpp gui/StringPairDrag.cpp gui/SubWindow.cpp diff --git a/src/gui/SampleTCOView.cpp b/src/gui/SampleTCOView.cpp new file mode 100644 index 000000000..ad8738ca7 --- /dev/null +++ b/src/gui/SampleTCOView.cpp @@ -0,0 +1,410 @@ +/* + * SampleTCOView.cpp + * + * Copyright (c) 2005-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 "SampleTCOView.h" + +#include +#include + +#include "embed.h" +#include "gui_templates.h" +#include "PathUtil.h" +#include "Song.h" +#include "StringPairDrag.h" +#include "ToolTip.h" + +SampleTCOView::SampleTCOView( SampleTCO * _tco, TrackView * _tv ) : + TrackContentObjectView( _tco, _tv ), + m_tco( _tco ), + m_paintPixmap() +{ + // update UI and tooltip + updateSample(); + + // track future changes of SampleTCO + connect(m_tco, SIGNAL(sampleChanged()), this, SLOT(updateSample())); + + connect(m_tco, SIGNAL(wasReversed()), this, SLOT(update())); + + setStyle( QApplication::style() ); +} + +void SampleTCOView::updateSample() +{ + update(); + // set tooltip to filename so that user can see what sample this + // sample-tco contains + ToolTip::add( this, ( m_tco->m_sampleBuffer->audioFile() != "" ) ? + PathUtil::toAbsolute(m_tco->m_sampleBuffer->audioFile()) : + tr( "Double-click to open sample" ) ); +} + + + + +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" ), + 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.addAction( embed::getIconPixmap( "record" ), + 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" ), + 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() ); +} + + + + +void SampleTCOView::dragEnterEvent( QDragEnterEvent * _dee ) +{ + if( StringPairDrag::processDragEnterEvent( _dee, + "samplefile,sampledata" ) == false ) + { + TrackContentObjectView::dragEnterEvent( _dee ); + } +} + + + + + + +void SampleTCOView::dropEvent( QDropEvent * _de ) +{ + if( StringPairDrag::decodeKey( _de ) == "samplefile" ) + { + m_tco->setSampleFile( StringPairDrag::decodeValue( _de ) ); + _de->accept(); + } + else if( StringPairDrag::decodeKey( _de ) == "sampledata" ) + { + m_tco->m_sampleBuffer->loadFromBase64( + StringPairDrag::decodeValue( _de ) ); + m_tco->updateLength(); + update(); + _de->accept(); + Engine::getSong()->setModified(); + } + else + { + TrackContentObjectView::dropEvent( _de ); + } +} + + + + +void SampleTCOView::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton && + _me->modifiers() & Qt::ControlModifier && + _me->modifiers() & Qt::ShiftModifier ) + { + m_tco->toggleRecord(); + } + else + { + if( _me->button() == Qt::MiddleButton && _me->modifiers() == Qt::ControlModifier ) + { + SampleTCO * sTco = dynamic_cast( getTrackContentObject() ); + if( sTco ) + { + sTco->updateTrackTcos(); + } + } + TrackContentObjectView::mousePressEvent( _me ); + } +} + + + + +void SampleTCOView::mouseReleaseEvent(QMouseEvent *_me) +{ + if( _me->button() == Qt::MiddleButton && !_me->modifiers() ) + { + SampleTCO * sTco = dynamic_cast( getTrackContentObject() ); + if( sTco ) + { + sTco->playbackPositionChanged(); + } + } + TrackContentObjectView::mouseReleaseEvent( _me ); +} + + + + +void SampleTCOView::mouseDoubleClickEvent( QMouseEvent * ) +{ + QString af = m_tco->m_sampleBuffer->openAudioFile(); + + if ( af.isEmpty() ) {} //Don't do anything if no file is loaded + else if ( af == m_tco->m_sampleBuffer->audioFile() ) + { //Instead of reloading the existing file, just reset the size + int length = (int) ( m_tco->m_sampleBuffer->frames() / Engine::framesPerTick() ); + m_tco->changeLength(length); + } + else + { //Otherwise load the new file as ususal + m_tco->setSampleFile( af ); + Engine::getSong()->setModified(); + } +} + + + + +void SampleTCOView::paintEvent( QPaintEvent * pe ) +{ + QPainter painter( this ); + + if( !needsUpdate() ) + { + painter.drawPixmap( 0, 0, m_paintPixmap ); + return; + } + + setNeedsUpdate( false ); + + if (m_paintPixmap.isNull() || m_paintPixmap.size() != size()) + { + m_paintPixmap = QPixmap(size()); + } + + QPainter p( &m_paintPixmap ); + + bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); + bool selected = isSelected(); + + QLinearGradient lingrad(0, 0, 0, height()); + QColor c = painter.background().color(); + if (muted) { c = c.darker(150); } + if (selected) { c = c.darker(150); } + + lingrad.setColorAt( 1, c.darker( 300 ) ); + lingrad.setColorAt( 0, c ); + + // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds + p.fillRect( rect(), QColor( 0, 0, 0 ) ); + + if( gradient() ) + { + p.fillRect( rect(), lingrad ); + } + else + { + p.fillRect( rect(), c ); + } + + auto tcoColor = m_tco->hasColor() + ? (m_tco->usesCustomClipColor() + ? m_tco->color() + : m_tco->getTrack()->color()) + : painter.pen().brush().color(); + + p.setPen(tcoColor); + + if (muted) + { + QColor penColor = p.pen().brush().color(); + penColor.setHsv(penColor.hsvHue(), penColor.hsvSaturation() / 4, penColor.value()); + p.setPen(penColor.darker(250)); + } + if (selected) + { + p.setPen(p.pen().brush().color().darker(150)); + } + + const int spacing = TCO_BORDER_WIDTH + 1; + const float ppb = fixedTCOs() ? + ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH ) + / (float) m_tco->length().getBar() : + pixelsPerBar(); + + float nom = Engine::getSong()->getTimeSigModel().getNumerator(); + float den = Engine::getSong()->getTimeSigModel().getDenominator(); + float ticksPerBar = DefaultTicksPerBar * nom / den; + + float offset = m_tco->startTimeOffset() / ticksPerBar * pixelsPerBar(); + QRect r = QRect( TCO_BORDER_WIDTH + offset, spacing, + qMax( static_cast( m_tco->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); + m_tco->m_sampleBuffer->visualize( p, r, pe->rect() ); + + 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 ); + + // inner border + 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( 200 ) ); + p.drawRect( 0, 0, rect().right(), rect().bottom() ); + + // draw the 'muted' pixmap only if the pattern was manualy muted + if( m_tco->isMuted() ) + { + const int spacing = TCO_BORDER_WIDTH; + const int size = 14; + p.drawPixmap( spacing, height() - ( size + spacing ), + embed::getIconPixmap( "muted", size, size ) ); + } + + if ( m_marker ) + { + p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); + } + // recording sample tracks is not possible at the moment + + /* if( m_tco->isRecord() ) + { + p.setFont( pointSize<7>( p.font() ) ); + + p.setPen( textShadowColor() ); + p.drawText( 10, p.fontMetrics().height()+1, "Rec" ); + p.setPen( textColor() ); + p.drawText( 9, p.fontMetrics().height(), "Rec" ); + + p.setBrush( QBrush( textColor() ) ); + p.drawEllipse( 4, 5, 4, 4 ); + }*/ + + p.end(); + + painter.drawPixmap( 0, 0, m_paintPixmap ); +} + + + + +void SampleTCOView::reverseSample() +{ + m_tco->sampleBuffer()->setReversed(!m_tco->sampleBuffer()->reversed()); + Engine::getSong()->setModified(); + update(); +} + + + + +//! Split this TCO. +/*! \param pos the position of the split, relative to the start of the clip */ +bool SampleTCOView::splitTCO( const TimePos pos ) +{ + setMarkerEnabled( false ); + + const TimePos splitPos = m_initialTCOPos + pos; + + //Don't split if we slid off the TCO or if we're on the clip's start/end + //Cutting at exactly the start/end position would create a zero length + //clip (bad), and a clip the same length as the original one (pointless). + if ( splitPos > m_initialTCOPos && splitPos < m_initialTCOEnd ) + { + m_tco->getTrack()->addJournalCheckPoint(); + m_tco->getTrack()->saveJournallingState( false ); + + SampleTCO * rightTCO = new SampleTCO ( *m_tco ); + + m_tco->changeLength( splitPos - m_initialTCOPos ); + + rightTCO->movePosition( splitPos ); + rightTCO->changeLength( m_initialTCOEnd - splitPos ); + rightTCO->setStartTimeOffset( m_tco->startTimeOffset() - m_tco->length() ); + + m_tco->getTrack()->restoreJournallingState(); + return true; + } + else { return false; } +} \ No newline at end of file diff --git a/src/gui/SampleTrackView.cpp b/src/gui/SampleTrackView.cpp new file mode 100644 index 000000000..743cffcaf --- /dev/null +++ b/src/gui/SampleTrackView.cpp @@ -0,0 +1,231 @@ +/* + * SampleTrackView.cpp + * + * Copyright (c) 2005-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 "SampleTrackView.h" + +#include + +#include "embed.h" +#include "Engine.h" +#include "FxMixerView.h" +#include "gui_templates.h" +#include "GuiApplication.h" +#include "Knob.h" +#include "MainWindow.h" +#include "SampleTrackWindow.h" +#include "StringPairDrag.h" +#include "TrackContainerView.h" +#include "TrackLabelButton.h" + + +SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : + TrackView( _t, tcv ) +{ + setFixedHeight( 32 ); + + m_tlb = new TrackLabelButton(this, getTrackSettingsWidget()); + m_tlb->setCheckable(true); + connect(m_tlb, SIGNAL(clicked( bool )), + this, SLOT(showEffects())); + m_tlb->setIcon(embed::getIconPixmap("sample_track")); + m_tlb->move(3, 1); + m_tlb->show(); + + m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + tr( "Track volume" ) ); + m_volumeKnob->setVolumeKnob( true ); + m_volumeKnob->setModel( &_t->m_volumeModel ); + m_volumeKnob->setHintText( tr( "Channel volume:" ), "%" ); + + int settingsWidgetWidth = ConfigManager::inst()-> + value( "ui", "compacttrackbuttons" ).toInt() + ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + : DEFAULT_SETTINGS_WIDGET_WIDTH; + m_volumeKnob->move( settingsWidgetWidth - 2 * 24, 2 ); + m_volumeKnob->setLabel( tr( "VOL" ) ); + m_volumeKnob->show(); + + m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + tr( "Panning" ) ); + m_panningKnob->setModel( &_t->m_panningModel ); + m_panningKnob->setHintText( tr( "Panning:" ), "%" ); + m_panningKnob->move( settingsWidgetWidth - 24, 2 ); + m_panningKnob->setLabel( tr( "PAN" ) ); + m_panningKnob->show(); + + m_activityIndicator = new FadeButton( + QApplication::palette().color(QPalette::Active, QPalette::Background), + QApplication::palette().color(QPalette::Active, QPalette::BrightText), + QApplication::palette().color(QPalette::Active, QPalette::BrightText).darker(), + getTrackSettingsWidget() + ); + m_activityIndicator->setGeometry(settingsWidgetWidth - 2 * 24 - 11, 2, 8, 28); + m_activityIndicator->show(); + connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); + + setModel( _t ); + + m_window = new SampleTrackWindow(this); + m_window->toggleVisibility(false); +} + + + + +void SampleTrackView::updateIndicator() +{ + if (model()->isPlaying()) { m_activityIndicator->activateOnce(); } + else { m_activityIndicator->noteEnd(); } +} + + + + +SampleTrackView::~SampleTrackView() +{ + if(m_window != NULL) + { + m_window->setSampleTrackView(NULL); + m_window->parentWidget()->hide(); + } + m_window = NULL; +} + + + +//FIXME: This is identical to InstrumentTrackView::createFxMenu +QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) +{ + int channelIndex = model()->effectChannelModel()->value(); + + FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex); + + // If title allows interpolation, pass channel index and name + if (title.contains("%2")) + { + title = title.arg(channelIndex).arg(fxChannel->m_name); + } + + QMenu *fxMenu = new QMenu(title); + + fxMenu->addAction(newFxLabel, this, SLOT(createFxLine())); + fxMenu->addSeparator(); + + for (int i = 0; i < Engine::fxMixer()->numChannels(); ++i) + { + FxChannel * currentChannel = Engine::fxMixer()->effectChannel(i); + + if (currentChannel != fxChannel) + { + const auto index = currentChannel->m_channelIndex; + QString label = tr("FX %1: %2").arg(currentChannel->m_channelIndex).arg(currentChannel->m_name); + fxMenu->addAction(label, [this, index](){ + assignFxLine(index); + }); + } + } + + return fxMenu; +} + + + + +void SampleTrackView::showEffects() +{ + m_window->toggleVisibility(m_window->parentWidget()->isHidden()); +} + + + +void SampleTrackView::modelChanged() +{ + SampleTrack * st = castModel(); + m_volumeKnob->setModel(&st->m_volumeModel); + + TrackView::modelChanged(); +} + + + + +void SampleTrackView::dragEnterEvent(QDragEnterEvent *dee) +{ + StringPairDrag::processDragEnterEvent(dee, QString("samplefile")); +} + + + + +void SampleTrackView::dropEvent(QDropEvent *de) +{ + QString type = StringPairDrag::decodeKey(de); + QString value = StringPairDrag::decodeValue(de); + + if (type == "samplefile") + { + int trackHeadWidth = ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt()==1 + ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT + : DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; + + int xPos = de->pos().x() < trackHeadWidth + ? trackHeadWidth + : de->pos().x(); + + 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)); + if (sTco) { sTco->setSampleFile(value); } + } +} + + + + +/*! \brief Create and assign a new FX Channel for this track */ +void SampleTrackView::createFxLine() +{ + int channelIndex = gui->fxMixerView()->addNewChannel(); + auto channel = Engine::fxMixer()->effectChannel(channelIndex); + + channel->m_name = getTrack()->name(); + if (getTrack()->useColor()) { channel->setColor (getTrack()->color()); } + + assignFxLine(channelIndex); +} + + + + +/*! \brief Assign a specific FX Channel for this track */ +void SampleTrackView::assignFxLine(int channelIndex) +{ + model()->effectChannelModel()->setValue(channelIndex); + + gui->fxMixerView()->setCurrentFxLine(channelIndex); +} \ No newline at end of file diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp new file mode 100644 index 000000000..0f8baa741 --- /dev/null +++ b/src/gui/SampleTrackWindow.cpp @@ -0,0 +1,265 @@ +/* + * SampleTrackWindow.cpp + * + * Copyright (c) 2005-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 "SampleTrackWindow.h" + +#include +#include + +#include "embed.h" +#include "gui_templates.h" +#include "GuiApplication.h" +#include "Knob.h" +#include "MainWindow.h" +#include "Song.h" +#include "TabWidget.h" +#include "TrackLabelButton.h" + + +SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : + QWidget(), + ModelView(NULL, this), + m_track(tv->model()), + m_stv(tv) +{ + // init own layout + widgets + setFocusPolicy(Qt::StrongFocus); + QVBoxLayout * vlayout = new QVBoxLayout(this); + vlayout->setMargin(0); + vlayout->setSpacing(0); + + TabWidget* generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this); + + QVBoxLayout* generalSettingsLayout = new QVBoxLayout(generalSettingsWidget); + + generalSettingsLayout->setContentsMargins(8, 18, 8, 8); + generalSettingsLayout->setSpacing(6); + + QWidget* nameWidget = new QWidget(generalSettingsWidget); + QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget); + nameLayout->setContentsMargins(0, 0, 0, 0); + nameLayout->setSpacing(2); + + // setup line edit for changing sample track name + m_nameLineEdit = new QLineEdit; + m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font())); + connect(m_nameLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(textChanged(const QString &))); + + m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); + nameLayout->addWidget(m_nameLineEdit); + + + generalSettingsLayout->addWidget(nameWidget); + + + QGridLayout* basicControlsLayout = new QGridLayout; + basicControlsLayout->setHorizontalSpacing(3); + basicControlsLayout->setVerticalSpacing(0); + basicControlsLayout->setContentsMargins(0, 0, 0, 0); + + QString labelStyleSheet = "font-size: 6pt;"; + Qt::Alignment labelAlignment = Qt::AlignHCenter | Qt::AlignTop; + Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter; + + // set up volume knob + m_volumeKnob = new Knob(knobBright_26, NULL, tr("Sample volume")); + m_volumeKnob->setVolumeKnob(true); + m_volumeKnob->setHintText(tr("Volume:"), "%"); + + basicControlsLayout->addWidget(m_volumeKnob, 0, 0); + basicControlsLayout->setAlignment(m_volumeKnob, widgetAlignment); + + QLabel *label = new QLabel(tr("VOL"), this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 0); + basicControlsLayout->setAlignment(label, labelAlignment); + + + // set up panning knob + m_panningKnob = new Knob(knobBright_26, NULL, tr("Panning")); + m_panningKnob->setHintText(tr("Panning:"), ""); + + basicControlsLayout->addWidget(m_panningKnob, 0, 1); + basicControlsLayout->setAlignment(m_panningKnob, widgetAlignment); + + label = new QLabel(tr("PAN"),this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 1); + basicControlsLayout->setAlignment(label, labelAlignment); + + + basicControlsLayout->setColumnStretch(2, 1); + + + // setup spinbox for selecting FX-channel + m_effectChannelNumber = new FxLineLcdSpinBox(2, NULL, tr("FX channel"), m_stv); + + basicControlsLayout->addWidget(m_effectChannelNumber, 0, 3); + basicControlsLayout->setAlignment(m_effectChannelNumber, widgetAlignment); + + label = new QLabel(tr("FX"), this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 3); + basicControlsLayout->setAlignment(label, labelAlignment); + + generalSettingsLayout->addLayout(basicControlsLayout); + + m_effectRack = new EffectRackView(tv->model()->audioPort()->effects()); + m_effectRack->setFixedSize(EffectRackView::DEFAULT_WIDTH, 242); + + vlayout->addWidget(generalSettingsWidget); + vlayout->addWidget(m_effectRack); + + + setModel(tv->model()); + + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget(this); + Qt::WindowFlags flags = subWin->windowFlags(); + flags |= Qt::MSWindowsFixedSizeDialogHint; + flags &= ~Qt::WindowMaximizeButtonHint; + subWin->setWindowFlags(flags); + + // Hide the Size and Maximize options from the system menu + // since the dialog size is fixed. + QMenu * systemMenu = subWin->systemMenu(); + systemMenu->actions().at(2)->setVisible(false); // Size + systemMenu->actions().at(4)->setVisible(false); // Maximize + + subWin->setWindowIcon(embed::getIconPixmap("sample_track")); + subWin->setFixedSize(subWin->size()); + subWin->hide(); +} + +SampleTrackWindow::~SampleTrackWindow() +{ +} + + + +void SampleTrackWindow::setSampleTrackView(SampleTrackView* tv) +{ + if(m_stv && tv) + { + m_stv->m_tlb->setChecked(false); + } + + m_stv = tv; +} + + + +void SampleTrackWindow::modelChanged() +{ + m_track = castModel(); + + m_nameLineEdit->setText(m_track->name()); + + m_track->disconnect(SIGNAL(nameChanged()), this); + + connect(m_track, SIGNAL(nameChanged()), + this, SLOT(updateName())); + + m_volumeKnob->setModel(&m_track->m_volumeModel); + m_panningKnob->setModel(&m_track->m_panningModel); + m_effectChannelNumber->setModel(&m_track->m_effectChannelModel); + + updateName(); +} + + + + +void SampleTrackWindow::updateName() +{ + setWindowTitle(m_track->name().length() > 25 ? (m_track->name().left(24) + "...") : m_track->name()); + + if(m_nameLineEdit->text() != m_track->name()) + { + m_nameLineEdit->setText(m_track->name()); + } +} + + + +void SampleTrackWindow::textChanged(const QString& new_name) +{ + m_track->setName(new_name); + Engine::getSong()->setModified(); +} + + + +void SampleTrackWindow::toggleVisibility(bool on) +{ + if(on) + { + show(); + parentWidget()->show(); + parentWidget()->raise(); + } + else + { + parentWidget()->hide(); + } +} + + + + +void SampleTrackWindow::closeEvent(QCloseEvent* ce) +{ + ce->ignore(); + + if(gui->mainWindow()->workspace()) + { + parentWidget()->hide(); + } + else + { + hide(); + } + + m_stv->m_tlb->setFocus(); + m_stv->m_tlb->setChecked(false); +} + + + +void SampleTrackWindow::saveSettings(QDomDocument& doc, QDomElement & element) +{ + MainWindow::saveWidgetState(this, element); + Q_UNUSED(element) +} + + + +void SampleTrackWindow::loadSettings(const QDomElement& element) +{ + MainWindow::restoreWidgetState(this, element); + if(isVisible()) + { + m_stv->m_tlb->setChecked(true); + } +} \ No newline at end of file diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 79499b6c1..d035e747c 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -22,705 +22,15 @@ * Boston, MA 02110-1301 USA. * */ + #include "SampleTrack.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "BBTrack.h" -#include "EffectRackView.h" -#include "embed.h" -#include "FxMixerView.h" -#include "gui_templates.h" -#include "GuiApplication.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 "TabWidget.h" -#include "TimeLineWidget.h" -#include "ToolTip.h" -#include "TrackLabelButton.h" - -SampleTCO::SampleTCO( Track * _track ) : - TrackContentObject( _track ), - m_sampleBuffer( new SampleBuffer ), - m_isPlaying( false ) -{ - saveJournallingState( false ); - setSampleFile( "" ); - restoreJournallingState(); - - // we need to receive bpm-change-events, because then we have to - // change length of this TCO - connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), - this, SLOT( updateLength() ), Qt::DirectConnection ); - connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ), - this, SLOT( updateLength() ) ); - - //care about positionmarker - TimeLineWidget * timeLine = Engine::getSong()->getPlayPos( Engine::getSong()->Mode_PlaySong ).m_timeLine; - if( timeLine ) - { - connect( timeLine, SIGNAL( positionMarkerMoved() ), this, SLOT( playbackPositionChanged() ) ); - } - //playbutton clicked or space key / on Export Song set isPlaying to false - connect( Engine::getSong(), SIGNAL( playbackStateChanged() ), - this, SLOT( playbackPositionChanged() ), Qt::DirectConnection ); - //care about loops - connect( Engine::getSong(), SIGNAL( updateSampleTracks() ), - this, SLOT( playbackPositionChanged() ), Qt::DirectConnection ); - //care about mute TCOs - connect( this, SIGNAL( dataChanged() ), this, SLOT( playbackPositionChanged() ) ); - //care about mute track - connect( getTrack()->getMutedModel(), SIGNAL( dataChanged() ), - this, SLOT( playbackPositionChanged() ), Qt::DirectConnection ); - //care about TCO position - connect( this, SIGNAL( positionChanged() ), this, SLOT( updateTrackTcos() ) ); - - switch( getTrack()->trackContainer()->type() ) - { - case TrackContainer::BBContainer: - setAutoResize( true ); - break; - - case TrackContainer::SongContainer: - // move down - default: - setAutoResize( false ); - break; - } - updateTrackTcos(); -} - -SampleTCO::SampleTCO(const SampleTCO& orig) : - SampleTCO(orig.getTrack()) -{ - // TODO: This creates a new SampleBuffer for the new TCO, eating up memory - // & eventually causing performance issues. Letting tracks share buffers - // when they're identical would fix this, but isn't possible right now. - *m_sampleBuffer = *orig.m_sampleBuffer; - m_isPlaying = orig.m_isPlaying; -} - - - - -SampleTCO::~SampleTCO() -{ - SampleTrack * sampletrack = dynamic_cast( getTrack() ); - if ( sampletrack ) - { - sampletrack->updateTcos(); - } - Engine::mixer()->requestChangeInModel(); - sharedObject::unref( m_sampleBuffer ); - Engine::mixer()->doneChangeInModel(); -} - - - - -void SampleTCO::changeLength( const TimePos & _length ) -{ - TrackContentObject::changeLength( qMax( static_cast( _length ), 1 ) ); -} - - - - -const QString & SampleTCO::sampleFile() const -{ - return m_sampleBuffer->audioFile(); -} - - - -void SampleTCO::setSampleBuffer( SampleBuffer* sb ) -{ - Engine::mixer()->requestChangeInModel(); - sharedObject::unref( m_sampleBuffer ); - Engine::mixer()->doneChangeInModel(); - m_sampleBuffer = sb; - updateLength(); - - emit sampleChanged(); -} - - - -void SampleTCO::setSampleFile( const QString & _sf ) -{ - int length; - if ( _sf.isEmpty() ) - { //When creating an empty sample pattern make it a bar long - float nom = Engine::getSong()->getTimeSigModel().getNumerator(); - float den = Engine::getSong()->getTimeSigModel().getDenominator(); - length = DefaultTicksPerBar * ( nom / den ); - } - else - { //Otherwise set it to the sample's length - m_sampleBuffer->setAudioFile( _sf ); - length = sampleLength(); - } - changeLength(length); - - setStartTimeOffset( 0 ); - - emit sampleChanged(); - emit playbackPositionChanged(); -} - - - - -void SampleTCO::toggleRecord() -{ - m_recordModel.setValue( !m_recordModel.value() ); - emit dataChanged(); -} - - - - -void SampleTCO::playbackPositionChanged() -{ - Engine::mixer()->removePlayHandlesOfTypes( getTrack(), PlayHandle::TypeSamplePlayHandle ); - SampleTrack * st = dynamic_cast( getTrack() ); - st->setPlayingTcos( false ); -} - - - - -void SampleTCO::updateTrackTcos() -{ - SampleTrack * sampletrack = dynamic_cast( getTrack() ); - if( sampletrack) - { - sampletrack->updateTcos(); - } -} - - - - -bool SampleTCO::isPlaying() const -{ - return m_isPlaying; -} - - - - -void SampleTCO::setIsPlaying(bool isPlaying) -{ - m_isPlaying = isPlaying; -} - - - - -void SampleTCO::updateLength() -{ - emit sampleChanged(); -} - - - - -TimePos SampleTCO::sampleLength() const -{ - return (int)( m_sampleBuffer->frames() / Engine::framesPerTick() ); -} - - - - -void SampleTCO::setSampleStartFrame(f_cnt_t startFrame) -{ - m_sampleBuffer->setStartFrame( startFrame ); -} - - - - -void SampleTCO::setSamplePlayLength(f_cnt_t length) -{ - m_sampleBuffer->setEndFrame( length ); -} - - - - -void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this ) -{ - if( _this.parentNode().nodeName() == "clipboard" ) - { - _this.setAttribute( "pos", -1 ); - } - else - { - _this.setAttribute( "pos", startPosition() ); - } - _this.setAttribute( "len", length() ); - _this.setAttribute( "muted", isMuted() ); - _this.setAttribute( "src", sampleFile() ); - _this.setAttribute( "off", startTimeOffset() ); - if( sampleFile() == "" ) - { - QString s; - _this.setAttribute( "data", m_sampleBuffer->toBase64( s ) ); - } - - _this.setAttribute( "sample_rate", m_sampleBuffer->sampleRate()); - if( usesCustomClipColor() ) - { - _this.setAttribute( "color", color().name() ); - } - if (m_sampleBuffer->reversed()) - { - _this.setAttribute("reversed", "true"); - } - // TODO: start- and end-frame -} - - - - -void SampleTCO::loadSettings( const QDomElement & _this ) -{ - if( _this.attribute( "pos" ).toInt() >= 0 ) - { - movePosition( _this.attribute( "pos" ).toInt() ); - } - setSampleFile( _this.attribute( "src" ) ); - if( sampleFile().isEmpty() && _this.hasAttribute( "data" ) ) - { - m_sampleBuffer->loadFromBase64( _this.attribute( "data" ) ); - } - changeLength( _this.attribute( "len" ).toInt() ); - 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( "color" ) ) - { - useCustomClipColor( true ); - setColor( _this.attribute( "color" ) ); - } - - if(_this.hasAttribute("reversed")) - { - m_sampleBuffer->setReversed(true); - emit wasReversed(); // tell SampleTCOView to update the view - } -} - - - - -TrackContentObjectView * SampleTCO::createView( TrackView * _tv ) -{ - return new SampleTCOView( this, _tv ); -} - - - - -SampleTCOView::SampleTCOView( SampleTCO * _tco, TrackView * _tv ) : - TrackContentObjectView( _tco, _tv ), - m_tco( _tco ), - m_paintPixmap() -{ - // update UI and tooltip - updateSample(); - - // track future changes of SampleTCO - connect(m_tco, SIGNAL(sampleChanged()), this, SLOT(updateSample())); - - connect(m_tco, SIGNAL(wasReversed()), this, SLOT(update())); - - setStyle( QApplication::style() ); -} - -void SampleTCOView::updateSample() -{ - update(); - // set tooltip to filename so that user can see what sample this - // sample-tco contains - ToolTip::add( this, ( m_tco->m_sampleBuffer->audioFile() != "" ) ? - PathUtil::toAbsolute(m_tco->m_sampleBuffer->audioFile()) : - tr( "Double-click to open sample" ) ); -} - - - - -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" ), - 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.addAction( embed::getIconPixmap( "record" ), - 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" ), - 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() ); -} - - - - -void SampleTCOView::dragEnterEvent( QDragEnterEvent * _dee ) -{ - if( StringPairDrag::processDragEnterEvent( _dee, - "samplefile,sampledata" ) == false ) - { - TrackContentObjectView::dragEnterEvent( _dee ); - } -} - - - - - - -void SampleTCOView::dropEvent( QDropEvent * _de ) -{ - if( StringPairDrag::decodeKey( _de ) == "samplefile" ) - { - m_tco->setSampleFile( StringPairDrag::decodeValue( _de ) ); - _de->accept(); - } - else if( StringPairDrag::decodeKey( _de ) == "sampledata" ) - { - m_tco->m_sampleBuffer->loadFromBase64( - StringPairDrag::decodeValue( _de ) ); - m_tco->updateLength(); - update(); - _de->accept(); - Engine::getSong()->setModified(); - } - else - { - TrackContentObjectView::dropEvent( _de ); - } -} - - - - -void SampleTCOView::mousePressEvent( QMouseEvent * _me ) -{ - if( _me->button() == Qt::LeftButton && - _me->modifiers() & Qt::ControlModifier && - _me->modifiers() & Qt::ShiftModifier ) - { - m_tco->toggleRecord(); - } - else - { - if( _me->button() == Qt::MiddleButton && _me->modifiers() == Qt::ControlModifier ) - { - SampleTCO * sTco = dynamic_cast( getTrackContentObject() ); - if( sTco ) - { - sTco->updateTrackTcos(); - } - } - TrackContentObjectView::mousePressEvent( _me ); - } -} - - - - -void SampleTCOView::mouseReleaseEvent(QMouseEvent *_me) -{ - if( _me->button() == Qt::MiddleButton && !_me->modifiers() ) - { - SampleTCO * sTco = dynamic_cast( getTrackContentObject() ); - if( sTco ) - { - sTco->playbackPositionChanged(); - } - } - TrackContentObjectView::mouseReleaseEvent( _me ); -} - - - - -void SampleTCOView::mouseDoubleClickEvent( QMouseEvent * ) -{ - QString af = m_tco->m_sampleBuffer->openAudioFile(); - - if ( af.isEmpty() ) {} //Don't do anything if no file is loaded - else if ( af == m_tco->m_sampleBuffer->audioFile() ) - { //Instead of reloading the existing file, just reset the size - int length = (int) ( m_tco->m_sampleBuffer->frames() / Engine::framesPerTick() ); - m_tco->changeLength(length); - } - else - { //Otherwise load the new file as ususal - m_tco->setSampleFile( af ); - Engine::getSong()->setModified(); - } -} - - - - -void SampleTCOView::paintEvent( QPaintEvent * pe ) -{ - QPainter painter( this ); - - if( !needsUpdate() ) - { - painter.drawPixmap( 0, 0, m_paintPixmap ); - return; - } - - setNeedsUpdate( false ); - - if (m_paintPixmap.isNull() || m_paintPixmap.size() != size()) - { - m_paintPixmap = QPixmap(size()); - } - - QPainter p( &m_paintPixmap ); - - bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); - bool selected = isSelected(); - - QLinearGradient lingrad(0, 0, 0, height()); - QColor c = painter.background().color(); - if (muted) { c = c.darker(150); } - if (selected) { c = c.darker(150); } - - lingrad.setColorAt( 1, c.darker( 300 ) ); - lingrad.setColorAt( 0, c ); - - // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds - p.fillRect( rect(), QColor( 0, 0, 0 ) ); - - if( gradient() ) - { - p.fillRect( rect(), lingrad ); - } - else - { - p.fillRect( rect(), c ); - } - - auto tcoColor = m_tco->hasColor() - ? (m_tco->usesCustomClipColor() - ? m_tco->color() - : m_tco->getTrack()->color()) - : painter.pen().brush().color(); - - p.setPen(tcoColor); - - if (muted) - { - QColor penColor = p.pen().brush().color(); - penColor.setHsv(penColor.hsvHue(), penColor.hsvSaturation() / 4, penColor.value()); - p.setPen(penColor.darker(250)); - } - if (selected) - { - p.setPen(p.pen().brush().color().darker(150)); - } - - const int spacing = TCO_BORDER_WIDTH + 1; - const float ppb = fixedTCOs() ? - ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH ) - / (float) m_tco->length().getBar() : - pixelsPerBar(); - - float nom = Engine::getSong()->getTimeSigModel().getNumerator(); - float den = Engine::getSong()->getTimeSigModel().getDenominator(); - float ticksPerBar = DefaultTicksPerBar * nom / den; - - float offset = m_tco->startTimeOffset() / ticksPerBar * pixelsPerBar(); - QRect r = QRect( TCO_BORDER_WIDTH + offset, spacing, - qMax( static_cast( m_tco->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); - m_tco->m_sampleBuffer->visualize( p, r, pe->rect() ); - - 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 ); - - // inner border - 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( 200 ) ); - p.drawRect( 0, 0, rect().right(), rect().bottom() ); - - // draw the 'muted' pixmap only if the pattern was manualy muted - if( m_tco->isMuted() ) - { - const int spacing = TCO_BORDER_WIDTH; - const int size = 14; - p.drawPixmap( spacing, height() - ( size + spacing ), - embed::getIconPixmap( "muted", size, size ) ); - } - - if ( m_marker ) - { - p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); - } - // recording sample tracks is not possible at the moment - - /* if( m_tco->isRecord() ) - { - p.setFont( pointSize<7>( p.font() ) ); - - p.setPen( textShadowColor() ); - p.drawText( 10, p.fontMetrics().height()+1, "Rec" ); - p.setPen( textColor() ); - p.drawText( 9, p.fontMetrics().height(), "Rec" ); - - p.setBrush( QBrush( textColor() ) ); - p.drawEllipse( 4, 5, 4, 4 ); - }*/ - - p.end(); - - painter.drawPixmap( 0, 0, m_paintPixmap ); -} - - - - -void SampleTCOView::reverseSample() -{ - m_tco->sampleBuffer()->setReversed(!m_tco->sampleBuffer()->reversed()); - Engine::getSong()->setModified(); - update(); -} - - - - -//! Split this TCO. -/*! \param pos the position of the split, relative to the start of the clip */ -bool SampleTCOView::splitTCO( const TimePos pos ) -{ - setMarkerEnabled( false ); - - const TimePos splitPos = m_initialTCOPos + pos; - - //Don't split if we slid off the TCO or if we're on the clip's start/end - //Cutting at exactly the start/end position would create a zero length - //clip (bad), and a clip the same length as the original one (pointless). - if ( splitPos > m_initialTCOPos && splitPos < m_initialTCOEnd ) - { - m_tco->getTrack()->addJournalCheckPoint(); - m_tco->getTrack()->saveJournallingState( false ); - - SampleTCO * rightTCO = new SampleTCO ( *m_tco ); - - m_tco->changeLength( splitPos - m_initialTCOPos ); - - rightTCO->movePosition( splitPos ); - rightTCO->changeLength( m_initialTCOEnd - splitPos ); - rightTCO->setStartTimeOffset( m_tco->startTimeOffset() - m_tco->length() ); - - m_tco->getTrack()->restoreJournallingState(); - return true; - } - else { return false; } -} - - - @@ -931,431 +241,4 @@ void SampleTrack::setPlayingTcos( bool isPlaying ) void SampleTrack::updateEffectChannel() { m_audioPort.setNextFxChannel( m_effectChannelModel.value() ); -} - - - - - - -SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : - TrackView( _t, tcv ) -{ - setFixedHeight( 32 ); - - m_tlb = new TrackLabelButton(this, getTrackSettingsWidget()); - m_tlb->setCheckable(true); - connect(m_tlb, SIGNAL(clicked( bool )), - this, SLOT(showEffects())); - m_tlb->setIcon(embed::getIconPixmap("sample_track")); - m_tlb->move(3, 1); - m_tlb->show(); - - m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), - tr( "Track volume" ) ); - m_volumeKnob->setVolumeKnob( true ); - m_volumeKnob->setModel( &_t->m_volumeModel ); - m_volumeKnob->setHintText( tr( "Channel volume:" ), "%" ); - - int settingsWidgetWidth = ConfigManager::inst()-> - value( "ui", "compacttrackbuttons" ).toInt() - ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT - : DEFAULT_SETTINGS_WIDGET_WIDTH; - m_volumeKnob->move( settingsWidgetWidth - 2 * 24, 2 ); - m_volumeKnob->setLabel( tr( "VOL" ) ); - m_volumeKnob->show(); - - m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), - tr( "Panning" ) ); - m_panningKnob->setModel( &_t->m_panningModel ); - m_panningKnob->setHintText( tr( "Panning:" ), "%" ); - m_panningKnob->move( settingsWidgetWidth - 24, 2 ); - m_panningKnob->setLabel( tr( "PAN" ) ); - m_panningKnob->show(); - - m_activityIndicator = new FadeButton( - QApplication::palette().color(QPalette::Active, QPalette::Background), - QApplication::palette().color(QPalette::Active, QPalette::BrightText), - QApplication::palette().color(QPalette::Active, QPalette::BrightText).darker(), - getTrackSettingsWidget() - ); - m_activityIndicator->setGeometry(settingsWidgetWidth - 2 * 24 - 11, 2, 8, 28); - m_activityIndicator->show(); - connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); - - setModel( _t ); - - m_window = new SampleTrackWindow(this); - m_window->toggleVisibility(false); -} - - - - -void SampleTrackView::updateIndicator() -{ - if (model()->isPlaying()) { m_activityIndicator->activateOnce(); } - else { m_activityIndicator->noteEnd(); } -} - - - - -SampleTrackView::~SampleTrackView() -{ - if(m_window != NULL) - { - m_window->setSampleTrackView(NULL); - m_window->parentWidget()->hide(); - } - m_window = NULL; -} - - - -//FIXME: This is identical to InstrumentTrackView::createFxMenu -QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) -{ - int channelIndex = model()->effectChannelModel()->value(); - - FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex); - - // If title allows interpolation, pass channel index and name - if (title.contains("%2")) - { - title = title.arg(channelIndex).arg(fxChannel->m_name); - } - - QMenu *fxMenu = new QMenu(title); - - fxMenu->addAction(newFxLabel, this, SLOT(createFxLine())); - fxMenu->addSeparator(); - - for (int i = 0; i < Engine::fxMixer()->numChannels(); ++i) - { - FxChannel * currentChannel = Engine::fxMixer()->effectChannel(i); - - if (currentChannel != fxChannel) - { - const auto index = currentChannel->m_channelIndex; - QString label = tr("FX %1: %2").arg(currentChannel->m_channelIndex).arg(currentChannel->m_name); - fxMenu->addAction(label, [this, index](){ - assignFxLine(index); - }); - } - } - - return fxMenu; -} - - - - -void SampleTrackView::showEffects() -{ - m_window->toggleVisibility(m_window->parentWidget()->isHidden()); -} - - - -void SampleTrackView::modelChanged() -{ - SampleTrack * st = castModel(); - m_volumeKnob->setModel(&st->m_volumeModel); - - TrackView::modelChanged(); -} - - - - -void SampleTrackView::dragEnterEvent(QDragEnterEvent *dee) -{ - StringPairDrag::processDragEnterEvent(dee, QString("samplefile")); -} - - - - -void SampleTrackView::dropEvent(QDropEvent *de) -{ - QString type = StringPairDrag::decodeKey(de); - QString value = StringPairDrag::decodeValue(de); - - if (type == "samplefile") - { - int trackHeadWidth = ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt()==1 - ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT - : DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; - - int xPos = de->pos().x() < trackHeadWidth - ? trackHeadWidth - : de->pos().x(); - - 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)); - if (sTco) { sTco->setSampleFile(value); } - } - -} - - - - -SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : - QWidget(), - ModelView(NULL, this), - m_track(tv->model()), - m_stv(tv) -{ - // init own layout + widgets - setFocusPolicy(Qt::StrongFocus); - QVBoxLayout * vlayout = new QVBoxLayout(this); - vlayout->setMargin(0); - vlayout->setSpacing(0); - - TabWidget* generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this); - - QVBoxLayout* generalSettingsLayout = new QVBoxLayout(generalSettingsWidget); - - generalSettingsLayout->setContentsMargins(8, 18, 8, 8); - generalSettingsLayout->setSpacing(6); - - QWidget* nameWidget = new QWidget(generalSettingsWidget); - QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget); - nameLayout->setContentsMargins(0, 0, 0, 0); - nameLayout->setSpacing(2); - - // setup line edit for changing sample track name - m_nameLineEdit = new QLineEdit; - m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font())); - connect(m_nameLineEdit, SIGNAL(textChanged(const QString &)), - this, SLOT(textChanged(const QString &))); - - m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); - nameLayout->addWidget(m_nameLineEdit); - - - generalSettingsLayout->addWidget(nameWidget); - - - QGridLayout* basicControlsLayout = new QGridLayout; - basicControlsLayout->setHorizontalSpacing(3); - basicControlsLayout->setVerticalSpacing(0); - basicControlsLayout->setContentsMargins(0, 0, 0, 0); - - QString labelStyleSheet = "font-size: 6pt;"; - Qt::Alignment labelAlignment = Qt::AlignHCenter | Qt::AlignTop; - Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter; - - // set up volume knob - m_volumeKnob = new Knob(knobBright_26, NULL, tr("Sample volume")); - m_volumeKnob->setVolumeKnob(true); - m_volumeKnob->setHintText(tr("Volume:"), "%"); - - basicControlsLayout->addWidget(m_volumeKnob, 0, 0); - basicControlsLayout->setAlignment(m_volumeKnob, widgetAlignment); - - QLabel *label = new QLabel(tr("VOL"), this); - label->setStyleSheet(labelStyleSheet); - basicControlsLayout->addWidget(label, 1, 0); - basicControlsLayout->setAlignment(label, labelAlignment); - - - // set up panning knob - m_panningKnob = new Knob(knobBright_26, NULL, tr("Panning")); - m_panningKnob->setHintText(tr("Panning:"), ""); - - basicControlsLayout->addWidget(m_panningKnob, 0, 1); - basicControlsLayout->setAlignment(m_panningKnob, widgetAlignment); - - label = new QLabel(tr("PAN"),this); - label->setStyleSheet(labelStyleSheet); - basicControlsLayout->addWidget(label, 1, 1); - basicControlsLayout->setAlignment(label, labelAlignment); - - - basicControlsLayout->setColumnStretch(2, 1); - - - // setup spinbox for selecting FX-channel - m_effectChannelNumber = new FxLineLcdSpinBox(2, NULL, tr("FX channel"), m_stv); - - basicControlsLayout->addWidget(m_effectChannelNumber, 0, 3); - basicControlsLayout->setAlignment(m_effectChannelNumber, widgetAlignment); - - label = new QLabel(tr("FX"), this); - label->setStyleSheet(labelStyleSheet); - basicControlsLayout->addWidget(label, 1, 3); - basicControlsLayout->setAlignment(label, labelAlignment); - - generalSettingsLayout->addLayout(basicControlsLayout); - - m_effectRack = new EffectRackView(tv->model()->audioPort()->effects()); - m_effectRack->setFixedSize(EffectRackView::DEFAULT_WIDTH, 242); - - vlayout->addWidget(generalSettingsWidget); - vlayout->addWidget(m_effectRack); - - - setModel(tv->model()); - - QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget(this); - Qt::WindowFlags flags = subWin->windowFlags(); - flags |= Qt::MSWindowsFixedSizeDialogHint; - flags &= ~Qt::WindowMaximizeButtonHint; - subWin->setWindowFlags(flags); - - // Hide the Size and Maximize options from the system menu - // since the dialog size is fixed. - QMenu * systemMenu = subWin->systemMenu(); - systemMenu->actions().at(2)->setVisible(false); // Size - systemMenu->actions().at(4)->setVisible(false); // Maximize - - subWin->setWindowIcon(embed::getIconPixmap("sample_track")); - subWin->setFixedSize(subWin->size()); - subWin->hide(); -} - - - -SampleTrackWindow::~SampleTrackWindow() -{ -} - - - -void SampleTrackWindow::setSampleTrackView(SampleTrackView* tv) -{ - if(m_stv && tv) - { - m_stv->m_tlb->setChecked(false); - } - - m_stv = tv; -} - - - -void SampleTrackWindow::modelChanged() -{ - m_track = castModel(); - - m_nameLineEdit->setText(m_track->name()); - - m_track->disconnect(SIGNAL(nameChanged()), this); - - connect(m_track, SIGNAL(nameChanged()), - this, SLOT(updateName())); - - m_volumeKnob->setModel(&m_track->m_volumeModel); - m_panningKnob->setModel(&m_track->m_panningModel); - m_effectChannelNumber->setModel(&m_track->m_effectChannelModel); - - updateName(); -} - - - -/*! \brief Create and assign a new FX Channel for this track */ -void SampleTrackView::createFxLine() -{ - int channelIndex = gui->fxMixerView()->addNewChannel(); - auto channel = Engine::fxMixer()->effectChannel(channelIndex); - - channel->m_name = getTrack()->name(); - if (getTrack()->useColor()) { channel->setColor (getTrack()->color()); } - - assignFxLine(channelIndex); -} - - - - -/*! \brief Assign a specific FX Channel for this track */ -void SampleTrackView::assignFxLine(int channelIndex) -{ - model()->effectChannelModel()->setValue(channelIndex); - - gui->fxMixerView()->setCurrentFxLine(channelIndex); -} - - - -void SampleTrackWindow::updateName() -{ - setWindowTitle(m_track->name().length() > 25 ? (m_track->name().left(24) + "...") : m_track->name()); - - if(m_nameLineEdit->text() != m_track->name()) - { - m_nameLineEdit->setText(m_track->name()); - } -} - - - -void SampleTrackWindow::textChanged(const QString& new_name) -{ - m_track->setName(new_name); - Engine::getSong()->setModified(); -} - - - -void SampleTrackWindow::toggleVisibility(bool on) -{ - if(on) - { - show(); - parentWidget()->show(); - parentWidget()->raise(); - } - else - { - parentWidget()->hide(); - } -} - - - - -void SampleTrackWindow::closeEvent(QCloseEvent* ce) -{ - ce->ignore(); - - if(gui->mainWindow()->workspace()) - { - parentWidget()->hide(); - } - else - { - hide(); - } - - m_stv->m_tlb->setFocus(); - m_stv->m_tlb->setChecked(false); -} - - - -void SampleTrackWindow::saveSettings(QDomDocument& doc, QDomElement & element) -{ - MainWindow::saveWidgetState(this, element); - Q_UNUSED(element) -} - - - -void SampleTrackWindow::loadSettings(const QDomElement& element) -{ - MainWindow::restoreWidgetState(this, element); - if(isVisible()) - { - m_stv->m_tlb->setChecked(true); - } -} +} \ No newline at end of file