hydrogen 1.2.6
SampleEditor.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include "SampleEditor.h"
24#include "../HydrogenApp.h"
25#include "../CommonStrings.h"
28
30#include "DetailWaveDisplay.h"
31#include "TargetWaveDisplay.h"
32
33#include <core/H2Exception.h>
35#include <core/Basics/Sample.h>
36#include <core/Basics/Note.h>
43#include <core/Hydrogen.h>
44
45#include <QModelIndex>
46#include <QTreeWidget>
47#include <QMessageBox>
48#include <algorithm>
49#include <memory>
50
51using namespace H2Core;
52
53
54SampleEditor::SampleEditor ( QWidget* pParent, int nSelectedComponent, int nSelectedLayer, QString sSampleFilename )
55 : QDialog ( pParent )
56 , Object ()
57{
58 setupUi ( this );
59
60 // Show and enable maximize button. This is key when enlarging the
61 // application using a scaling factor and allows the OS to force its size
62 // beyond the minimum and make the scrollbars appear.
63 setWindowFlags( windowFlags() | Qt::CustomizeWindowHint |
64 Qt::WindowMinMaxButtonsHint );
65
66 m_pTimer = new QTimer(this);
67 connect(m_pTimer, SIGNAL(timeout()), this, SLOT(updateMainsamplePositionRuler()));
68 m_pTargetDisplayTimer = new QTimer(this);
69 connect(m_pTargetDisplayTimer, SIGNAL(timeout()), this, SLOT(updateTargetsamplePositionRuler()));
70
71 setClean();
72 m_nSelectedLayer = nSelectedLayer;
73 m_nSelectedComponent = nSelectedComponent;
74 m_sSampleName = sSampleFilename;
75 m_fZoomfactor = 1;
77 m_sLineColor = "default";
78 m_bOnewayStart = false;
79 m_bOnewayLoop = false;
80 m_bOnewayEnd = false;
81 m_nSlframes = 0;
82 m_pPositionsRulerPath = nullptr;
83 m_bPlayButton = false;
84 m_fRatio = 1.0f;
85 __rubberband.c_settings = 4;
86
87 QString newfilename = sSampleFilename.section( '/', -1 );
88
89 //init Displays
90 m_pMainSampleWaveDisplay = new MainSampleWaveDisplay( mainSampleview );
91 m_pSampleAdjustView = new DetailWaveDisplay( mainSampleAdjustView );
92 m_pTargetSampleView = new TargetWaveDisplay( targetSampleView );
93
94 setWindowTitle ( QString( tr( "SampleEditor " ) + newfilename) );
95 setModal ( true );
96
97 //this new sample give us the not changed real samplelength
98 m_pSampleFromFile = Sample::load( sSampleFilename );
99 if ( m_pSampleFromFile == nullptr ) {
100 reject();
101 }
102
103 unsigned slframes = m_pSampleFromFile->get_frames();
104
105 LoopCountSpinBox->setRange(0, 20000 );
106 StartFrameSpinBox->setRange(0, slframes );
107 LoopFrameSpinBox->setRange(0, slframes );
108 EndFrameSpinBox->setRange(0, slframes );
109 EndFrameSpinBox->setValue( slframes );
110 rubberbandCsettingscomboBox->setCurrentIndex( 4 );
111 rubberComboBox->setCurrentIndex( 0 );
112
113 // Make things consistent with the LCDDisplay and LCDSpinBox classes.
114 pitchdoubleSpinBox->setLocale( QLocale( QLocale::C, QLocale::AnyCountry ) );
115
116 __rubberband.use = false;
117 __rubberband.divider = 1.0;
118 openDisplays();
120
121#ifndef H2CORE_HAVE_RUBBERBAND
122 if ( !Filesystem::file_executable( Preferences::get_instance()->m_rubberBandCLIexecutable , true /* silent */) ) {
123 RubberbandCframe->setDisabled ( true );
124 setClean();
125 }
126#else
127 RubberbandCframe->setDisabled ( false );
128 setClean();
129#endif
130
131 __rubberband.pitch = 0.0;
132
133 m_bAdjusting = false;
135}
136
137
138
140{
143 m_pMainSampleWaveDisplay = nullptr;
144
145 m_pSampleAdjustView->close();
146 delete m_pSampleAdjustView;
147 m_pSampleAdjustView = nullptr;
148
149 m_pTargetSampleView->close();
150 delete m_pTargetSampleView;
151 m_pTargetSampleView = nullptr;
152
153 INFOLOG ( "DESTROY" );
154}
155
156
157void SampleEditor::closeEvent(QCloseEvent *event)
158{
159 if ( !m_bSampleEditorClean ) {
160 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
161 if ( QMessageBox::information(
162 this, "Hydrogen", pCommonStrings->getUnsavedChanges(),
163 QMessageBox::Ok | QMessageBox::Cancel,
164 QMessageBox::Cancel ) == QMessageBox::Ok ) {
165 setClean();
166 accept();
167 }
168 else {
169 event->ignore();
170 return;
171 }
172 } else {
173 accept();
174 }
175}
176
177std::shared_ptr<Sample> SampleEditor::retrieveSample() const {
178
179 auto pInstrument = Hydrogen::get_instance()->getSelectedInstrument();
180 if ( pInstrument == nullptr ) {
181 ERRORLOG( "No instrument selected" );
182 return nullptr;
183 }
184
185 auto pCompo = pInstrument->get_component( m_nSelectedComponent );
186 if ( pCompo == nullptr ) {
187 ERRORLOG( QString( "Invalid component [%1]" ).arg( m_nSelectedComponent ) );
188 assert( pCompo );
189 return nullptr;
190 }
191
192 auto pLayer = pCompo->get_layer( m_nSelectedLayer );
193 if ( pLayer == nullptr ) {
194 ERRORLOG( QString( "Invalid layer [%1]" ).arg( m_nSelectedLayer ) );
195 assert( pLayer );
196 return nullptr;
197 }
198
199 return pLayer->get_sample();
200}
201
203{
204 auto pInstrument = Hydrogen::get_instance()->getSelectedInstrument();
205 if ( pInstrument == nullptr ) {
206 ERRORLOG( "No instrument selected" );
207 return;
208 }
209
210 auto pCompo = pInstrument->get_component( m_nSelectedComponent );
211 if ( pCompo == nullptr ) {
212 ERRORLOG( QString( "Invalid component [%1]" ).arg( m_nSelectedComponent ) );
213 assert( pCompo );
214 return;
215 }
216
217 auto pLayer = pCompo->get_layer( m_nSelectedLayer );
218 if ( pLayer == nullptr ) {
219 ERRORLOG( QString( "Invalid layer [%1]" ).arg( m_nSelectedLayer ) );
220 assert( pLayer );
221 return;
222 }
223
224 auto pSample = pLayer->get_sample();
225 if ( pSample == nullptr ) {
226 ERRORLOG( "Unable to retrieve sample" );
227 assert( pSample );
228 return;
229 }
230
231 // this values are needed if we restore a sample from disk if a
232 // new song with sample changes will load
233 m_bSampleIsModified = pSample->get_is_modified();
234 m_nSamplerate = pSample->get_sample_rate();
235 __loops = pSample->get_loops();
236
237 // Per default all loop frames will be set to zero by Hydrogen. But this is
238 // dangerous since just altering start or loop might move them beyond the
239 // end.
240 if ( __loops.start_frame == 0 &&
241 __loops.loop_frame == 0 &&
242 __loops.end_frame == 0 ) {
243 __loops.end_frame = pSample->get_frames();
244 }
245 __rubberband = pSample->get_rubberband();
246
247 if ( pSample->get_velocity_envelope()->size()==0 ) {
248 m_pTargetSampleView->get_velocity()->clear();
249 m_pTargetSampleView->get_velocity()->push_back( EnvelopePoint( 0, 0 ) );
250 m_pTargetSampleView->get_velocity()->push_back( EnvelopePoint( m_pTargetSampleView->width(), 0 ) );
251 } else {
252 m_pTargetSampleView->get_velocity()->clear();
253
254 for(auto& pt : *pSample->get_velocity_envelope() ){
255 m_pTargetSampleView->get_velocity()->emplace_back( pt );
256 }
257 }
258
259 if ( pSample->get_pan_envelope()->size()==0 ) {
260 m_pTargetSampleView->get_pan()->clear();
261 m_pTargetSampleView->get_pan()->push_back( EnvelopePoint( 0, m_pTargetSampleView->height()/2 ) );
262 m_pTargetSampleView->get_pan()->push_back( EnvelopePoint( m_pTargetSampleView->width(), m_pTargetSampleView->height()/2 ) );
263 } else {
264 for(auto& pt : *pSample->get_pan_envelope() ){
265 m_pTargetSampleView->get_pan()->emplace_back( pt );
266 }
267 }
268
270 __loops.end_frame = pSample->get_loops().end_frame;
271 if ( __loops.mode == Sample::Loops::FORWARD ) {
272 ProcessingTypeComboBox->setCurrentIndex ( 0 );
273 }
274 if ( __loops.mode == Sample::Loops::REVERSE ) {
275 ProcessingTypeComboBox->setCurrentIndex ( 1 );
276 }
277 if ( __loops.mode == Sample::Loops::PINGPONG ) {
278 ProcessingTypeComboBox->setCurrentIndex ( 2 );
279 }
280
281 StartFrameSpinBox->setValue( __loops.start_frame );
282 LoopFrameSpinBox->setValue( __loops.loop_frame );
283 EndFrameSpinBox->setValue( __loops.end_frame );
284 LoopCountSpinBox->setValue( __loops.count );
285
286 m_pMainSampleWaveDisplay->m_nStartFramePosition = __loops.start_frame / m_divider + 25 ;
287 m_pMainSampleWaveDisplay->updateDisplayPointer();
288 m_pMainSampleWaveDisplay->m_nLoopFramePosition = __loops.loop_frame / m_divider + 25 ;
289 m_pMainSampleWaveDisplay->updateDisplayPointer();
290 m_pMainSampleWaveDisplay->m_nEndFramePosition = __loops.end_frame / m_divider + 25 ;
291 m_pMainSampleWaveDisplay->updateDisplayPointer();
292
293 if( !__rubberband.use ) {
294 rubberComboBox->setCurrentIndex( 0 );
295 }
296
297 rubberbandCsettingscomboBox->setCurrentIndex( __rubberband.c_settings );
298 if( !__rubberband.use ) {
299 rubberbandCsettingscomboBox->setCurrentIndex( 4 );
300 }
301
302 pitchdoubleSpinBox->setValue( __rubberband.pitch );
303 if( !__rubberband.use ) {
304 pitchdoubleSpinBox->setValue( 0.0 );
305 }
306
307 if( __rubberband.divider == 1.0/64.0) {
308 rubberComboBox->setCurrentIndex( 1 );
309 }
310 else if( __rubberband.divider == 1.0/32.0) {
311 rubberComboBox->setCurrentIndex( 2 );
312 } else if( __rubberband.divider == 1.0/16.0) {
313 rubberComboBox->setCurrentIndex( 3 );
314 } else if( __rubberband.divider == 1.0/8.0) {
315 rubberComboBox->setCurrentIndex( 4 );
316 } else if( __rubberband.divider == 1.0/4.0) {
317 rubberComboBox->setCurrentIndex( 5 );
318 } else if( __rubberband.divider == 1.0/2.0) {
319 rubberComboBox->setCurrentIndex( 6 );
320 } else if( __rubberband.use && ( __rubberband.divider >= 1.0 ) ) {
321 rubberComboBox->setCurrentIndex( (int)(__rubberband.divider + 6) );
322 }
323
326
327 }
328 m_pTargetSampleView->updateDisplay( pLayer );
329
330 connect( StartFrameSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( valueChangedStartFrameSpinBox(int) ) );
331 connect( LoopFrameSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( valueChangedLoopFrameSpinBox(int) ) );
332 connect( EndFrameSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( valueChangedEndFrameSpinBox(int) ) );
333 connect( LoopCountSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( valueChangedLoopCountSpinBox( int ) ) );
334 connect( ProcessingTypeComboBox, SIGNAL( currentIndexChanged ( const QString ) ), this, SLOT( valueChangedProcessingTypeComboBox( const QString ) ) );
335 connect( rubberComboBox, SIGNAL( currentIndexChanged ( const QString ) ), this, SLOT( valueChangedrubberComboBox( const QString ) ) );
336 connect( rubberbandCsettingscomboBox, SIGNAL( currentIndexChanged ( const QString ) ), this, SLOT( valueChangedrubberbandCsettingscomboBox( const QString ) ) );
337 connect( pitchdoubleSpinBox, SIGNAL ( valueChanged( double ) ), this, SLOT( valueChangedpitchdoubleSpinBox( double ) ) );
338}
339
341{
342 __loops.start_frame = StartFrameSpinBox->value();
343 __loops.loop_frame = LoopFrameSpinBox->value();
344 __loops.count = LoopCountSpinBox->value();
345 __loops.end_frame = EndFrameSpinBox->value();
346}
347
348
349
351{
352 // wavedisplays
353 m_divider = m_pSampleFromFile->get_frames() / 574.0F;
354 m_pMainSampleWaveDisplay->updateDisplay( m_sSampleName );
355 m_pMainSampleWaveDisplay->move( 1, 1 );
356
357 m_pSampleAdjustView->updateDisplay( m_sSampleName );
358 m_pSampleAdjustView->move( 1, 1 );
359
360 m_pTargetSampleView->move( 1, 1 );
361}
362
363
364
366{
367 if ( !m_bSampleEditorClean ){
368 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
369 if ( QMessageBox::information(
370 this, "Hydrogen", pCommonStrings->getUnsavedChanges(),
371 QMessageBox::Ok | QMessageBox::Cancel,
372 QMessageBox::Cancel ) == QMessageBox::Ok ) {
373 setClean();
374 accept();
375 }
376 else {
377 return;
378 }
379 }
380 else {
381 accept();
382 }
383}
384
385
386
388{
389 QApplication::setOverrideCursor(Qt::WaitCursor);
392 setClean();
394 QApplication::restoreOverrideCursor();
396}
397
398
399
401{
402 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
403
404 return QMessageBox::information(
405 this, "Hydrogen", pCommonStrings->getUnsavedChanges(),
406 QMessageBox::Ok | QMessageBox::Cancel,
407 QMessageBox::Cancel ) == QMessageBox::Ok;
408}
409
410
411
413{
414 if ( !m_bSampleEditorClean ){
415
416 auto pHydrogen = H2Core::Hydrogen::get_instance();
417 auto pAudioEngine = pHydrogen->getAudioEngine();
418 auto pOldSample = retrieveSample();
419 if ( pOldSample == nullptr ) {
420 ERRORLOG( "Unable to retrieve sample" );
421 assert( pOldSample );
422 return;
423 }
424
425 auto pEditSample = std::make_shared<Sample>( m_sSampleName,
426 pOldSample->getLicense() );
427 pEditSample->set_loops( __loops );
428 pEditSample->set_rubberband( __rubberband );
429 pEditSample->set_velocity_envelope( *m_pTargetSampleView->get_velocity() );
430 pEditSample->set_pan_envelope( *m_pTargetSampleView->get_pan() );
431
432 if( ! pEditSample->load( pAudioEngine->getTransportPosition()->getBpm() ) ){
433 ERRORLOG( "Unable to load modified sample" );
434 return;
435 }
436
437 pAudioEngine->lock( RIGHT_HERE );
438
439 std::shared_ptr<H2Core::Instrument> pInstrument = nullptr;
440 auto pSong = pHydrogen->getSong();
441 if ( pSong != nullptr ) {
442 auto pInstrList = pSong->getInstrumentList();
443 int nInstr = pHydrogen->getSelectedInstrumentNumber();
444 if ( nInstr >= static_cast<int>(pInstrList->size()) ) {
445 nInstr = -1;
446 }
447
448 if (nInstr == -1) {
449 pInstrument = nullptr;
450 }
451 else {
452 pInstrument = pInstrList->get( nInstr );
453 }
454 }
455
456 std::shared_ptr<H2Core::InstrumentLayer> pLayer = nullptr;
457 if( pInstrument != nullptr ) {
458 pLayer = pInstrument->get_component( m_nSelectedComponent )->get_layer( m_nSelectedLayer );
459
460 // insert new sample from newInstrument
461 pLayer->set_sample( pEditSample );
462 }
463
464 pAudioEngine->unlock();
465
466 if ( pLayer != nullptr ) {
467 m_pTargetSampleView->updateDisplay( pLayer );
468 }
469 }
470}
471
472
473
475{
476
477}
478
479
480
482{
483 m_bAdjusting = true;
484
485 testpTimer();
486// QMessageBox::information ( this, "Hydrogen", tr ( "jep %1" ).arg(m_pSample->get_frames()));
487 m_bSampleIsModified = true;
488 if( m_pMainSampleWaveDisplay->m_bStartSliderIsMoved ) __loops.start_frame = m_pMainSampleWaveDisplay->m_nStartFramePosition * m_divider - 25 * m_divider;
489 if( m_pMainSampleWaveDisplay->m_bLoopSliderIsMoved ) __loops.loop_frame = m_pMainSampleWaveDisplay->m_nLoopFramePosition * m_divider - 25 * m_divider;
490 if( m_pMainSampleWaveDisplay->m_bEndSliderIsmoved ) __loops.end_frame = m_pMainSampleWaveDisplay->m_nEndFramePosition * m_divider - 25 * m_divider ;
491 StartFrameSpinBox->setValue( __loops.start_frame );
492 LoopFrameSpinBox->setValue( __loops.loop_frame );
493 EndFrameSpinBox->setValue( __loops.end_frame );
494 m_bOnewayStart = true;
495 m_bOnewayLoop = true;
496 m_bOnewayEnd = true;
498 m_bAdjusting = false;
499 setUnclean();
500 return true;
501}
502
508
510{
511 m_bSampleEditorClean = false;
512 PrevChangesPushButton->setDisabled ( false );
513 PrevChangesPushButton->setFlat ( false );
514 // PrevChangesPushButton->show();
515}
516
518{
520 PrevChangesPushButton->setDisabled ( true );
521 PrevChangesPushButton->setFlat ( true );
522}
523
525{
526 testpTimer();
527 m_pDetailFrame = StartFrameSpinBox->value();
528 if (m_pDetailFrame == __loops.start_frame) { // no actual change
530 return;
531 }
532 m_sLineColor = "Start";
533 if ( !m_bOnewayStart ){
534 m_pMainSampleWaveDisplay->m_nStartFramePosition = StartFrameSpinBox->value() / m_divider + 25 ;
535 m_pMainSampleWaveDisplay->updateDisplayPointer();
536 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor , m_sLineColor);
537 __loops.start_frame = m_pDetailFrame;
538
539 }else
540 {
541 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor , m_sLineColor);
542 m_bOnewayStart = false;
543 }
545 setUnclean();
547}
548
550{
551 testpTimer();
552 m_pDetailFrame = LoopFrameSpinBox->value();
553 if (m_pDetailFrame == __loops.loop_frame) {
555 return;
556 }
557 m_sLineColor = "Loop";
558 if ( !m_bOnewayLoop ){
559 m_pMainSampleWaveDisplay->m_nLoopFramePosition = LoopFrameSpinBox->value() / m_divider + 25 ;
560 m_pMainSampleWaveDisplay->updateDisplayPointer();
561 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor , m_sLineColor);
562 __loops.loop_frame = m_pDetailFrame;
563 }else
564 {
565 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor , m_sLineColor);
566 m_bOnewayLoop = false;
567 }
569 setUnclean();
571}
572
574{
575 testpTimer();
576 m_pDetailFrame = EndFrameSpinBox->value();
577 if ( m_pDetailFrame == __loops.end_frame) {
579 return;
580 }
581 m_sLineColor = "End";
582 if ( !m_bOnewayEnd ){
583 m_pMainSampleWaveDisplay->m_nEndFramePosition = EndFrameSpinBox->value() / m_divider + 25 ;
584 m_pMainSampleWaveDisplay->updateDisplayPointer();
585 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor , m_sLineColor);
586 __loops.end_frame = m_pDetailFrame;
587 }else
588 {
589 m_bOnewayEnd = false;
590 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor , m_sLineColor);
591 }
593 setUnclean();
595}
596
598{
599 Hydrogen* pHydrogen = Hydrogen::get_instance();
600 auto pAudioEngine = pHydrogen->getAudioEngine();
601
602 if (PlayPushButton->text() == "Stop" ){
603 testpTimer();
604 return;
605 }
606
607 const int selectedLayer = InstrumentEditorPanel::get_instance()->getSelectedLayer();
608
609 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
610 if ( pSong == nullptr ) {
611 return;
612 }
613 auto pInstr = pSong->getInstrumentList()->get( Hydrogen::get_instance()->getSelectedInstrumentNumber() );
614 if ( pInstr == nullptr ) {
615 return;
616 }
617 auto pCompo = pInstr->get_component( m_nSelectedComponent );
618 if ( pCompo == nullptr ) {
619 return;
620 }
621 auto pLayer = pCompo->get_layer( selectedLayer );
622 if ( pLayer == nullptr ) {
623 return;
624 }
625 Note *pNote = new Note( pInstr, 0, pLayer->get_end_velocity() - 0.01 );
627 pHydrogen->getAudioEngine()->getSampler()->noteOn(pNote);
628
631 m_bPlayButton = true;
632 m_pMainSampleWaveDisplay->paintLocatorEvent( StartFrameSpinBox->value() / m_divider + 24 , true);
633 m_pSampleAdjustView->setDetailSamplePosition( __loops.start_frame, m_fZoomfactor , nullptr);
634
635 if( __rubberband.use == false ){
636 m_pTimer->start(40); // update ruler at 25 fps
637 }
638
639
640 m_nRealtimeFrameEnd = pAudioEngine->getRealtimeFrame() + m_nSlframes;
641
642 //calculate the new rubberband sample length
643 if( __rubberband.use ){
644 m_nRealtimeFrameEndForTarget = pAudioEngine->getRealtimeFrame() + (m_nSlframes * m_fRatio + 0.1);
645 }else
646 {
648 }
649 m_pTargetDisplayTimer->start(40); // update ruler at 25 fps
650 PlayPushButton->setText( QString( "Stop") );
651
652}
653
655{
656 if (PlayOrigPushButton->text() == "Stop" ){
657 testpTimer();
658 return;
659 }
660 auto pHydrogen = Hydrogen::get_instance();
661 auto tearDown = [&]() {
662 m_pMainSampleWaveDisplay->paintLocatorEvent(
663 StartFrameSpinBox->value() / m_divider + 24 , true);
664 m_pSampleAdjustView->setDetailSamplePosition(
665 __loops.start_frame, m_fZoomfactor , nullptr);
666 m_pTimer->start(40); // update ruler at 25 fps
668 pHydrogen->getAudioEngine()->getRealtimeFrame() + m_nSlframes;
669 PlayOrigPushButton->setText( QString( "Stop") );
670 };
671
672 auto pSong = pHydrogen->getSong();
673 if ( pSong == nullptr ) {
674 tearDown();
675 return;
676 }
677
678 const int nSelectedlayer =
680 auto pInstr = pSong->getInstrumentList()->get(
681 pHydrogen->getSelectedInstrumentNumber() );
682 if ( pInstr == nullptr ) {
683 DEBUGLOG( "No instrument selected" );
684 tearDown();
685 return;
686 }
687
688 /*
689 *preview_instrument deletes the last used preview instrument, therefore we
690 *have to construct a temporary instrument. Otherwise pInstr would be
691 *deleted if consumed by preview_instrument.
692 */
693 auto pTmpInstrument = Instrument::load_instrument(
694 pInstr->get_drumkit_path(), pInstr->get_name() );
695 if ( pTmpInstrument == nullptr ) {
696 ERRORLOG( QString( "Unable to load instrument [%1] from [%2]" )
697 .arg( pInstr->get_name() ).arg( pInstr->get_drumkit_path() ) );
698 tearDown();
699 return;
700 }
701 const QString sSamplePath = pInstr->get_component( m_nSelectedComponent )
702 ->get_layer( nSelectedlayer )->get_sample()->get_filepath();
703 auto pNewSample = Sample::load( sSamplePath );
704 if ( pNewSample == nullptr ) {
705 ERRORLOG( QString( "Unable to load sample from [%1]" )
706 .arg( sSamplePath ) );
707 tearDown();
708 }
709
710 const int nLength = ( pNewSample->get_frames() /
711 pNewSample->get_sample_rate() + 1 ) * 100;
712 pHydrogen->getAudioEngine()->getSampler()->preview_instrument( pTmpInstrument );
713 pHydrogen->getAudioEngine()->getSampler()->preview_sample( pNewSample, nLength );
714 m_nSlframes = pNewSample->get_frames();
715
716 tearDown();
717}
718
720{
721 unsigned long realpos = Hydrogen::get_instance()->getAudioEngine()->getRealtimeFrame();
722 if ( realpos < m_nRealtimeFrameEnd ){
723 unsigned frame = m_nSlframes - ( m_nRealtimeFrameEnd - realpos );
724 if ( m_bPlayButton == true ){
725 m_pMainSampleWaveDisplay->paintLocatorEvent( m_pPositionsRulerPath[frame] / m_divider + 25 , true);
726 m_pSampleAdjustView->setDetailSamplePosition( m_pPositionsRulerPath[frame], m_fZoomfactor , nullptr);
727 }else{
728 m_pMainSampleWaveDisplay->paintLocatorEvent( frame / m_divider + 25 , true);
729 m_pSampleAdjustView->setDetailSamplePosition( frame, m_fZoomfactor , nullptr);
730 }
731// ERRORLOG( QString("sampleval: %1").arg(frame) );
732 } else {
733 auto pCommonString = HydrogenApp::get_instance()->getCommonStrings();
734 m_pMainSampleWaveDisplay->paintLocatorEvent( -1 , false);
735 m_pTimer->stop();
736 PlayPushButton->setText( pCommonString->getButtonPlay() );
737 PlayOrigPushButton->setText( pCommonString->getButtonPlayOriginalSample() );
738 m_bPlayButton = false;
739 }
740}
741
743{
744 unsigned long realpos = Hydrogen::get_instance()->getAudioEngine()->getRealtimeFrame();
745 unsigned targetSampleLength;
746 if ( __rubberband.use ){
747 targetSampleLength = m_nSlframes * m_fRatio + 0.1;
748 } else {
749 targetSampleLength = m_nSlframes;
750 }
751
752 if ( realpos < m_nRealtimeFrameEndForTarget ){
753 unsigned pos = targetSampleLength - ( m_nRealtimeFrameEndForTarget - realpos );
754 m_pTargetSampleView->paintLocatorEventTargetDisplay( (m_pTargetSampleView->width() * pos /targetSampleLength), true);
755// ERRORLOG( QString("sampleval: %1").arg(frame) );
756 } else {
757 auto pCommonString = HydrogenApp::get_instance()->getCommonStrings();
758 m_pTargetSampleView->paintLocatorEventTargetDisplay( -1 , false);
759 m_pTargetDisplayTimer->stop();
760 PlayPushButton->setText( pCommonString->getButtonPlay() );
761 PlayOrigPushButton->setText( pCommonString->getButtonPlayOriginalSample() );
762 m_bPlayButton = false;
763 }
764}
765
766
767
769{
771
772 unsigned oneSampleLength = __loops.end_frame - __loops.start_frame;
773 unsigned loopLength = __loops.end_frame - __loops.loop_frame;
774 unsigned repeatsLength = loopLength * __loops.count;
775 unsigned newLength = 0;
776 if (oneSampleLength == loopLength){
777 newLength = oneSampleLength + oneSampleLength * __loops.count ;
778 }else
779 {
780 newLength =oneSampleLength + repeatsLength;
781 }
782
783 unsigned normalLength = m_pSampleFromFile->get_frames();
784
785 unsigned * normalFrames = new unsigned[ normalLength ];
786 unsigned * tempFrames = new unsigned[ newLength ];
787 unsigned * loopFrames = new unsigned[ loopLength ];
788
789 for ( unsigned i = 0; i < normalLength; i++ ) {
790 normalFrames[i] = i;
791 }
792
793 Sample::Loops::LoopMode loopmode = __loops.mode;
794 long int z = __loops.loop_frame;
795 long int y = __loops.start_frame;
796
797 for ( unsigned i = 0; i < newLength; i++){ //first vector
798 tempFrames[i] = 0;
799 }
800
801 for ( unsigned i = 0; i < oneSampleLength; i++, y++){ //first vector
802
803 tempFrames[i] = normalFrames[y];
804 }
805
806 for ( unsigned i = 0; i < loopLength; i++, z++){ //loop vector
807
808 loopFrames[i] = normalFrames[z];
809 }
810
811 if ( loopmode == Sample::Loops::REVERSE ){
812 std::reverse(loopFrames, loopFrames + loopLength);
813 }
814
815 if ( loopmode == Sample::Loops::REVERSE && __loops.count > 0 && __loops.start_frame == __loops.loop_frame ){
816 std::reverse( tempFrames, tempFrames + oneSampleLength );
817 }
818
819 if ( loopmode == Sample::Loops::PINGPONG && __loops.start_frame == __loops.loop_frame){
820 std::reverse(loopFrames, loopFrames + loopLength);
821 }
822
823 for ( int i = 0; i< __loops.count ;i++){
824 unsigned tempdataend = oneSampleLength + ( loopLength * i );
825 if ( __loops.start_frame == __loops.loop_frame ){
826 std::copy( loopFrames, loopFrames+loopLength ,tempFrames+ tempdataend );
827 }
828 if ( loopmode == Sample::Loops::PINGPONG && __loops.count > 1){
829 std::reverse(loopFrames, loopFrames + loopLength);
830 }
831 if ( __loops.start_frame != __loops.loop_frame ){
832 std::copy( loopFrames, loopFrames+loopLength ,tempFrames+ tempdataend );
833 }
834 }
835
836
837 if ( __loops.count == 0 && loopmode == Sample::Loops::REVERSE ){
838 std::reverse( tempFrames + __loops.loop_frame, tempFrames + newLength);
839 }
840
842 {
843 delete[] m_pPositionsRulerPath;
844 }
845
846 m_pPositionsRulerPath = tempFrames;
847
848 delete[] loopFrames;
849 delete[] normalFrames;
850}
851
852
853
855{
857 unsigned oneSampleLength = __loops.end_frame - __loops.start_frame;
858 unsigned loopLength = __loops.end_frame - __loops.loop_frame ;
859 unsigned repeatsLength = loopLength * __loops.count;
860 unsigned newLength = 0;
861
862 if ( oneSampleLength == loopLength ){
863 newLength = oneSampleLength + oneSampleLength * __loops.count ;
864 } else {
865 newLength =oneSampleLength + repeatsLength;
866 }
867
868 m_nSlframes = newLength;
869 newlengthLabel->setText(QString( tr( "new sample length" ) )
870 .append( QString( ": %1 " ).arg(newLength) )
871 .append( tr( "frames" )));
873}
874
875
876
878{
879 testpTimer();
880 int count = LoopCountSpinBox->value();
881
882 if (count == __loops.count) {
884 return;
885 }
886
887 const auto pHydrogen = Hydrogen::get_instance();
888 const auto pAudioDriver = pHydrogen->getAudioOutput();
889 if ( pAudioDriver == nullptr ) {
890 ERRORLOG( "AudioDriver is not ready!" );
891 return;
892 }
893
894 if ( m_nSlframes > pAudioDriver->getSampleRate() * 60 ){
895 pHydrogen->getAudioEngine()->getSampler()->stopPlayingNotes();
896 m_pMainSampleWaveDisplay->paintLocatorEvent( -1 , false);
897 m_pTimer->stop();
898 m_bPlayButton = false;
899 }
900 __loops.count = count;
901 setUnclean();
903 if ( m_nSlframes > pAudioDriver->getSampleRate() * 60 * 30){ // >30 min
904 LoopCountSpinBox->setMaximum(LoopCountSpinBox->value() -1);
905 }
906
907}
908
909
910
912{
913 int new_settings = rubberbandCsettingscomboBox->currentIndex();
914 if (new_settings == __rubberband.c_settings) {
916 return;
917 }
918 __rubberband.c_settings = new_settings;
919 setUnclean();
920}
921
922
923
925{
926 double new_value = pitchdoubleSpinBox->value();
927 if (std::abs(new_value - __rubberband.pitch) < 0.0001) {
929 return;
930 }
931 __rubberband.pitch = new_value;
932 setUnclean();
933}
934
935
937{
938
939 if( rubberComboBox->currentText() != "off" ){
940 __rubberband.use = true;
941 }else
942 {
943 __rubberband.use = false;
944 __rubberband.divider = 1.0;
945 }
946
947
948 switch ( rubberComboBox->currentIndex() ){
949 case 0 ://
950 __rubberband.divider = 4.0;
951 break;
952 case 1 ://
953 __rubberband.divider = 1.0/64.0;
954 break;
955 case 2 ://
956 __rubberband.divider = 1.0/32.0;
957 break;
958 case 3 ://
959 __rubberband.divider = 1.0/16.0;
960 break;
961 case 4 ://
962 __rubberband.divider = 1.0/8.0;
963 break;
964 case 5 ://
965 __rubberband.divider = 1.0/4.0;
966 break;
967 case 6 ://
968 __rubberband.divider = 1.0/2.0;
969 break;
970 case 7 ://
971 __rubberband.divider = 1.0;
972 break;
973 default:
974 __rubberband.divider = (float)rubberComboBox->currentIndex() - 6.0;
975 }
976// QMessageBox::information ( this, "Hydrogen", tr ( "divider %1" ).arg( __rubberband.divider ));
977// float __rubberband.divider;
979
980
981 setUnclean();
982}
983
985{
986 //calculate ratio
987 double durationtime = 60.0 / Hydrogen::get_instance()->getAudioEngine()->getTransportPosition()->getBpm()
988 * __rubberband.divider;
989 double induration = (double) m_nSlframes / (double) m_nSamplerate;
990 if (induration != 0.0) m_fRatio = durationtime / induration;
991
992 //my personal ratio quality settings
993 //ratios < 0.1 || > 3.0 are bad (red) or experimental sounds
994 //ratios > 0.1 && < 0.5 || > 2.0 && < 3.0 are mediocre (yellow)
995 //ratios > 0.5 && < 2.0 are good (green)
996 //
997 // 0.1 0.5 2.0 3.0
998 //<---red---[--yellow--[------green------]----yellow----]---red--->
999
1000 //green ratio
1001 if( ( m_fRatio >= 0.5 ) && ( m_fRatio <= 2.0 ) ){
1002 rubberComboBox->setStyleSheet("QComboBox { background-color: green; }");
1003 }
1004 //yellow ratio
1005 else if( ( m_fRatio >= 0.1 ) && ( m_fRatio <= 3.0 ) ){
1006 rubberComboBox->setStyleSheet("QComboBox { background-color: yellow; }");
1007 }
1008 //red ratio
1009 else{
1010 rubberComboBox->setStyleSheet("QComboBox { background-color: red; }");
1011 }
1012 QString text = QString( tr(" RB-Ratio" ) )
1013 .append( QString( " %1" ).arg( m_fRatio ) );
1014 ratiolabel->setText( text );
1015
1016 //no rubberband = default
1017 if( !__rubberband.use ){
1018 rubberComboBox->setStyleSheet("QComboBox { background-color: 58, 62, 72; }");
1019 ratiolabel->setText( "" );
1020 }
1021}
1022
1023
1025{
1026 switch ( ProcessingTypeComboBox->currentIndex() ){
1027 case 0 ://
1029 break;
1030 case 1 ://
1032 break;
1033 case 2 ://
1035 break;
1036 default:
1038 }
1039 setUnclean();
1040}
1041
1042
1043
1045{
1046 m_fZoomfactor = value / 10 +1;
1047 m_pSampleAdjustView->setDetailSamplePosition( m_pDetailFrame, m_fZoomfactor, m_sLineColor );
1048}
1049
1050
1051
1053{
1054 m_bAdjusting = true;
1055 if ( __loops.start_frame > __loops.loop_frame ) __loops.loop_frame = __loops.start_frame;
1056 if ( __loops.start_frame > __loops.end_frame ) __loops.end_frame = __loops.start_frame;
1057 if ( __loops.loop_frame > __loops.end_frame ) __loops.end_frame = __loops.loop_frame;
1058 if ( __loops.end_frame < __loops.loop_frame ) __loops.loop_frame = __loops.end_frame;
1059 if ( __loops.end_frame < __loops.start_frame ) __loops.start_frame = __loops.end_frame;
1060 StartFrameSpinBox->setValue( __loops.start_frame );
1061 LoopFrameSpinBox->setValue( __loops.loop_frame );
1062 EndFrameSpinBox->setValue( __loops.end_frame );
1063 m_bAdjusting = false;
1064}
1065
1066
1067
1069{
1070 if ( m_pTimer->isActive() || m_pTargetDisplayTimer->isActive() ){
1071 auto pCommonString = HydrogenApp::get_instance()->getCommonStrings();
1072 m_pMainSampleWaveDisplay->paintLocatorEvent( -1 , false);
1073 m_pTimer->stop();
1074 m_pTargetDisplayTimer->stop();
1075 PlayPushButton->setText( pCommonString->getButtonPlay() );
1076 PlayOrigPushButton->setText( pCommonString->getButtonPlayOriginalSample() );
1078 m_bPlayButton = false;
1079 }
1080}
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:61
#define INFOLOG(x)
Definition Object.h:240
#define ERRORLOG(x)
Definition Object.h:242
#define DEBUGLOG(x)
Definition Object.h:239
Sampler * getSampler() const
const std::shared_ptr< TransportPosition > getTransportPosition() const
long long getRealtimeFrame() const
A container for a sample, being able to apply modifications on it.
Definition Sample.h:43
static bool file_executable(const QString &path, bool silent=false)
returns true if the given path is an existing executable regular file
Hydrogen Audio Engine.
Definition Hydrogen.h:54
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:123
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:663
std::shared_ptr< Instrument > getSelectedInstrument() const
void setIsModified(bool bIsModified)
Wrapper around Song::setIsModified() that checks whether a song is set.
static std::shared_ptr< Instrument > load_instrument(const QString &drumkit_path, const QString &instrument_name)
creates a new Instrument, loads samples from a given instrument within a given drumkit
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:101
void set_specific_compo_id(int value)
__specific_compo_id setter
Definition Note.h:519
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
LoopMode
possible sample editing loop mode
Definition Sample.h:81
static std::shared_ptr< Sample > load(const QString &filepath, const License &license=License())
Definition Sample.cpp:136
void noteOn(Note *pNote)
Start playing a note.
Definition Sampler.cpp:185
void stopPlayingNotes(std::shared_ptr< Instrument > pInstr=nullptr)
Definition Sampler.cpp:1335
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
std::shared_ptr< CommonStrings > getCommonStrings()
static InstrumentEditorPanel * get_instance()
void updateTargetsamplePositionRuler()
void updateMainsamplePositionRuler()
MainSampleWaveDisplay * m_pMainSampleWaveDisplay
bool returnAllMainWaveDisplayValues()
float m_fZoomfactor
unsigned * m_pPositionsRulerPath
std::shared_ptr< H2Core::Sample > m_pSampleFromFile
void valueChangedLoopFrameSpinBox(int)
QString m_sLineColor
void getAllLocalFrameInfos()
std::shared_ptr< H2Core::Sample > retrieveSample() const
void returnAllTargetDisplayValues()
void createPositionsRulerPath()
H2Core::Sample::Loops __loops
unsigned long m_nRealtimeFrameEndForTarget
void valueChangedProcessingTypeComboBox(const QString)
QTimer * m_pTargetDisplayTimer
QTimer * m_pTimer
bool getCloseQuestion()
void getAllFrameInfos()
bool m_bSampleEditorClean
void createNewLayer()
void setSamplelengthFrames()
bool m_bSampleIsModified
true if sample is modified
unsigned m_nSlframes
QString m_sSampleName
int m_nSelectedComponent
void valueChangedrubberComboBox(const QString)
virtual void closeEvent(QCloseEvent *event) override
void on_PrevChangesPushButton_clicked()
void on_PlayPushButton_clicked()
H2Core::Sample::Rubberband __rubberband
void valueChangedrubberbandCsettingscomboBox(const QString)
virtual void mouseReleaseEvent(QMouseEvent *ev) override
TargetWaveDisplay * m_pTargetSampleView
unsigned m_pDetailFrame
void checkRatioSettings()
SampleEditor(QWidget *pParent, int nSelectedComponent, int nSelectedLayer, QString nSampleFilename)
void valueChangedEndFrameSpinBox(int)
unsigned m_nSamplerate
void testPositionsSpinBoxes()
void on_verticalzoomSlider_valueChanged(int value)
void valueChangedStartFrameSpinBox(int)
void on_ClosePushButton_clicked()
void valueChangedpitchdoubleSpinBox(double)
void on_PlayOrigPushButton_clicked()
void valueChangedLoopCountSpinBox(int)
DetailWaveDisplay * m_pSampleAdjustView
unsigned long m_nRealtimeFrameEnd