123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335 |
- /*
- * audio_file_processor.cpp - instrument for using audio-files
- *
- * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
- *
- * 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 <QPainter>
- #include <QBitmap>
- #include <QDomDocument>
- #include <QFileInfo>
- #include <QDropEvent>
- #include <samplerate.h>
- #include "audio_file_processor.h"
- #include "ConfigManager.h"
- #include "Engine.h"
- #include "Song.h"
- #include "InstrumentTrack.h"
- #include "Mixer.h"
- #include "NotePlayHandle.h"
- #include "interpolation.h"
- #include "gui_templates.h"
- #include "ToolTip.h"
- #include "StringPairDrag.h"
- #include "DataFile.h"
- #include "embed.cpp"
- extern "C"
- {
- Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor =
- {
- STRINGIFY( PLUGIN_NAME ),
- "AudioFileProcessor",
- QT_TRANSLATE_NOOP( "pluginBrowser",
- "Simple sampler with various settings for "
- "using samples (e.g. drums) in an "
- "instrument-track" ),
- "Tobias Doerffel <tobydox/at/users.sf.net>",
- 0x0100,
- Plugin::Instrument,
- new PluginPixmapLoader( "logo" ),
- "wav,ogg,ds,spx,au,voc,aif,aiff,flac,raw",
- NULL
- } ;
- }
- audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) :
- Instrument( _instrument_track, &audiofileprocessor_plugin_descriptor ),
- m_sampleBuffer(),
- m_ampModel( 100, 0, 500, 1, this, tr( "Amplify" ) ),
- m_startPointModel( 0, 0, 1, 0.0000001f, this, tr( "Start of sample" ) ),
- m_endPointModel( 1, 0, 1, 0.0000001f, this, tr( "End of sample" ) ),
- m_loopPointModel( 0, 0, 1, 0.0000001f, this, tr( "Loopback point" ) ),
- m_reverseModel( false, this, tr( "Reverse sample" ) ),
- m_loopModel( 0, 0, 2, this, tr( "Loop mode" ) ),
- m_stutterModel( false, this, tr( "Stutter" ) ),
- m_interpolationModel( this, tr( "Interpolation mode" ) ),
- m_nextPlayStartPoint( 0 ),
- m_nextPlayBackwards( false )
- {
- connect( &m_reverseModel, SIGNAL( dataChanged() ),
- this, SLOT( reverseModelChanged() ) );
- connect( &m_ampModel, SIGNAL( dataChanged() ),
- this, SLOT( ampModelChanged() ) );
- connect( &m_startPointModel, SIGNAL( dataChanged() ),
- this, SLOT( startPointChanged() ) );
- connect( &m_endPointModel, SIGNAL( dataChanged() ),
- this, SLOT( endPointChanged() ) );
- connect( &m_loopPointModel, SIGNAL( dataChanged() ),
- this, SLOT( loopPointChanged() ) );
- connect( &m_stutterModel, SIGNAL( dataChanged() ),
- this, SLOT( stutterModelChanged() ) );
-
- //interpolation modes
- m_interpolationModel.addItem( tr( "None" ) );
- m_interpolationModel.addItem( tr( "Linear" ) );
- m_interpolationModel.addItem( tr( "Sinc" ) );
- m_interpolationModel.setValue( 1 );
-
- pointChanged();
- }
- audioFileProcessor::~audioFileProcessor()
- {
- }
- void audioFileProcessor::playNote( NotePlayHandle * _n,
- sampleFrame * _working_buffer )
- {
- const fpp_t frames = _n->framesLeftForCurrentPeriod();
- const f_cnt_t offset = _n->noteOffset();
- // Magic key - a frequency < 20 (say, the bottom piano note if using
- // a A4 base tuning) restarts the start point. The note is not actually
- // played.
- if( m_stutterModel.value() == true && _n->frequency() < 20.0 )
- {
- m_nextPlayStartPoint = m_sampleBuffer.startFrame();
- m_nextPlayBackwards = false;
- return;
- }
- if( !_n->m_pluginData )
- {
- if( m_stutterModel.value() == true && m_nextPlayStartPoint >= m_sampleBuffer.endFrame() )
- {
- // Restart playing the note if in stutter mode, not in loop mode,
- // and we're at the end of the sample.
- m_nextPlayStartPoint = m_sampleBuffer.startFrame();
- m_nextPlayBackwards = false;
- }
- // set interpolation mode for libsamplerate
- int srcmode = SRC_LINEAR;
- switch( m_interpolationModel.value() )
- {
- case 0:
- srcmode = SRC_ZERO_ORDER_HOLD;
- break;
- case 1:
- srcmode = SRC_LINEAR;
- break;
- case 2:
- srcmode = SRC_SINC_MEDIUM_QUALITY;
- break;
- }
- _n->m_pluginData = new handleState( _n->hasDetuningInfo(), srcmode );
- ((handleState *)_n->m_pluginData)->setFrameIndex( m_nextPlayStartPoint );
- ((handleState *)_n->m_pluginData)->setBackwards( m_nextPlayBackwards );
- // debug code
- /* qDebug( "frames %d", m_sampleBuffer.frames() );
- qDebug( "startframe %d", m_sampleBuffer.startFrame() );
- qDebug( "nextPlayStartPoint %d", m_nextPlayStartPoint );*/
- }
- if( ! _n->isFinished() )
- {
- if( m_sampleBuffer.play( _working_buffer + offset,
- (handleState *)_n->m_pluginData,
- frames, _n->frequency(),
- static_cast<SampleBuffer::LoopMode>( m_loopModel.value() ) ) )
- {
- applyRelease( _working_buffer, _n );
- instrumentTrack()->processAudioBuffer( _working_buffer,
- frames + offset, _n );
- emit isPlaying( ((handleState *)_n->m_pluginData)->frameIndex() );
- }
- else
- {
- memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) );
- emit isPlaying( 0 );
- }
- }
- else
- {
- emit isPlaying( 0 );
- }
- if( m_stutterModel.value() == true )
- {
- m_nextPlayStartPoint = ((handleState *)_n->m_pluginData)->frameIndex();
- m_nextPlayBackwards = ((handleState *)_n->m_pluginData)->isBackwards();
- }
- }
- void audioFileProcessor::deleteNotePluginData( NotePlayHandle * _n )
- {
- delete (handleState *)_n->m_pluginData;
- }
- void audioFileProcessor::saveSettings( QDomDocument & _doc,
- QDomElement & _this )
- {
- _this.setAttribute( "src", m_sampleBuffer.audioFile() );
- if( m_sampleBuffer.audioFile() == "" )
- {
- QString s;
- _this.setAttribute( "sampledata",
- m_sampleBuffer.toBase64( s ) );
- }
- m_reverseModel.saveSettings( _doc, _this, "reversed" );
- m_loopModel.saveSettings( _doc, _this, "looped" );
- m_ampModel.saveSettings( _doc, _this, "amp" );
- m_startPointModel.saveSettings( _doc, _this, "sframe" );
- m_endPointModel.saveSettings( _doc, _this, "eframe" );
- m_loopPointModel.saveSettings( _doc, _this, "lframe" );
- m_stutterModel.saveSettings( _doc, _this, "stutter" );
- m_interpolationModel.saveSettings( _doc, _this, "interp" );
- }
- void audioFileProcessor::loadSettings( const QDomElement & _this )
- {
- if( _this.attribute( "src" ) != "" )
- {
- setAudioFile( _this.attribute( "src" ), false );
- QString absolutePath = m_sampleBuffer.tryToMakeAbsolute( m_sampleBuffer.audioFile() );
- if ( !QFileInfo( absolutePath ).exists() )
- {
- QString message = tr( "Sample not found: %1" ).arg( m_sampleBuffer.audioFile() );
- Engine::getSong()->collectError( message );
- }
- }
- else if( _this.attribute( "sampledata" ) != "" )
- {
- m_sampleBuffer.loadFromBase64( _this.attribute( "srcdata" ) );
- }
- m_loopModel.loadSettings( _this, "looped" );
- m_ampModel.loadSettings( _this, "amp" );
- m_endPointModel.loadSettings( _this, "eframe" );
- // compat code for not having a separate loopback point
- if( _this.hasAttribute( "lframe" ) )
- {
- m_loopPointModel.loadSettings( _this, "lframe" );
- m_startPointModel.loadSettings( _this, "sframe" );
- }
- else
- {
- m_loopPointModel.loadSettings( _this, "sframe" );
- m_startPointModel.setValue( m_loopPointModel.value() );
- }
- m_reverseModel.loadSettings( _this, "reversed" );
- m_stutterModel.loadSettings( _this, "stutter" );
- if( _this.hasAttribute( "interp" ) )
- {
- m_interpolationModel.loadSettings( _this, "interp" );
- }
- else
- {
- m_interpolationModel.setValue( 1 ); //linear by default
- }
- pointChanged();
- }
- void audioFileProcessor::loadFile( const QString & _file )
- {
- setAudioFile( _file );
- }
- QString audioFileProcessor::nodeName( void ) const
- {
- return audiofileprocessor_plugin_descriptor.name;
- }
- int audioFileProcessor::getBeatLen( NotePlayHandle * _n ) const
- {
- const float freq_factor = BaseFreq / _n->frequency() *
- Engine::mixer()->processingSampleRate() / Engine::mixer()->baseSampleRate();
- return static_cast<int>( floorf( ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * freq_factor ) );
- }
- PluginView * audioFileProcessor::instantiateView( QWidget * _parent )
- {
- return new AudioFileProcessorView( this, _parent );
- }
- void audioFileProcessor::setAudioFile( const QString & _audio_file,
- bool _rename )
- {
- // is current channel-name equal to previous-filename??
- if( _rename &&
- ( instrumentTrack()->name() ==
- QFileInfo( m_sampleBuffer.audioFile() ).fileName() ||
- m_sampleBuffer.audioFile().isEmpty() ) )
- {
- // then set it to new one
- instrumentTrack()->setName( QFileInfo( _audio_file).fileName() );
- }
- // else we don't touch the track-name, because the user named it self
- m_sampleBuffer.setAudioFile( _audio_file );
- loopPointChanged();
- }
- void audioFileProcessor::reverseModelChanged( void )
- {
- m_sampleBuffer.setReversed( m_reverseModel.value() );
- m_nextPlayStartPoint = m_sampleBuffer.startFrame();
- m_nextPlayBackwards = false;
- }
- void audioFileProcessor::ampModelChanged( void )
- {
- m_sampleBuffer.setAmplification( m_ampModel.value() / 100.0f );
- }
- void audioFileProcessor::stutterModelChanged()
- {
- m_nextPlayStartPoint = m_sampleBuffer.startFrame();
- m_nextPlayBackwards = false;
- }
- void audioFileProcessor::startPointChanged( void )
- {
- // check if start is over end and swap values if so
- if( m_startPointModel.value() > m_endPointModel.value() )
- {
- float tmp = m_endPointModel.value();
- m_endPointModel.setValue( m_startPointModel.value() );
- m_startPointModel.setValue( tmp );
- }
- // nudge loop point with end
- if( m_loopPointModel.value() >= m_endPointModel.value() )
- {
- m_loopPointModel.setValue( qMax( m_endPointModel.value() - 0.001f, 0.0f ) );
- }
- // nudge loop point with start
- if( m_loopPointModel.value() < m_startPointModel.value() )
- {
- m_loopPointModel.setValue( m_startPointModel.value() );
- }
- // check if start & end overlap and nudge end up if so
- if( m_startPointModel.value() == m_endPointModel.value() )
- {
- m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) );
- }
-
- pointChanged();
- }
- void audioFileProcessor::endPointChanged( void )
- {
- // same as start, for now
- startPointChanged();
- }
- void audioFileProcessor::loopPointChanged( void )
- {
- // check that loop point is between start-end points and not overlapping with endpoint
- // ...and move start/end points ahead if loop point is moved over them
- if( m_loopPointModel.value() >= m_endPointModel.value() )
- {
- m_endPointModel.setValue( m_loopPointModel.value() + 0.001f );
- if( m_endPointModel.value() == 1.0f )
- {
- m_loopPointModel.setValue( 1.0f - 0.001f );
- }
- }
- // nudge start point with loop
- if( m_loopPointModel.value() < m_startPointModel.value() )
- {
- m_startPointModel.setValue( m_loopPointModel.value() );
- }
- pointChanged();
- }
- void audioFileProcessor::pointChanged( void )
- {
- const f_cnt_t f_start = static_cast<f_cnt_t>( m_startPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
- const f_cnt_t f_end = static_cast<f_cnt_t>( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
- const f_cnt_t f_loop = static_cast<f_cnt_t>( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
- m_nextPlayStartPoint = f_start;
- m_nextPlayBackwards = false;
- m_sampleBuffer.setAllPointFrames( f_start, f_end, f_loop, f_end );
- emit dataChanged();
- }
- QPixmap * AudioFileProcessorView::s_artwork = NULL;
- AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
- QWidget * _parent ) :
- InstrumentView( _instrument, _parent )
- {
- if( s_artwork == NULL )
- {
- s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap(
- "artwork" ) );
- }
- m_openAudioFileButton = new PixmapButton( this );
- m_openAudioFileButton->setCursor( QCursor( Qt::PointingHandCursor ) );
- m_openAudioFileButton->move( 227, 72 );
- m_openAudioFileButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
- "select_file" ) );
- m_openAudioFileButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
- "select_file" ) );
- connect( m_openAudioFileButton, SIGNAL( clicked() ),
- this, SLOT( openAudioFile() ) );
- ToolTip::add( m_openAudioFileButton, tr( "Open other sample" ) );
- m_openAudioFileButton->setWhatsThis(
- tr( "Click here, if you want to open another audio-file. "
- "A dialog will appear where you can select your file. "
- "Settings like looping-mode, start and end-points, "
- "amplify-value, and so on are not reset. So, it may not "
- "sound like the original sample.") );
- m_reverseButton = new PixmapButton( this );
- m_reverseButton->setCheckable( true );
- m_reverseButton->move( 164, 105 );
- m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
- "reverse_on" ) );
- m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
- "reverse_off" ) );
- ToolTip::add( m_reverseButton, tr( "Reverse sample" ) );
- m_reverseButton->setWhatsThis(
- tr( "If you enable this button, the whole sample is reversed. "
- "This is useful for cool effects, e.g. a reversed "
- "crash." ) );
- // loop button group
- PixmapButton * m_loopOffButton = new PixmapButton( this );
- m_loopOffButton->setCheckable( true );
- m_loopOffButton->move( 190, 105 );
- m_loopOffButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
- "loop_off_on" ) );
- m_loopOffButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
- "loop_off_off" ) );
- ToolTip::add( m_loopOffButton, tr( "Disable loop" ) );
- m_loopOffButton->setWhatsThis(
- tr( "This button disables looping. "
- "The sample plays only once from start to end. " ) );
- PixmapButton * m_loopOnButton = new PixmapButton( this );
- m_loopOnButton->setCheckable( true );
- m_loopOnButton->move( 190, 124 );
- m_loopOnButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
- "loop_on_on" ) );
- m_loopOnButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
- "loop_on_off" ) );
- ToolTip::add( m_loopOnButton, tr( "Enable loop" ) );
- m_loopOnButton->setWhatsThis(
- tr( "This button enables forwards-looping. "
- "The sample loops between the end point and the loop point." ) );
- PixmapButton * m_loopPingPongButton = new PixmapButton( this );
- m_loopPingPongButton->setCheckable( true );
- m_loopPingPongButton->move( 216, 124 );
- m_loopPingPongButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
- "loop_pingpong_on" ) );
- m_loopPingPongButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
- "loop_pingpong_off" ) );
- ToolTip::add( m_loopPingPongButton, tr( "Enable ping-pong loop" ) );
- m_loopPingPongButton->setWhatsThis(
- tr( "This button enables ping-pong-looping. "
- "The sample loops backwards and forwards between the end point "
- "and the loop point." ) );
- m_loopGroup = new automatableButtonGroup( this );
- m_loopGroup->addButton( m_loopOffButton );
- m_loopGroup->addButton( m_loopOnButton );
- m_loopGroup->addButton( m_loopPingPongButton );
- m_stutterButton = new PixmapButton( this );
- m_stutterButton->setCheckable( true );
- m_stutterButton->move( 164, 124 );
- m_stutterButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
- "stutter_on" ) );
- m_stutterButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
- "stutter_off" ) );
- ToolTip::add( m_stutterButton,
- tr( "Continue sample playback across notes" ) );
- m_stutterButton->setWhatsThis(
- tr( "Enabling this option makes the sample continue playing "
- "across different notes - if you change pitch, or the note "
- "length stops before the end of the sample, then the next "
- "note played will continue where it left off. To reset the "
- "playback to the start of the sample, insert a note at the bottom "
- "of the keyboard (< 20 Hz)") );
- m_ampKnob = new Knob( knobBright_26, this );
- m_ampKnob->setVolumeKnob( true );
- m_ampKnob->move( 5, 108 );
- m_ampKnob->setHintText( tr( "Amplify:" ), "%" );
- m_ampKnob->setWhatsThis(
- tr( "With this knob you can set the amplify ratio. When you "
- "set a value of 100% your sample isn't changed. "
- "Otherwise it will be amplified up or down (your "
- "actual sample-file isn't touched!)" ) );
- m_startKnob = new AudioFileProcessorWaveView::knob( this );
- m_startKnob->move( 45, 108 );
- m_startKnob->setHintText( tr( "Startpoint:" ), "" );
- m_startKnob->setWhatsThis(
- tr( "With this knob you can set the point where "
- "AudioFileProcessor should begin playing your sample. " ) );
- m_endKnob = new AudioFileProcessorWaveView::knob( this );
- m_endKnob->move( 125, 108 );
- m_endKnob->setHintText( tr( "Endpoint:" ), "" );
- m_endKnob->setWhatsThis(
- tr( "With this knob you can set the point where "
- "AudioFileProcessor should stop playing your sample. " ) );
- m_loopKnob = new AudioFileProcessorWaveView::knob( this );
- m_loopKnob->move( 85, 108 );
- m_loopKnob->setHintText( tr( "Loopback point:" ), "" );
- m_loopKnob->setWhatsThis(
- tr( "With this knob you can set the point where "
- "the loop starts. " ) );
- // interpolation selector
- m_interpBox = new ComboBox( this );
- m_interpBox->setGeometry( 142, 62, 82, 22 );
- m_interpBox->setFont( pointSize<8>( m_interpBox->font() ) );
- // wavegraph
- m_waveView = 0;
- newWaveView();
- connect( castModel<audioFileProcessor>(), SIGNAL( isPlaying( f_cnt_t ) ),
- m_waveView, SLOT( isPlaying( f_cnt_t ) ) );
- qRegisterMetaType<f_cnt_t>( "f_cnt_t" );
- setAcceptDrops( true );
- }
- AudioFileProcessorView::~AudioFileProcessorView()
- {
- }
- void AudioFileProcessorView::dragEnterEvent( QDragEnterEvent * _dee )
- {
- if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) )
- {
- QString txt = _dee->mimeData()->data(
- StringPairDrag::mimeType() );
- if( txt.section( ':', 0, 0 ) == QString( "tco_%1" ).arg(
- Track::SampleTrack ) )
- {
- _dee->acceptProposedAction();
- }
- else if( txt.section( ':', 0, 0 ) == "samplefile" )
- {
- _dee->acceptProposedAction();
- }
- else
- {
- _dee->ignore();
- }
- }
- else
- {
- _dee->ignore();
- }
- }
- void AudioFileProcessorView::newWaveView()
- {
- if ( m_waveView )
- {
- delete m_waveView;
- m_waveView = 0;
- }
- m_waveView = new AudioFileProcessorWaveView( this, 245, 75, castModel<audioFileProcessor>()->m_sampleBuffer );
- m_waveView->move( 2, 172 );
- m_waveView->setKnobs(
- dynamic_cast<AudioFileProcessorWaveView::knob *>( m_startKnob ),
- dynamic_cast<AudioFileProcessorWaveView::knob *>( m_endKnob ),
- dynamic_cast<AudioFileProcessorWaveView::knob *>( m_loopKnob ) );
- m_waveView->show();
- }
- void AudioFileProcessorView::dropEvent( QDropEvent * _de )
- {
- QString type = StringPairDrag::decodeKey( _de );
- QString value = StringPairDrag::decodeValue( _de );
- if( type == "samplefile" )
- {
- castModel<audioFileProcessor>()->setAudioFile( value );
- _de->accept();
- newWaveView();
- return;
- }
- else if( type == QString( "tco_%1" ).arg( Track::SampleTrack ) )
- {
- DataFile dataFile( value.toUtf8() );
- castModel<audioFileProcessor>()->setAudioFile( dataFile.content().firstChild().toElement().attribute( "src" ) );
- _de->accept();
- return;
- }
- _de->ignore();
- }
- void AudioFileProcessorView::paintEvent( QPaintEvent * )
- {
- QPainter p( this );
- p.drawPixmap( 0, 0, *s_artwork );
- audioFileProcessor * a = castModel<audioFileProcessor>();
- QString file_name = "";
- int idx = a->m_sampleBuffer.audioFile().length();
- p.setFont( pointSize<8>( font() ) );
- QFontMetrics fm( p.font() );
- // simple algorithm for creating a text from the filename that
- // matches in the white rectangle
- while( idx > 0 &&
- fm.size( Qt::TextSingleLine, file_name + "..." ).width() < 210 )
- {
- file_name = a->m_sampleBuffer.audioFile()[--idx] + file_name;
- }
- if( idx > 0 )
- {
- file_name = "..." + file_name;
- }
- p.setPen( QColor( 255, 255, 255 ) );
- p.drawText( 8, 99, file_name );
- }
- void AudioFileProcessorView::sampleUpdated( void )
- {
- m_waveView->updateSampleRange();
- m_waveView->update();
- update();
- }
- void AudioFileProcessorView::openAudioFile( void )
- {
- QString af = castModel<audioFileProcessor>()->m_sampleBuffer.
- openAudioFile();
- if( af != "" )
- {
- castModel<audioFileProcessor>()->setAudioFile( af );
- Engine::getSong()->setModified();
- m_waveView->updateSampleRange();
- }
- }
- void AudioFileProcessorView::modelChanged( void )
- {
- audioFileProcessor * a = castModel<audioFileProcessor>();
- connect( &a->m_sampleBuffer, SIGNAL( sampleUpdated() ),
- this, SLOT( sampleUpdated() ) );
- m_ampKnob->setModel( &a->m_ampModel );
- m_startKnob->setModel( &a->m_startPointModel );
- m_endKnob->setModel( &a->m_endPointModel );
- m_loopKnob->setModel( &a->m_loopPointModel );
- m_reverseButton->setModel( &a->m_reverseModel );
- m_loopGroup->setModel( &a->m_loopModel );
- m_stutterButton->setModel( &a->m_stutterModel );
- m_interpBox->setModel( &a->m_interpolationModel );
- sampleUpdated();
- }
- void AudioFileProcessorWaveView::updateSampleRange()
- {
- if( m_sampleBuffer.frames() > 1 )
- {
- const f_cnt_t marging = ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * 0.1;
- m_from = qMax( 0, m_sampleBuffer.startFrame() - marging );
- m_to = qMin( m_sampleBuffer.endFrame() + marging, m_sampleBuffer.frames() );
- }
- }
- AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, SampleBuffer& buf ) :
- QWidget( _parent ),
- m_sampleBuffer( buf ),
- m_graph( QPixmap( _w - 2 * s_padding, _h - 2 * s_padding ) ),
- m_from( 0 ),
- m_to( m_sampleBuffer.frames() ),
- m_last_from( 0 ),
- m_last_to( 0 ),
- m_last_amp( 0 ),
- m_startKnob( 0 ),
- m_endKnob( 0 ),
- m_loopKnob( 0 ),
- m_isDragging( false ),
- m_reversed( false ),
- m_framesPlayed( 0 ),
- m_animation(ConfigManager::inst()->value("ui", "animateafp").toInt())
- {
- setFixedSize( _w, _h );
- setMouseTracking( true );
- updateSampleRange();
- m_graph.fill( Qt::transparent );
- update();
- updateCursor();
- }
- void AudioFileProcessorWaveView::isPlaying( f_cnt_t _current_frame )
- {
- m_framesPlayed = _current_frame;
- update();
- }
- void AudioFileProcessorWaveView::enterEvent( QEvent * _e )
- {
- updateCursor();
- }
- void AudioFileProcessorWaveView::leaveEvent( QEvent * _e )
- {
- updateCursor();
- }
- void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me )
- {
- m_isDragging = true;
- m_draggingLastPoint = _me->pos();
- const int x = _me->x();
- const int start_dist = qAbs( m_startFrameX - x );
- const int end_dist = qAbs( m_endFrameX - x );
- const int loop_dist = qAbs( m_loopFrameX - x );
- draggingType dt = sample_loop; int md = loop_dist;
- if( start_dist < loop_dist ) { dt = sample_start; md = start_dist; }
- else if( end_dist < loop_dist ) { dt = sample_end; md = end_dist; }
- if( md < 4 )
- {
- m_draggingType = dt;
- }
- else
- {
- m_draggingType = wave;
- updateCursor(_me);
- }
- }
- void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me )
- {
- m_isDragging = false;
- if( m_draggingType == wave )
- {
- updateCursor(_me);
- }
- }
- void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me )
- {
- if( ! m_isDragging )
- {
- updateCursor(_me);
- return;
- }
- const int step = _me->x() - m_draggingLastPoint.x();
- switch( m_draggingType )
- {
- case sample_start:
- slideSamplePointByPx( start, step );
- break;
- case sample_end:
- slideSamplePointByPx( end, step );
- break;
- case sample_loop:
- slideSamplePointByPx( loop, step );
- break;
- case wave:
- default:
- if( qAbs( _me->y() - m_draggingLastPoint.y() )
- < 2 * qAbs( _me->x() - m_draggingLastPoint.x() ) )
- {
- slide( step );
- }
- else
- {
- zoom( _me->y() < m_draggingLastPoint.y() );
- }
- }
- m_draggingLastPoint = _me->pos();
- update();
- }
- void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we )
- {
- zoom( _we->delta() > 0 );
- update();
- }
- void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe )
- {
- QPainter p( this );
- p.drawPixmap( s_padding, s_padding, m_graph );
- const QRect graph_rect( s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding );
- const f_cnt_t frames = m_to - m_from;
- m_startFrameX = graph_rect.x() + ( m_sampleBuffer.startFrame() - m_from ) *
- double( graph_rect.width() ) / frames;
- m_endFrameX = graph_rect.x() + ( m_sampleBuffer.endFrame() - m_from ) *
- double( graph_rect.width() ) / frames;
- m_loopFrameX = graph_rect.x() + ( m_sampleBuffer.loopStartFrame() - m_from ) *
- double( graph_rect.width() ) / frames;
- const int played_width_px = ( m_framesPlayed - m_from ) *
- double( graph_rect.width() ) / frames;
- // loop point line
- p.setPen( QColor( 0x7F, 0xFF, 0xFF ) ); //TODO: put into a qproperty
- p.drawLine( m_loopFrameX, graph_rect.y(),
- m_loopFrameX,
- graph_rect.height() + graph_rect.y() );
- // start/end lines
- p.setPen( QColor( 0xFF, 0xFF, 0xFF ) ); //TODO: put into a qproperty
- p.drawLine( m_startFrameX, graph_rect.y(),
- m_startFrameX,
- graph_rect.height() + graph_rect.y() );
- p.drawLine( m_endFrameX, graph_rect.y(),
- m_endFrameX,
- graph_rect.height() + graph_rect.y() );
- if( m_endFrameX - m_startFrameX > 2 )
- {
- p.fillRect(
- m_startFrameX + 1,
- graph_rect.y(),
- m_endFrameX - m_startFrameX - 1,
- graph_rect.height() + graph_rect.y(),
- QColor( 95, 175, 255, 50 ) //TODO: put into a qproperty
- );
- if( m_endFrameX - m_loopFrameX > 2 )
- p.fillRect(
- m_loopFrameX + 1,
- graph_rect.y(),
- m_endFrameX - m_loopFrameX - 1,
- graph_rect.height() + graph_rect.y(),
- QColor( 95, 205, 255, 65 ) //TODO: put into a qproperty
- );
- if( m_framesPlayed && m_animation)
- {
- QLinearGradient g( m_startFrameX, 0, played_width_px, 0 );
- const QColor c( 0, 120, 255, 180 ); //TODO: put into a qproperty
- g.setColorAt( 0, Qt::transparent );
- g.setColorAt( 0.8, c );
- g.setColorAt( 1, c );
- p.fillRect(
- m_startFrameX + 1,
- graph_rect.y(),
- played_width_px - ( m_startFrameX + 1 ),
- graph_rect.height() + graph_rect.y(),
- g
- );
- p.setPen( QColor( 255, 255, 255 ) ); //TODO: put into a qproperty
- p.drawLine(
- played_width_px,
- graph_rect.y(),
- played_width_px,
- graph_rect.height() + graph_rect.y()
- );
- m_framesPlayed = 0;
- }
- }
- QLinearGradient g( 0, 0, width() * 0.7, 0 );
- const QColor c( 16, 111, 170, 180 );
- g.setColorAt( 0, c );
- g.setColorAt( 0.4, c );
- g.setColorAt( 1, Qt::transparent );
- p.fillRect( s_padding, s_padding, m_graph.width(), 14, g );
- p.setPen( QColor( 255, 255, 255 ) );
- p.setFont( pointSize<8>( font() ) );
- QString length_text;
- const int length = m_sampleBuffer.sampleLength();
- if( length > 20000 )
- {
- length_text = QString::number( length / 1000 ) + "s";
- }
- else if( length > 2000 )
- {
- length_text = QString::number( ( length / 100 ) / 10.0 ) + "s";
- }
- else
- {
- length_text = QString::number( length ) + "ms";
- }
- p.drawText(
- s_padding + 2,
- s_padding + 10,
- tr( "Sample length:" ) + " " + length_text
- );
- }
- void AudioFileProcessorWaveView::updateGraph()
- {
- if( m_to == 1 )
- {
- m_to = m_sampleBuffer.frames() * 0.7;
- slideSamplePointToFrames( end, m_to * 0.7 );
- }
- if( m_from > m_sampleBuffer.startFrame() )
- {
- m_from = m_sampleBuffer.startFrame();
- }
- if( m_to < m_sampleBuffer.endFrame() )
- {
- m_to = m_sampleBuffer.endFrame();
- }
- if( m_sampleBuffer.reversed() != m_reversed )
- {
- reverse();
- }
- else if( m_last_from == m_from && m_last_to == m_to && m_sampleBuffer.amplification() == m_last_amp )
- {
- return;
- }
- m_last_from = m_from;
- m_last_to = m_to;
- m_last_amp = m_sampleBuffer.amplification();
- m_graph.fill( Qt::transparent );
- QPainter p( &m_graph );
- p.setPen( QColor( 255, 255, 255 ) );
- m_sampleBuffer.visualize(
- p,
- QRect( 0, 0, m_graph.width(), m_graph.height() ),
- m_from, m_to
- );
- }
- void AudioFileProcessorWaveView::zoom( const bool _out )
- {
- const f_cnt_t start = m_sampleBuffer.startFrame();
- const f_cnt_t end = m_sampleBuffer.endFrame();
- const f_cnt_t frames = m_sampleBuffer.frames();
- const f_cnt_t d_from = start - m_from;
- const f_cnt_t d_to = m_to - end;
- const f_cnt_t step = qMax( 1, qMax( d_from, d_to ) / 10 );
- const f_cnt_t step_from = ( _out ? - step : step );
- const f_cnt_t step_to = ( _out ? step : - step );
- const double comp_ratio = double( qMin( d_from, d_to ) )
- / qMax( 1, qMax( d_from, d_to ) );
- f_cnt_t new_from;
- f_cnt_t new_to;
- if( ( _out && d_from < d_to ) || ( ! _out && d_to < d_from ) )
- {
- new_from = qBound( 0, m_from + step_from, start );
- new_to = qBound(
- end,
- m_to + f_cnt_t( step_to * ( new_from == m_from ? 1 : comp_ratio ) ),
- frames
- );
- }
- else
- {
- new_to = qBound( end, m_to + step_to, frames );
- new_from = qBound(
- 0,
- m_from + f_cnt_t( step_from * ( new_to == m_to ? 1 : comp_ratio ) ),
- start
- );
- }
- if( double( new_to - new_from ) / m_sampleBuffer.sampleRate() > 0.05 )
- {
- m_from = new_from;
- m_to = new_to;
- }
- }
- void AudioFileProcessorWaveView::slide( int _px )
- {
- const double fact = qAbs( double( _px ) / width() );
- f_cnt_t step = ( m_to - m_from ) * fact;
- if( _px > 0 )
- {
- step = -step;
- }
- f_cnt_t step_from = qBound( 0, m_from + step, m_sampleBuffer.frames() ) - m_from;
- f_cnt_t step_to = qBound( m_from + 1, m_to + step, m_sampleBuffer.frames() ) - m_to;
- step = qAbs( step_from ) < qAbs( step_to ) ? step_from : step_to;
- m_from += step;
- m_to += step;
- slideSampleByFrames( step );
- }
- void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end, knob * _loop )
- {
- m_startKnob = _start;
- m_endKnob = _end;
- m_loopKnob = _loop;
- m_startKnob->setWaveView( this );
- m_startKnob->setRelatedKnob( m_endKnob );
- m_endKnob->setWaveView( this );
- m_endKnob->setRelatedKnob( m_startKnob );
- m_loopKnob->setWaveView( this );
- }
- void AudioFileProcessorWaveView::slideSamplePointByPx( knobType _point, int _px )
- {
- slideSamplePointByFrames(
- _point,
- f_cnt_t( ( double( _px ) / width() ) * ( m_to - m_from ) )
- );
- }
- void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to )
- {
- knob * a_knob = m_startKnob;
- switch( _point )
- {
- case end:
- a_knob = m_endKnob;
- break;
- case loop:
- a_knob = m_loopKnob;
- break;
- case start:
- break;
- }
- if( a_knob == NULL )
- {
- return;
- }
- else
- {
- const double v = static_cast<double>( _frames ) / m_sampleBuffer.frames();
- if( _slide_to )
- {
- a_knob->slideTo( v );
- }
- else
- {
- a_knob->slideBy( v );
- }
- }
- }
- void AudioFileProcessorWaveView::slideSampleByFrames( f_cnt_t _frames )
- {
- if( m_sampleBuffer.frames() <= 1 )
- {
- return;
- }
- const double v = static_cast<double>( _frames ) / m_sampleBuffer.frames();
- if( m_startKnob ) {
- m_startKnob->slideBy( v, false );
- }
- if( m_endKnob ) {
- m_endKnob->slideBy( v, false );
- }
- if( m_loopKnob ) {
- m_loopKnob->slideBy( v, false );
- }
- }
- void AudioFileProcessorWaveView::reverse()
- {
- slideSampleByFrames(
- m_sampleBuffer.frames()
- - m_sampleBuffer.endFrame()
- - m_sampleBuffer.startFrame()
- );
- const f_cnt_t from = m_from;
- m_from = m_sampleBuffer.frames() - m_to;
- m_to = m_sampleBuffer.frames() - from;
- m_reversed = ! m_reversed;
- }
- void AudioFileProcessorWaveView::updateCursor( QMouseEvent * _me )
- {
- bool const waveIsDragged = m_isDragging && (m_draggingType == wave);
- bool const pointerCloseToStartEndOrLoop = (_me != nullptr ) &&
- ( isCloseTo( _me->x(), m_startFrameX ) ||
- isCloseTo( _me->x(), m_endFrameX ) ||
- isCloseTo( _me->x(), m_loopFrameX ) );
- if( !m_isDragging && pointerCloseToStartEndOrLoop)
- setCursor(Qt::SizeHorCursor);
- else if( waveIsDragged )
- setCursor(Qt::ClosedHandCursor);
- else
- setCursor(Qt::OpenHandCursor);
- }
- void AudioFileProcessorWaveView::knob::slideTo( double _v, bool _check_bound )
- {
- if( _check_bound && ! checkBound( _v ) )
- {
- return;
- }
- model()->setValue( _v );
- emit sliderMoved( model()->value() );
- }
- float AudioFileProcessorWaveView::knob::getValue( const QPoint & _p )
- {
- const double dec_fact = ! m_waveView ? 1 :
- double( m_waveView->m_to - m_waveView->m_from )
- / m_waveView->m_sampleBuffer.frames();
- const float inc = ::Knob::getValue( _p ) * dec_fact;
- return inc;
- }
- bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const
- {
- if( ! m_relatedKnob || ! m_waveView )
- {
- return true;
- }
- if( ( m_relatedKnob->model()->value() - _v > 0 ) !=
- ( m_relatedKnob->model()->value() - model()->value() >= 0 ) )
- return false;
- const double d1 = qAbs( m_relatedKnob->model()->value() - model()->value() )
- * ( m_waveView->m_sampleBuffer.frames() )
- / m_waveView->m_sampleBuffer.sampleRate();
- const double d2 = qAbs( m_relatedKnob->model()->value() - _v )
- * ( m_waveView->m_sampleBuffer.frames() )
- / m_waveView->m_sampleBuffer.sampleRate();
- return d1 < d2 || d2 > 0.005;
- }
- extern "C"
- {
- // necessary for getting instance out of shared lib
- Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
- {
- return new audioFileProcessor(
- static_cast<InstrumentTrack *>( _data ) );
- }
- }
|