hydrogen 1.2.6
Sample.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
24
25#include <limits>
26#include <memory>
27
28#include <core/Hydrogen.h>
31#include <core/Basics/Sample.h>
32#include <core/Basics/Note.h>
33
34#if defined(H2CORE_HAVE_RUBBERBAND) || _DOXYGEN_
35#include <rubberband/RubberBandStretcher.h>
36#define RUBBERBAND_BUFFER_OVERSIZE 500
37#define RUBBERBAND_DEBUG 0
38#endif
39
40namespace H2Core
41{
42
43const std::vector<QString> Sample::__loop_modes = { "forward", "reverse", "pingpong" };
44
45#if defined(H2CORE_HAVE_RUBBERBAND) || _DOXYGEN_
46static double compute_pitch_scale( const Sample::Rubberband& r );
47static RubberBand::RubberBandStretcher::Options compute_rubberband_options( const Sample::Rubberband& r );
48#endif
49
50
51/* EnvelopePoint */
55
56EnvelopePoint::EnvelopePoint( int f, int v ) : frame( f ), value( v )
57{
58}
59
60EnvelopePoint::EnvelopePoint( const EnvelopePoint& other ) : Object(other), frame ( other.frame ), value ( other.value )
61{
62}
63/* EnvelopePoint */
64
65
66Sample::Sample( const QString& filepath, const License& license, int frames, int sample_rate, float* data_l, float* data_r )
67 : __filepath( filepath ),
68 __frames( frames ),
69 __sample_rate( sample_rate ),
70 __data_l( data_l ),
71 __data_r( data_r ),
72 __is_modified( false ),
73 m_license( license )
74{
75 if ( filepath.lastIndexOf( "/" ) <= 0 ) {
76 WARNINGLOG( QString( "Provided filepath [%1] does not seem like an absolute path. Sample will most probably be unable to load." ) );
77 }
78}
79
80Sample::Sample( std::shared_ptr<Sample> pOther ): Object( *pOther ),
81 __filepath( pOther->get_filepath() ),
82 __frames( pOther->get_frames() ),
83 __sample_rate( pOther->get_sample_rate() ),
84 __data_l( nullptr ),
85 __data_r( nullptr ),
86 __is_modified( pOther->get_is_modified() ),
87 __loops( pOther->__loops ),
88 __rubberband( pOther->__rubberband ),
89 m_license( pOther->m_license )
90{
91
92 __data_l = new float[__frames];
93 __data_r = new float[__frames];
94
95 // Since the third argument of memcpy takes the number of bytes,
96 // which are about to be copied, and the data is given in float,
97 // which are four bytes each, the number of copied frames
98 // `__frames` has to be multiplied by four.
99 memcpy( __data_l, pOther->get_data_l(), __frames * 4 );
100 memcpy( __data_r, pOther->get_data_r(), __frames * 4 );
101
102 PanEnvelope* pPan = pOther->get_pan_envelope();
103 for( int i=0; i<pPan->size(); i++ ) {
104 __pan_envelope.push_back( pPan->at(i) );
105 }
106
107 PanEnvelope* pVelocity = pOther->get_velocity_envelope();
108 for( int i=0; i<pVelocity->size(); i++ ) {
109 __velocity_envelope.push_back( pVelocity->at(i) );
110 }
111}
112
114{
115 if ( __data_l != nullptr ) {
116 delete[] __data_l;
117 }
118 if ( __data_r != nullptr ) {
119 delete[] __data_r;
120 }
121}
122
123void Sample::set_filename( const QString& filename )
124{
125 QFileInfo Filename = QFileInfo( filename );
126 QFileInfo Dest = QFileInfo( get_filepath() );
127 __filepath = QDir(Dest.absolutePath()).filePath( Filename.fileName() );
128}
129
130
135
136std::shared_ptr<Sample> Sample::load( const QString& sFilepath, const License& license )
137{
138 std::shared_ptr<Sample> pSample;
139
140 if( !Filesystem::file_readable( sFilepath ) ) {
141 ERRORLOG( QString( "Unable to read %1" ).arg( sFilepath ) );
142 return nullptr;
143 }
144
145 pSample = std::make_shared<Sample>( sFilepath, license );
146
147 // Samples loaded this way have no loops, rubberband, or envelopes
148 // set. Therefore, we do not have to pass a tempo in here.
149 if( !pSample->load() ) {
150 return nullptr;
151 }
152
153 return pSample;
154}
155
156bool Sample::load( float fBpm )
157{
158 // Will contain a bunch of metadata about the loaded sample.
159 SF_INFO sound_info = {0};
160
161 // Opens file in read-only mode.
162#ifdef WIN32
163 // On Windows we use a special version of sf_open to ensure we get all
164 // characters of the filename entered in the GUI right. No matter which
165 // encoding was used locally.
166 // We have to terminate the string using a null character ourselves.
167 QString sPaddedPath = get_filepath().append( '\0' );
168 wchar_t* encodedFilename = new wchar_t[ sPaddedPath.size() ];
169
170 sPaddedPath.toWCharArray( encodedFilename );
171
172 SNDFILE* file = sf_wchar_open( encodedFilename, SFM_READ,
173 &sound_info );
174 delete encodedFilename;
175#else
176 SNDFILE* file = sf_open( get_filepath().toLocal8Bit(), SFM_READ,
177 &sound_info );
178#endif
179 if ( file == nullptr ) {
180 ERRORLOG( QString( "Error loading file [%1] with format [%2]: %3" )
181 .arg( get_filepath() )
182 .arg( sndfileFormatToQString( sound_info.format ) )
183 .arg( sf_strerror( file ) ) );
184 return false;
185 }
186
187 // Sanity check. SAMPLE_CHANNELS is defined in
188 // core/include/hydrogen/globals.h and set to 2.
189 if ( sound_info.channels > SAMPLE_CHANNELS ) {
190 WARNINGLOG( QString( "can't handle %1 channels, only 2 will be used" ).arg( sound_info.channels ) );
191 sound_info.channels = SAMPLE_CHANNELS;
192 }
193 if ( sound_info.frames > ( std::numeric_limits<int>::max()/sound_info.channels ) ) {
194 WARNINGLOG( QString( "sample frames count (%1) and channels (%2) are too much, truncate it." ).arg( sound_info.frames ).arg( sound_info.channels ) );
195 sound_info.frames = ( std::numeric_limits<int>::max()/sound_info.channels );
196 }
197
198 // Create an array, which will hold the block of samples read
199 // from file.
200 float* buffer = new float[ sound_info.frames * sound_info.channels ];
201
202 //memset( buffer, 0, sound_info.frames *sound_info.channels );
203
204 // Read all frames into `buffer'. Libsndfile does seamlessly
205 // convert the format of the underlying data on the fly. The
206 // output will be an array of floats regardless of file's
207 // encoding (e.g. 16 bit PCM).
208 sf_count_t count = sf_read_float( file, buffer, sound_info.frames * sound_info.channels );
209 if( count==0 ){
210 WARNINGLOG( QString( "%1 is an empty sample" ).arg( get_filepath() ) );
211 }
212
213 // Deallocate the handler.
214 if ( sf_close( file ) != 0 ){
215 WARNINGLOG( QString( "Unable to close sample file %1" ).arg( get_filepath() ) );
216 }
217
218 // Flush the current content of the left and right channel and
219 // the current metadata.
220 unload();
221
222 // Save the metadata of the loaded file into private members
223 // of the Sample class.
224 __frames = sound_info.frames;
225 __sample_rate = sound_info.samplerate;
226
227 // Split the loaded frames into left and right channel.
228 // If only one channels was present in the underlying data,
229 // duplicate its content.
230 __data_l = new float[ sound_info.frames ];
231 __data_r = new float[ sound_info.frames ];
232 if ( sound_info.channels == 1 ) {
233 memcpy( __data_l, buffer, __frames * sizeof( float ) );
234 memcpy( __data_r, buffer, __frames * sizeof( float ) );
235 } else if ( sound_info.channels == SAMPLE_CHANNELS ) {
236 for ( int i = 0; i < __frames; i++ ) {
237 __data_l[i] = buffer[i * SAMPLE_CHANNELS ];
238 __data_r[i] = buffer[i * SAMPLE_CHANNELS + 1 ];
239 }
240 }
241 delete[] buffer;
242
243 // Apply modifiers (if present/altered).
244 if ( ! apply_loops() ) {
245 WARNINGLOG( "Unable to apply loops" );
246 }
248 apply_pan();
249#ifdef H2CORE_HAVE_RUBBERBAND
250 apply_rubberband( fBpm );
251#else
252 if ( ! exec_rubberband_cli( fBpm ) ) {
253 WARNINGLOG( "Unable to apply rubberband" );
254 }
255#endif
256
257 return true;
258}
259
261{
262 if( __loops.start_frame == 0 && __loops.loop_frame == 0 &&
263 __loops.end_frame == 0 && __loops.count == 0 ) {
264 // Default parameters. No looping was set by the
265 // user. Skipping.
266 return true;
267 }
268
269 if( __loops.start_frame<0 ) {
270 ERRORLOG( QString( "start_frame %1 < 0 is not allowed" ).arg( __loops.start_frame ) );
271 return false;
272 }
273 if( __loops.loop_frame<__loops.start_frame ) {
274 ERRORLOG( QString( "loop_frame %1 < start_frame %2 is not allowed" ).arg( __loops.loop_frame ).arg( __loops.start_frame ) );
275 return false;
276 }
277 if( __loops.end_frame<__loops.loop_frame ) {
278 ERRORLOG( QString( "end_frame %1 < loop_frame %2 is not allowed" ).arg( __loops.end_frame ).arg( __loops.loop_frame ) );
279 return false;
280 }
281 if( __loops.end_frame>__frames ) {
282 ERRORLOG( QString( "end_frame %1 > __frames %2 is not allowed" ).arg( __loops.end_frame ).arg( __frames ) );
283 return false;
284 }
285 if( __loops.count<0 ) {
286 ERRORLOG( QString( "count %1 < 0 is not allowed" ).arg( __loops.count ) );
287 return false;
288 }
289 //if( lo == __loops ) return true;
290
291 bool full_loop = __loops.start_frame==__loops.loop_frame;
292 int full_length = __loops.end_frame - __loops.start_frame;
293 int loop_length = __loops.end_frame - __loops.loop_frame;
294 int new_length = full_length + loop_length * __loops.count;
295
296 float* new_data_l = new float[ new_length ];
297 float* new_data_r = new float[ new_length ];
298
299 // copy full_length frames to new_data
300 if ( __loops.mode==Loops::REVERSE && ( __loops.count==0 || full_loop ) ) {
301 if( full_loop ) {
302 // copy end => start
303 for( int i=0, j=__loops.end_frame; i<full_length; i++, j-- ) {
304 new_data_l[i]=__data_l[j];
305 }
306 for( int i=0, j=__loops.end_frame; i<full_length; i++, j-- ) {
307 new_data_r[i]=__data_r[j];
308 }
309 } else {
310 // copy start => loop
311 int to_loop = __loops.loop_frame - __loops.start_frame;
312 memcpy( new_data_l, __data_l+__loops.start_frame, sizeof( float )*to_loop );
313 memcpy( new_data_r, __data_r+__loops.start_frame, sizeof( float )*to_loop );
314 // copy end => loop
315 for( int i=to_loop, j=__loops.end_frame; i<full_length; i++, j-- ) {
316 new_data_l[i]=__data_l[j];
317 }
318 for( int i=to_loop, j=__loops.end_frame; i<full_length; i++, j-- ) {
319 new_data_r[i]=__data_r[j];
320 }
321 }
322 } else {
323 // copy start => end
324 memcpy( new_data_l, __data_l+__loops.start_frame, sizeof( float )*full_length );
325 memcpy( new_data_r, __data_r+__loops.start_frame, sizeof( float )*full_length );
326 }
327 // copy the loops
328 if( __loops.count>0 ) {
329 int x = full_length;
330 bool forward = ( __loops.mode==Loops::FORWARD );
331 bool ping_pong = ( __loops.mode==Loops::PINGPONG );
332 for( int i=0; i<__loops.count; i++ ) {
333 if ( forward ) {
334 // copy loop => end
335 memcpy( &new_data_l[x], __data_l+__loops.loop_frame, sizeof( float )*loop_length );
336 memcpy( &new_data_r[x], __data_r+__loops.loop_frame, sizeof( float )*loop_length );
337 } else {
338 // copy end => loop
339 for( int i=__loops.end_frame, y=x; i>__loops.loop_frame; i--, y++ ) {
340 new_data_l[y]=__data_l[i];
341 }
342 for( int i=__loops.end_frame, y=x; i>__loops.loop_frame; i--, y++ ) {
343 new_data_r[y]=__data_r[i];
344 }
345 }
346 x+=loop_length;
347 if( ping_pong ) {
348 forward=!forward;
349 }
350 }
351 assert( x==new_length );
352 }
353 delete[] __data_l;
354 delete[] __data_r;
355 __data_l = new_data_l;
356 __data_r = new_data_r;
357 __frames = new_length;
358 __is_modified = true;
359
360 return true;
361}
362
364{
365 // TODO frame width (841) and height (91) should go out of here
366 // the VelocityEnvelope should be processed within TargetWaveDisplay
367 // so that we here have ( int frame_idx, float scale ) points
368 // but that will break the xml storage
369 if ( __velocity_envelope.size() == 0 ) {
370 return;
371 }
372
373 float inv_resolution = __frames / 841.0F;
374 for ( int i = 1; i < __velocity_envelope.size(); i++ ) {
375 float y = ( 91 - __velocity_envelope[i - 1].value ) / 91.0F;
376 float k = ( 91 - __velocity_envelope[i].value ) / 91.0F;
377 int start_frame = __velocity_envelope[i - 1].frame * inv_resolution;
378 int end_frame = __velocity_envelope[i].frame * inv_resolution;
379 if ( i == __velocity_envelope.size() -1 ) {
380 end_frame = __frames;
381 }
382 int length = end_frame - start_frame ;
383 float step = ( y - k ) / length;;
384 for ( int z = start_frame ; z < end_frame; z++ ) {
385 __data_l[z] = __data_l[z] * y;
386 __data_r[z] = __data_r[z] * y;
387 y-=step;
388 }
389 }
390
391 __is_modified = true;
392}
393
395{
396 if( __pan_envelope.size() == 0 ) {
397 return;
398 }
399
400 float inv_resolution = __frames / 841.0F;
401 for ( int i = 1; i < __pan_envelope.size(); i++ ) {
402 float y = ( 45 - __pan_envelope[i - 1].value ) / 45.0F;
403 float k = ( 45 - __pan_envelope[i].value ) / 45.0F;
404 int start_frame = __pan_envelope[i - 1].frame * inv_resolution;
405 int end_frame = __pan_envelope[i].frame * inv_resolution;
406 if ( i == __pan_envelope.size() -1 ) {
407 end_frame = __frames;
408 }
409 int length = end_frame - start_frame ;
410 float step = ( y - k ) / length;;
411 for ( int z = start_frame ; z < end_frame; z++ ) {
412 // seems wrong to modify only one channel ?!?!
413 if( y < 0 ) {
414 float k = 1 + y;
415 __data_l[z] = __data_l[z] * k;
416 __data_r[z] = __data_r[z];
417 } else if ( y > 0 ) {
418 float k = 1 - y;
419 __data_l[z] = __data_l[z];
420 __data_r[z] = __data_r[z] * k;
421 } else if( y==0 ) {
422 __data_l[z] = __data_l[z];
423 __data_r[z] = __data_r[z];
424 }
425 y-=step;
426 }
427 }
428
429 __is_modified = true;
430}
431
432void Sample::apply_rubberband( float fBpm ) {
433 // TODO see Rubberband declaration in sample.h
434#ifdef H2CORE_HAVE_RUBBERBAND
435 if( ! __rubberband.use ){
436 // Default behavior
437 return;
438 }
439
440 // compute rubberband options
441 double output_duration = 60.0 / fBpm * __rubberband.divider;
442 double time_ratio = output_duration / get_sample_duration();
443 RubberBand::RubberBandStretcher::Options options =
445 double pitch_scale = compute_pitch_scale( __rubberband );
446 // output buffer
447 //
448 // Sometimes the Rubber Band result is _way_ larger than expected,
449 // e.g. `out_buffer_size` = 1837 and retrieved frames = 11444. No
450 // idea what is going on there but it would make Hydrogen crash if
451 // not accounting for resizing the output buffer. The +10 is in
452 // place to cover the more frequent situations of a difference of
453 // just one frame.
454 int out_buffer_size = static_cast<int>( __frames * time_ratio + 0.1 + 10 );
455 // instantiate rubberband
456 RubberBand::RubberBandStretcher rubber = RubberBand::RubberBandStretcher( __sample_rate, 2, options, time_ratio, pitch_scale );
457 rubber.setDebugLevel( RUBBERBAND_DEBUG );
458 // This option will be ignored in real-time processing.
459 rubber.setExpectedInputDuration( __frames );
460
461 int retrieved = 0;
462 //int buffer_free = out_buffer_size;
463 float* out_data_l = new float[ out_buffer_size ];
464 float* out_data_r = new float[ out_buffer_size ];
465 float* out_data_l_tmp;
466 float* out_data_r_tmp;
467
468 DEBUGLOG( QString( "on %1\n\toptions\t\t: %2\n\ttime ratio\t: %3\n\tpitch\t\t: %4" ).arg( get_filename() ).arg( options ).arg( time_ratio ).arg( pitch_scale ) );
469
470 float* ibuf[2];
471 int block_size = MAX_BUFFER_SIZE;
472
473 // If the RUB button in the player control is activated and
474 // Hydrogen is told to apply Rubber Band to samples on-the-fly
475 // when encountering tempo changes, we will use Rubber Band's
476 // real-time processing mode.
477 if ( !Preferences::get_instance()->getRubberBandBatchMode() ) {
478 ibuf[0] = __data_l;
479 ibuf[1] = __data_r;
480 rubber.study( ibuf, __frames, true );
481 } else {
482 rubber.setMaxProcessSize( block_size );
483 }
484
485 // retrieve data
486 float* obuf[2];
487 int processed = 0;
488 int available = 0;
489 int nRequired = 0;
490
491 while( processed < __frames ) {
492
493 if ( !Preferences::get_instance()->getRubberBandBatchMode() ) {
494 // Ask Rubber Band how many samples it requires to produce
495 // further output.
496 nRequired = rubber.getSamplesRequired();
497 } else {
498 nRequired = block_size;
499 }
500 bool final = (processed + nRequired >= __frames);
501 int ibs = (final ? (__frames-processed) : nRequired );
502 float tempIbufL[ibs];
503 float tempIbufR[ibs];
504 for(int i = 0 ; i < ibs; i++) {
505 tempIbufL[i] = __data_l[i + processed];
506 tempIbufR[i] = __data_r[i + processed];
507 }
508 ibuf[0] = tempIbufL;
509 ibuf[1] = tempIbufR;
510 rubber.process( ibuf, ibs, final );
511 processed += ibs;
512
513 // .available() == 0 does indicate that Rubber Band requires
514 // more input samples in order to produce more output. Whether
515 // the stretching is complete will be checked after the parent
516 // while loop.
517 while( (available=rubber.available()) > 0 ) {
518
519 if ( retrieved + available > out_buffer_size ) {
520 // The buffers defined above are too small.
521 int nNewBufferSize = static_cast<int>( ( retrieved + available ) * 1.2 );
522 WARNINGLOG( QString( "Unexpected output size of stretched Rubber Band sample. Increasing output buffer from [%1] to [%2]" )
523 .arg( out_buffer_size )
524 .arg( nNewBufferSize ) );
525 out_data_l_tmp = new float[ out_buffer_size ];
526 out_data_r_tmp = new float[ out_buffer_size ];
527 memcpy( out_data_l_tmp, out_data_l, out_buffer_size * sizeof( float ) );
528 memcpy( out_data_r_tmp, out_data_r, out_buffer_size * sizeof( float ) );
529 delete [] out_data_l;
530 delete [] out_data_r;
531 out_data_l = new float[ nNewBufferSize ];
532 out_data_r = new float[ nNewBufferSize ];
533 memcpy( out_data_l, out_data_l_tmp, out_buffer_size * sizeof( float ) );
534 memcpy( out_data_r, out_data_r_tmp, out_buffer_size * sizeof( float ) );
535 delete [] out_data_l_tmp;
536 delete [] out_data_r_tmp;
537 }
538
539 obuf[0] = &out_data_l[retrieved];
540 obuf[1] = &out_data_r[retrieved];
541 int n = rubber.retrieve( obuf, available);
542
543 retrieved += n;
544 }
545
546 if( final ){
547 break;
548 }
549
550 }
551
552 // second run of stretcher to retrieve all last
553 // frames until stretcher returns -1.
554 while( (available=rubber.available())!= -1) {
555
556 if ( retrieved + available > out_buffer_size ) {
557 // The buffers defined above are too small.
558 int nNewBufferSize = static_cast<int>( ( retrieved + available ) * 1.5 );
559 WARNINGLOG( QString( "Unexpected output size of stretched Rubber Band sample. Increasing output buffer from [%1] to [%2[" )
560 .arg( out_buffer_size )
561 .arg( nNewBufferSize ) );
562 out_data_l_tmp = new float[ out_buffer_size ];
563 out_data_r_tmp = new float[ out_buffer_size ];
564 memcpy( out_data_l_tmp, out_data_l, out_buffer_size * sizeof( float ) );
565 memcpy( out_data_r_tmp, out_data_r, out_buffer_size * sizeof( float ) );
566 delete [] out_data_l;
567 delete [] out_data_r;
568 out_data_l = new float[ nNewBufferSize ];
569 out_data_r = new float[ nNewBufferSize ];
570 memcpy( out_data_l, out_data_l_tmp, out_buffer_size * sizeof( float ) );
571 memcpy( out_data_r, out_data_r_tmp, out_buffer_size * sizeof( float ) );
572 delete [] out_data_l_tmp;
573 delete [] out_data_r_tmp;
574
575 out_buffer_size = nNewBufferSize;
576 }
577
578 obuf[0] = &out_data_l[retrieved];
579 obuf[1] = &out_data_r[retrieved];
580 int n = rubber.retrieve( obuf, available);
581
582 retrieved += n;
583 }
584
585 delete [] __data_l;
586 delete [] __data_r;
587 __data_l = new float[ retrieved ];
588 __data_r = new float[ retrieved ];
589 memcpy( __data_l, out_data_l, retrieved*sizeof( float ) );
590 memcpy( __data_r, out_data_r, retrieved*sizeof( float ) );
591 delete [] out_data_l;
592 delete [] out_data_r;
593
594 // update sample
595 __frames = retrieved;
596 __is_modified = true;
597#endif
598}
599
601{
602 if ( ! __rubberband.use ) {
603 // Default behavior
604 return true;
605 }
606
607 //set the path to rubberband-cli
609 //test the path. if test fails return NULL
610 if ( QFile( program ).exists() == false && __rubberband.use ) {
611 ERRORLOG( QString( "Rubberband executable: File %1 not found" ).arg( program ) );
612 return false;
613 }
614
615 QString outfilePath = QDir::tempPath() + "/tmp_rb_outfile.wav";
616 if( !write( outfilePath ) ) {
617 ERRORLOG( "unable to write sample" );
618 return false;
619 };
620
621 unsigned rubberoutframes = 0;
622 double ratio = 1.0;
623 double durationtime = 60.0 / fBpm * __rubberband.divider/*beats*/;
624 double induration = get_sample_duration();
625 if ( induration != 0.0 ) {
626 ratio = durationtime / induration;
627 }
628
629 rubberoutframes = int( __frames * ratio + 0.1 );
630 _INFOLOG( QString( "ratio: %1, rubberoutframes: %2, rubberinframes: %3" ).arg( ratio ).arg ( rubberoutframes ).arg ( __frames ) );
631
632 QObject* pParent = nullptr;
633 QProcess* pRubberbandProc = new QProcess( pParent );
634
635 QStringList arguments;
636 QString rCs = QString( " %1" ).arg( __rubberband.c_settings );
637 float fFrequency = Note::pitchToFrequency( ( double )__rubberband.pitch );
638 QString rFs = QString( " %1" ).arg( fFrequency );
639 QString rubberResultPath = QDir::tempPath() + "/tmp_rb_result_file.wav";
640
641 arguments << "-D" << QString( " %1" ).arg( durationtime ) //stretch or squash to make output file X seconds long
642 << "--threads" //assume multi-CPU even if only one CPU is identified
643 << "-P" //aim for minimal time distortion
644 << "-f" << rFs //frequency
645 << "-c" << rCs //"crispness" levels
646 << outfilePath //infile
647 << rubberResultPath; //outfile
648
649 pRubberbandProc->start( program, arguments );
650
651 while( pRubberbandProc->state() != QProcess::NotRunning
652 && !pRubberbandProc->waitForFinished() ) {
653 //_ERRORLOG( QString( "processing" ));
654 }
655
656 delete pRubberbandProc;
657 if ( QFile( rubberResultPath ).exists() == false ) {
658 _ERRORLOG( QString( "Rubberband reimporter File %1 not found" ).arg( rubberResultPath ) );
659 return false;
660 }
661
662 auto p_Rubberbanded = Sample::load( rubberResultPath );
663 if( p_Rubberbanded == nullptr ) {
664 return false;
665 }
666
667 QFile( outfilePath ).remove();
668
669 QFile( rubberResultPath ).remove();
670
671 __frames = p_Rubberbanded->get_frames();
672
673 __data_l = p_Rubberbanded->get_data_l();
674 __data_r = p_Rubberbanded->get_data_r();
675 p_Rubberbanded->__data_l = nullptr;
676 p_Rubberbanded->__data_r = nullptr;
677
678 __is_modified = true;
679
680 return true;
681}
682
684{
685 if ( sMode == "forward" ) {
686 return Loops::FORWARD;
687 } else if ( sMode == "reverse" ) {
688 return Loops::REVERSE;
689 } else if ( sMode == "pingpong" ) {
690 return Loops::PINGPONG;
691 }
692
693 return Loops::FORWARD;
694}
695
696bool Sample::write( const QString& path, int format )
697{
698 float* obuf = new float[ SAMPLE_CHANNELS * __frames ];
699 for ( int i = 0; i < __frames; ++i ) {
700 float value_l = __data_l[i];
701 float value_r = __data_r[i];
702
703 if ( value_l > 1.f ) {
704 value_l = 1.f;
705 } else if ( value_l < -1.f ) {
706 value_l = -1.f;
707 } else if ( value_r > 1.f ) {
708 value_r = 1.f;
709 } else if ( value_r < -1.f ) {
710 value_r = -1.f;
711 }
712
713 obuf[ i* SAMPLE_CHANNELS + 0 ] = value_l;
714 obuf[ i* SAMPLE_CHANNELS + 1 ] = value_r;
715 }
716 SF_INFO sf_info;
717 sf_info.channels = SAMPLE_CHANNELS;
718 sf_info.frames = __frames;
719 sf_info.samplerate = __sample_rate;
720 sf_info.format = format;
721 if ( !sf_format_check( &sf_info ) ) {
722 ERRORLOG( "SF_INFO error" );
723 delete[] obuf;
724 return false;
725 }
726
727#ifdef WIN32
728 // On Windows we use a special version of sf_open to ensure we get all
729 // characters of the filename entered in the GUI right. No matter which
730 // encoding was used locally.
731 // We have to terminate the string using a null character ourselves.
732 QString sPaddedPath = QString( path ).append( '\0' );
733 wchar_t* encodedFilename = new wchar_t[ sPaddedPath.size() ];
734
735 sPaddedPath.toWCharArray( encodedFilename );
736
737 SNDFILE* sf_file = sf_wchar_open( encodedFilename, SFM_WRITE,
738 &sf_info );
739 delete encodedFilename;
740#else
741 const auto sPathLocal8Bit = path.toLocal8Bit();
742 SNDFILE* sf_file = sf_open( sPathLocal8Bit.data(), SFM_WRITE, &sf_info );
743#endif
744
745 if ( sf_file == nullptr ) {
746 ERRORLOG( QString( "Unable to create file [%1] with format [%2]: %3" )
747 .arg( path )
748 .arg( sndfileFormatToQString( format ) )
749 .arg( sf_strerror( sf_file ) ) );
750 sf_close( sf_file );
751 delete[] obuf;
752 return false;
753 }
754
755 sf_count_t res = sf_writef_float( sf_file, obuf, __frames );
756
757 if ( res<=0 ) {
758 ERRORLOG( QString( "sf_writef_float error : %1" ).arg( sf_strerror( sf_file ) ) );
759 sf_close( sf_file );
760 delete[] obuf;
761 return false;
762 }
763
764 sf_close( sf_file );
765 delete[] obuf;
766 return true;
767}
768
769QString Sample::Loops::toQString( const QString& sPrefix, bool bShort ) const {
770 QString s = Base::sPrintIndention;
771 QString sOutput;
772 if ( ! bShort ) {
773 sOutput = QString( "%1[Loops]\n" ).arg( sPrefix )
774 .append( QString( "%1%2start_frame: %3\n" ).arg( sPrefix ).arg( s ).arg( start_frame ) )
775 .append( QString( "%1%2loop_frame: %3\n" ).arg( sPrefix ).arg( s ).arg( loop_frame ) )
776 .append( QString( "%1%2end_frame: %3\n" ).arg( sPrefix ).arg( s ).arg( end_frame ) )
777 .append( QString( "%1%2count: %3\n" ).arg( sPrefix ).arg( s ).arg( count ) )
778 .append( QString( "%1%2mode: %3\n" ).arg( sPrefix ).arg( s ).arg( mode ) );
779 } else {
780 sOutput = QString( "[Loops]" )
781 .append( QString( " start_frame: %1" ).arg( start_frame ) )
782 .append( QString( ", loop_frame: %1" ).arg( loop_frame ) )
783 .append( QString( ", end_frame: %1" ).arg( end_frame ) )
784 .append( QString( ", count: %1" ).arg( count ) )
785 .append( QString( ", mode: %1" ).arg( mode ) );
786 }
787
788 return sOutput;
789}
790
791QString Sample::Rubberband::toQString( const QString& sPrefix, bool bShort ) const {
792 QString s = Base::sPrintIndention;
793 QString sOutput;
794 if ( ! bShort ) {
795 sOutput = QString( "%1[Rubberband]\n" ).arg( sPrefix )
796 .append( QString( "%1%2use: %3\n" ).arg( sPrefix ).arg( s ).arg( use ) )
797 .append( QString( "%1%2divider: %3\n" ).arg( sPrefix ).arg( s ).arg( divider ) )
798 .append( QString( "%1%2pitch: %3\n" ).arg( sPrefix ).arg( s ).arg( pitch ) )
799 .append( QString( "%1%2c_settings: %3\n" ).arg( sPrefix ).arg( s ).arg( c_settings ) );
800 } else {
801 sOutput = QString( "[Rubberband]" )
802 .append( QString( " use: %1" ).arg( use ) )
803 .append( QString( ", divider: %1" ).arg( divider ) )
804 .append( QString( ", pitch: %1" ).arg( pitch ) )
805 .append( QString( ", c_settings: %1" ).arg( c_settings ) );
806 }
807 return sOutput;
808}
809
810QString Sample::toQString( const QString& sPrefix, bool bShort ) const {
811 QString s = Base::sPrintIndention;
812 QString sOutput;
813 if ( ! bShort ) {
814 sOutput = QString( "%1[Sample]\n" ).arg( sPrefix )
815 .append( QString( "%1%2filepath: %3\n" ).arg( sPrefix ).arg( s ).arg( __filepath ) )
816 .append( QString( "%1%2frames: %3\n" ).arg( sPrefix ).arg( s ).arg( __frames ) )
817 .append( QString( "%1%2sample_rate: %3\n" ).arg( sPrefix ).arg( s ).arg( __sample_rate ) )
818 .append( QString( "%1%2is_modified: %3\n" ).arg( sPrefix ).arg( s ).arg( __is_modified ) )
819 .append( QString( "%1%2m_license: %3\n" ).arg( sPrefix ).arg( s ).arg( m_license.toQString() ) )
820 .append( QString( "%1" ).arg( __loops.toQString( sPrefix + s, bShort ) ) )
821 .append( QString( "%1" ).arg( __rubberband.toQString( sPrefix + s, bShort ) ) );
822 } else {
823 sOutput = QString( "[Sample]" )
824 .append( QString( " filepath: %1" ).arg( __filepath ) )
825 .append( QString( ", frames: %1" ).arg( __frames ) )
826 .append( QString( ", sample_rate: %1" ).arg( __sample_rate ) )
827 .append( QString( ", is_modified: %1" ).arg( __is_modified ) )
828 .append( QString( ", m_license: %1" ).arg( m_license.toQString() ) )
829 .append( QString( ", [%1]" ).arg( __loops.toQString( sPrefix + s, bShort ) ) )
830 .append( QString( ", [%1]\n" ).arg( __rubberband.toQString( sPrefix + s, bShort ) ) );
831 }
832
833 return sOutput;
834}
835
836QString Sample::sndfileFormatToQString( int nFormat ) {
837 QString sFormat;
838 if ( nFormat & 0x010000 ) {
839 sFormat = "Microsoft WAV format (little endian)";
840 } else if ( nFormat & 0x020000 ) {
841 sFormat = "Apple/SGI AIFF format (big endian)";
842 } else if ( nFormat & 0x030000 ) {
843 sFormat = "Sun/NeXT AU format (big endian)";
844 } else if ( nFormat & 0x040000 ) {
845 sFormat = "RAW PCM data";
846 } else if ( nFormat & 0x050000 ) {
847 sFormat = "Ensoniq PARIS file format";
848 } else if ( nFormat & 0x060000 ) {
849 sFormat = "Amiga IFF / SVX8 / SV16 format";
850 } else if ( nFormat & 0x070000 ) {
851 sFormat = "Sphere NIST format";
852 } else if ( nFormat & 0x080000 ) {
853 sFormat = "VOC files";
854 } else if ( nFormat & 0x0A0000 ) {
855 sFormat = "Berkeley/IRCAM/CARL";
856 } else if ( nFormat & 0x0B0000 ) {
857 sFormat = "Sonic Foundry's 64 bit RIFF/WAV";
858 } else if ( nFormat & 0x0C0000 ) {
859 sFormat = "Matlab (tm) V4.2 / GNU Octave 2.0";
860 } else if ( nFormat & 0x0D0000 ) {
861 sFormat = "Matlab (tm) V5.0 / GNU Octave 2.1";
862 } else if ( nFormat & 0x0E0000 ) {
863 sFormat = "Portable Voice Format";
864 } else if ( nFormat & 0x0F0000 ) {
865 sFormat = "Fasttracker 2 Extended Instrument";
866 } else if ( nFormat & 0x100000 ) {
867 sFormat = "HMM Tool Kit format";
868 } else if ( nFormat & 0x110000 ) {
869 sFormat = "Midi Sample Dump Standard";
870 } else if ( nFormat & 0x120000 ) {
871 sFormat = "Audio Visual Research";
872 } else if ( nFormat & 0x130000 ) {
873 sFormat = "MS WAVE with WAVEFORMATEX";
874 } else if ( nFormat & 0x160000 ) {
875 sFormat = "Sound Designer 2";
876 } else if ( nFormat & 0x170000 ) {
877 sFormat = "FLAC lossless file format";
878 } else if ( nFormat & 0x180000 ) {
879 sFormat = "Core Audio File format";
880 } else if ( nFormat & 0x190000 ) {
881 sFormat = "Psion WVE format";
882 } else if ( nFormat & 0x200000 ) {
883 sFormat = "Xiph OGG container";
884 } else if ( nFormat & 0x210000 ) {
885 sFormat = "Akai MPC 2000 sampler";
886 } else if ( nFormat & 0x220000 ) {
887 sFormat = "RF64 WAV file";
888 } else if ( nFormat & 0x230000 ) {
889 sFormat = "MPEG-1/2 audio stream_FORMAT_OGG";
890 } else {
891 return QString( "Unknown format [%1]" ).arg( nFormat );
892 }
893
894 QString sSubType;
895 if ( nFormat & 0x0001 ) {
896 sSubType = "Signed 8 bit data";
897 } else if ( nFormat & 0x0002 ) {
898 sSubType = "Signed 16 bit data";
899 } else if ( nFormat & 0x0003 ) {
900 sSubType = "Signed 24 bit data";
901 } else if ( nFormat & 0x0004 ) {
902 sSubType = "Signed 32 bit data";
903 } else if ( nFormat & 0x0005 ) {
904 sSubType = "Unsigned 8 bit data (WAV and RAW only)";
905 } else if ( nFormat & 0x0006 ) {
906 sSubType = "32 bit float data";
907 } else if ( nFormat & 0x0007 ) {
908 sSubType = "64 bit float data";
909 } else if ( nFormat & 0x0010 ) {
910 sSubType = "U-Law encoded";
911 } else if ( nFormat & 0x0011 ) {
912 sSubType = "A-Law encoded";
913 } else if ( nFormat & 0x0012 ) {
914 sSubType = "IMA ADPCM";
915 } else if ( nFormat & 0x0013 ) {
916 sSubType = "Microsoft ADPCM";
917 } else if ( nFormat & 0x0020 ) {
918 sSubType = "GSM 6.10 encoding";
919 } else if ( nFormat & 0x0021 ) {
920 sSubType = "OKI / Dialogix ADPCM";
921 } else if ( nFormat & 0x0022 ) {
922 sSubType = "16kbs NMS G721-variant encoding";
923 } else if ( nFormat & 0x0023 ) {
924 sSubType = "24kbs NMS G721-variant encoding";
925 } else if ( nFormat & 0x0024 ) {
926 sSubType = "32kbs NMS G721-variant encoding";
927 } else if ( nFormat & 0x0030 ) {
928 sSubType = "32kbs G721 ADPCM encoding";
929 } else if ( nFormat & 0x0031 ) {
930 sSubType = "24kbs G723 ADPCM encoding";
931 } else if ( nFormat & 0x0032 ) {
932 sSubType = "40kbs G723 ADPCM encoding";
933 } else if ( nFormat & 0x0040 ) {
934 sSubType = "12 bit Delta Width Variable Word encoding";
935 } else if ( nFormat & 0x0041 ) {
936 sSubType = "16 bit Delta Width Variable Word encoding";
937 } else if ( nFormat & 0x0042 ) {
938 sSubType = "24 bit Delta Width Variable Word encoding";
939 } else if ( nFormat & 0x0043 ) {
940 sSubType = "N bit Delta Width Variable Word encoding";
941 } else if ( nFormat & 0x0050 ) {
942 sSubType = "8 bit differential PCM (XI only)";
943 } else if ( nFormat & 0x0051 ) {
944 sSubType = "16 bit differential PCM (XI only)";
945 } else if ( nFormat & 0x0060 ) {
946 sSubType = "Xiph Vorbis encoding";
947 } else if ( nFormat & 0x0064 ) {
948 sSubType = "Xiph/Skype Opus encoding";
949 } else if ( nFormat & 0x0070 ) {
950 sSubType = "Apple Lossless Audio Codec (16 bit)";
951 } else if ( nFormat & 0x0071 ) {
952 sSubType = "Apple Lossless Audio Codec (20 bit)";
953 } else if ( nFormat & 0x0072 ) {
954 sSubType = "Apple Lossless Audio Codec (24 bit)";
955 } else if ( nFormat & 0x0073 ) {
956 sSubType = "Apple Lossless Audio Codec (32 bit)";
957 } else if ( nFormat & 0x0080 ) {
958 sSubType = "MPEG-1 Audio Layer I";
959 } else if ( nFormat & 0x0081 ) {
960 sSubType = "MPEG-1 Audio Layer II";
961 } else if ( nFormat & 0x0082 ) {
962 sSubType = "MPEG-2 Audio Layer III";
963 } else {
964 INFOLOG( QString( "Unknown subtype [%1]" ).arg( nFormat ) );
965 }
966
967 QString sEndianness;
968 if ( nFormat & 0x00000000 ) {
969 sEndianness = "Default file endian-ness";
970 } else if ( nFormat & 0x10000000 ) {
971 sEndianness = "Force little endian-ness";
972 } else if ( nFormat & 0x20000000 ) {
973 sEndianness = "Force big endian-ness";
974 } else if ( nFormat & 0x30000000 ) {
975 sEndianness = "Force CPU endian-ness";
976 }
977
978 if ( ! sSubType.isEmpty() ) {
979 sFormat.append( QString( " - %1" ).arg( sSubType ) );
980 }
981 if ( ! sEndianness.isEmpty() ) {
982 sFormat.append( QString( " - %1" ).arg( sEndianness ) );
983 }
984
985 return sFormat;
986}
987
988#ifdef H2CORE_HAVE_RUBBERBAND
989static double compute_pitch_scale( const Sample::Rubberband& rb )
990{
991 return Note::pitchToFrequency( rb.pitch );
992}
993
994static RubberBand::RubberBandStretcher::Options compute_rubberband_options( const Sample::Rubberband& rb )
995{
996 // default settings
997 enum {
998 CompoundDetector,
999 PercussiveDetector,
1000 SoftDetector
1001 } detector = CompoundDetector;
1002 enum {
1003 NoTransients,
1004 BandLimitedTransients,
1005 Transients
1006 } transients = Transients;
1007 bool lamination = true;
1008 bool longwin = false;
1009 bool shortwin = false;
1010 RubberBand::RubberBandStretcher::Options options = RubberBand::RubberBandStretcher::DefaultOptions;
1011 // apply our settings
1012 int crispness = rb.c_settings;
1013 // compute result options
1014 switch ( crispness ) {
1015 case -1:
1016 crispness = 5;
1017 break;
1018 case 0:
1019 detector = CompoundDetector;
1020 transients = NoTransients;
1021 lamination = false;
1022 longwin = true;
1023 shortwin = false;
1024 break;
1025 case 1:
1026 detector = SoftDetector;
1027 transients = Transients;
1028 lamination = false;
1029 longwin = true;
1030 shortwin = false;
1031 break;
1032 case 2:
1033 detector = CompoundDetector;
1034 transients = NoTransients;
1035 lamination = false;
1036 longwin = false;
1037 shortwin = false;
1038 break;
1039 case 3:
1040 detector = CompoundDetector;
1041 transients = NoTransients;
1042 lamination = true;
1043 longwin = false;
1044 shortwin = false;
1045 break;
1046 case 4:
1047 detector = CompoundDetector;
1048 transients = BandLimitedTransients;
1049 lamination = true;
1050 longwin = false;
1051 shortwin = false;
1052 break;
1053 case 5:
1054 detector = CompoundDetector;
1055 transients = Transients;
1056 lamination = true;
1057 longwin = false;
1058 shortwin = false;
1059 break;
1060 case 6:
1061 detector = CompoundDetector;
1062 transients = Transients;
1063 lamination = false;
1064 longwin = false;
1065 shortwin = true;
1066 break;
1067 };
1068
1069 if ( Preferences::get_instance()->getRubberBandBatchMode() ) {
1070 options |= RubberBand::RubberBandStretcher::OptionProcessRealTime;
1071 } else {
1072 options |= RubberBand::RubberBandStretcher::OptionProcessOffline;
1073 }
1074
1075 if ( !lamination ) options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent;
1076 if ( longwin ) options |= RubberBand::RubberBandStretcher::OptionWindowLong;
1077 if ( shortwin ) options |= RubberBand::RubberBandStretcher::OptionWindowShort;
1078 options |= RubberBand::RubberBandStretcher::OptionStretchPrecise;
1079 //if (smoothing) options |= RubberBand::RubberBandStretcher::OptionSmoothingOn;
1080 //if (formant) options |= RubberBand::RubberBandStretcher::OptionFormantPreserved;
1081 //if (hqpitch) options |= RubberBand::RubberBandStretcher::OptionPitchHighQuality;
1082 options |= RubberBand::RubberBandStretcher::OptionPitchHighQuality;
1083 /*
1084 switch (threading) {
1085 case 0:
1086 options |= RubberBand::RubberBandStretcher::OptionThreadingAuto;
1087 break;
1088 case 1:
1089 options |= RubberBand::RubberBandStretcher::OptionThreadingNever;
1090 break;
1091 case 2:
1092 options |= RubberBand::RubberBandStretcher::OptionThreadingAlways;
1093 break;
1094 }
1095 */
1096 switch ( transients ) {
1097 case NoTransients:
1098 options |= RubberBand::RubberBandStretcher::OptionTransientsSmooth;
1099 break;
1100 case BandLimitedTransients:
1101 options |= RubberBand::RubberBandStretcher::OptionTransientsMixed;
1102 break;
1103 case Transients:
1104 options |= RubberBand::RubberBandStretcher::OptionTransientsCrisp;
1105 break;
1106 }
1107 /*
1108 switch (detector) {
1109 case CompoundDetector:
1110 options |= RubberBand::RubberBandStretcher::OptionDetectorCompound;
1111 break;
1112 case PercussiveDetector:
1113 options |= RubberBand::RubberBandStretcher::OptionDetectorPercussive;
1114 break;
1115 case SoftDetector:
1116 options |= RubberBand::RubberBandStretcher::OptionDetectorSoft;
1117 break;
1118 }
1119 */
1120 return options;
1121}
1122#endif
1123
1124};
1125
1126/* vim: set softtabstop=4 noexpandtab: */
#define INFOLOG(x)
Definition Object.h:240
#define _INFOLOG(x)
Definition Object.h:246
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
#define _ERRORLOG(x)
Definition Object.h:248
#define DEBUGLOG(x)
Definition Object.h:239
#define RUBBERBAND_DEBUG
Definition Sample.cpp:37
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
EnvelopePoint()
default constructor
Definition Sample.cpp:52
int frame
frame index
Definition Sample.h:46
static QString ensure_session_compatibility(const QString &sPath)
If Hydrogen is under session management, we support for paths relative to the session folder.
static bool file_readable(const QString &path, bool silent=false)
returns true if the given path is an existing readable regular file
Wrapper class to help Hydrogen deal with the license information specified in a drumkit.
Definition License.h:48
static double pitchToFrequency(double fPitch)
Convert a logarithmic pitch-space value in semitones to a frequency-domain value.
Definition Note.h:387
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString m_rubberBandCLIexecutable
Rubberband CLI.
QString toQString(const QString &sPrefix="", bool bShort=true) const
Definition Sample.cpp:769
LoopMode
possible sample editing loop mode
Definition Sample.h:81
int end_frame
the frame index where to end the new sample to
Definition Sample.h:88
int start_frame
the frame index where to start the new sample from
Definition Sample.h:86
LoopMode mode
one of the possible loop modes
Definition Sample.h:90
int count
the counts of loops to apply
Definition Sample.h:89
int loop_frame
the frame index where to start the loop from
Definition Sample.h:87
set of rubberband configuration flags
Definition Sample.h:110
QString toQString(const QString &sPrefix="", bool bShort=true) const
Definition Sample.cpp:791
float pitch
desired pitch
Definition Sample.h:114
int c_settings
TODO should be crispness, see rubberband -h.
Definition Sample.h:115
float divider
TODO should be ratio : desired time ratio.
Definition Sample.h:113
bool use
is rubberband enabled
Definition Sample.h:112
int __frames
number of frames in this sample
Definition Sample.h:309
bool __is_modified
true if sample is modified
Definition Sample.h:313
float * __data_l
left channel data
Definition Sample.h:311
bool apply_loops()
apply __loops transformation to the sample
Definition Sample.cpp:260
QString get_filepath() const
Definition Sample.cpp:131
void apply_pan()
apply __pan_envelope transformation to the sample
Definition Sample.cpp:394
int __sample_rate
samplerate for this sample
Definition Sample.h:310
static Loops::LoopMode parse_loop_mode(const QString &string)
parse the given string and rturn the corresponding loop_mode
Definition Sample.cpp:683
Rubberband __rubberband
set of rubberband parameters
Definition Sample.h:317
int get_sample_rate() const
Definition Sample.h:384
void unload()
Flush the current content of the left and right channel and the current metadata.
Definition Sample.h:338
double get_sample_duration() const
Definition Sample.h:394
const QString get_filename() const
Definition Sample.h:369
bool get_is_modified() const
Definition Sample.h:419
static QString sndfileFormatToQString(int nFormat)
Definition Sample.cpp:836
int get_frames() const
Definition Sample.h:379
~Sample()
destructor
Definition Sample.cpp:113
Loops __loops
set of loop parameters
Definition Sample.h:316
License m_license
Transient property indicating the license associated with the sample.
Definition Sample.h:333
float * __data_r
right channel data
Definition Sample.h:312
static std::shared_ptr< Sample > load(const QString &filepath, const License &license=License())
Definition Sample.cpp:136
bool exec_rubberband_cli(float fBpm)
call rubberband cli to modify the sample using __rubberband
Definition Sample.cpp:600
VelocityEnvelope __velocity_envelope
velocity envelope vector
Definition Sample.h:315
Sample(const QString &filepath, const License &license=License(), int frames=0, int sample_rate=0, float *data_l=nullptr, float *data_r=nullptr)
Sample constructor.
Definition Sample.cpp:66
void apply_velocity()
apply __velocity_envelope transformation to the sample
Definition Sample.cpp:363
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Sample.cpp:810
QString __filepath
filepath of the sample
Definition Sample.h:308
bool write(const QString &path, int format=(SF_FORMAT_WAV|SF_FORMAT_PCM_16))
write sample to a file
Definition Sample.cpp:696
PanEnvelope __pan_envelope
pan envelope vector
Definition Sample.h:314
void apply_rubberband(float fBpm)
apply __rubberband transformation to the sample
Definition Sample.cpp:432
std::vector< EnvelopePoint > PanEnvelope
define the type used to store pan envelope points
Definition Sample.h:73
static const std::vector< QString > __loop_modes
loop modes string
Definition Sample.h:319
void set_filename(const QString &filename)
Definition Sample.cpp:123
#define MAX_BUFFER_SIZE
Maximum buffer size.
Definition config.dox:87
#define SAMPLE_CHANNELS
Definition Globals.h:38
static double compute_pitch_scale(const Sample::Rubberband &r)
static RubberBand::RubberBandStretcher::Options compute_rubberband_options(const Sample::Rubberband &r)