34#if defined(H2CORE_HAVE_RUBBERBAND) || _DOXYGEN_
35#include <rubberband/RubberBandStretcher.h>
36#define RUBBERBAND_BUFFER_OVERSIZE 500
37#define RUBBERBAND_DEBUG 0
45#if defined(H2CORE_HAVE_RUBBERBAND) || _DOXYGEN_
66Sample::Sample(
const QString& filepath,
const License& license,
int frames,
int sample_rate,
float* data_l,
float* data_r )
67 : __filepath( filepath ),
69 __sample_rate( sample_rate ),
72 __is_modified( false ),
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." ) );
81 __filepath( pOther->get_filepath() ),
82 __frames( pOther->get_frames() ),
83 __sample_rate( pOther->get_sample_rate() ),
86 __is_modified( pOther->get_is_modified() ),
87 __loops( pOther->__loops ),
88 __rubberband( pOther->__rubberband ),
89 m_license( pOther->m_license )
103 for(
int i=0; i<pPan->size(); i++ ) {
107 PanEnvelope* pVelocity = pOther->get_velocity_envelope();
108 for(
int i=0; i<pVelocity->size(); i++ ) {
125 QFileInfo Filename = QFileInfo( filename );
127 __filepath = QDir(Dest.absolutePath()).filePath( Filename.fileName() );
138 std::shared_ptr<Sample> pSample;
141 ERRORLOG( QString(
"Unable to read %1" ).arg( sFilepath ) );
145 pSample = std::make_shared<Sample>( sFilepath, license );
149 if( !pSample->load() ) {
159 SF_INFO sound_info = {0};
162 SNDFILE* file = sf_open(
get_filepath().toLocal8Bit(), SFM_READ, &sound_info );
171 WARNINGLOG( QString(
"can't handle %1 channels, only 2 will be used" ).arg( sound_info.channels ) );
174 if ( sound_info.frames > ( std::numeric_limits<int>::max()/sound_info.channels ) ) {
175 WARNINGLOG( QString(
"sample frames count (%1) and channels (%2) are too much, truncate it." ).arg( sound_info.frames ).arg( sound_info.channels ) );
176 sound_info.frames = ( std::numeric_limits<int>::max()/sound_info.channels );
181 float* buffer =
new float[ sound_info.frames * sound_info.channels ];
189 sf_count_t count = sf_read_float( file, buffer, sound_info.frames * sound_info.channels );
195 if ( sf_close( file ) != 0 ){
211 __data_l =
new float[ sound_info.frames ];
212 __data_r =
new float[ sound_info.frames ];
213 if ( sound_info.channels == 1 ) {
217 for (
int i = 0; i <
__frames; i++ ) {
230#ifdef H2CORE_HAVE_RUBBERBAND
275 int new_length = full_length + loop_length *
__loops.
count;
277 float* new_data_l =
new float[ new_length ];
278 float* new_data_r =
new float[ new_length ];
332 assert( x==new_length );
354 float inv_resolution =
__frames / 841.0F;
363 int length = end_frame - start_frame ;
364 float step = ( y - k ) / length;;
365 for (
int z = start_frame ; z < end_frame; z++ ) {
381 float inv_resolution =
__frames / 841.0F;
390 int length = end_frame - start_frame ;
391 float step = ( y - k ) / length;;
392 for (
int z = start_frame ; z < end_frame; z++ ) {
398 }
else if ( y > 0 ) {
415#ifdef H2CORE_HAVE_RUBBERBAND
424 RubberBand::RubberBandStretcher::Options options =
435 int out_buffer_size =
static_cast<int>(
__frames * time_ratio + 0.1 + 10 );
437 RubberBand::RubberBandStretcher rubber = RubberBand::RubberBandStretcher(
__sample_rate, 2, options, time_ratio, pitch_scale );
440 rubber.setExpectedInputDuration(
__frames );
444 float* out_data_l =
new float[ out_buffer_size ];
445 float* out_data_r =
new float[ out_buffer_size ];
446 float* out_data_l_tmp;
447 float* out_data_r_tmp;
449 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 ) );
461 rubber.study( ibuf,
__frames,
true );
463 rubber.setMaxProcessSize( block_size );
477 nRequired = rubber.getSamplesRequired();
479 nRequired = block_size;
481 bool final = (processed + nRequired >=
__frames);
482 int ibs = (
final ? (
__frames-processed) : nRequired );
483 float tempIbufL[ibs];
484 float tempIbufR[ibs];
485 for(
int i = 0 ; i < ibs; i++) {
486 tempIbufL[i] =
__data_l[i + processed];
487 tempIbufR[i] =
__data_r[i + processed];
491 rubber.process( ibuf, ibs,
final );
498 while( (available=rubber.available()) > 0 ) {
500 if ( retrieved + available > out_buffer_size ) {
502 int nNewBufferSize =
static_cast<int>( ( retrieved + available ) * 1.2 );
503 WARNINGLOG( QString(
"Unexpected output size of stretched Rubber Band sample. Increasing output buffer from [%1] to [%2]" )
504 .arg( out_buffer_size )
505 .arg( nNewBufferSize ) );
506 out_data_l_tmp =
new float[ out_buffer_size ];
507 out_data_r_tmp =
new float[ out_buffer_size ];
508 memcpy( out_data_l_tmp, out_data_l, out_buffer_size *
sizeof(
float ) );
509 memcpy( out_data_r_tmp, out_data_r, out_buffer_size *
sizeof(
float ) );
510 delete [] out_data_l;
511 delete [] out_data_r;
512 out_data_l =
new float[ nNewBufferSize ];
513 out_data_r =
new float[ nNewBufferSize ];
514 memcpy( out_data_l, out_data_l_tmp, out_buffer_size *
sizeof(
float ) );
515 memcpy( out_data_r, out_data_r_tmp, out_buffer_size *
sizeof(
float ) );
516 delete [] out_data_l_tmp;
517 delete [] out_data_r_tmp;
520 obuf[0] = &out_data_l[retrieved];
521 obuf[1] = &out_data_r[retrieved];
522 int n = rubber.retrieve( obuf, available);
535 while( (available=rubber.available())!= -1) {
537 if ( retrieved + available > out_buffer_size ) {
539 int nNewBufferSize =
static_cast<int>( ( retrieved + available ) * 1.5 );
540 WARNINGLOG( QString(
"Unexpected output size of stretched Rubber Band sample. Increasing output buffer from [%1] to [%2[" )
541 .arg( out_buffer_size )
542 .arg( nNewBufferSize ) );
543 out_data_l_tmp =
new float[ out_buffer_size ];
544 out_data_r_tmp =
new float[ out_buffer_size ];
545 memcpy( out_data_l_tmp, out_data_l, out_buffer_size *
sizeof(
float ) );
546 memcpy( out_data_r_tmp, out_data_r, out_buffer_size *
sizeof(
float ) );
547 delete [] out_data_l;
548 delete [] out_data_r;
549 out_data_l =
new float[ nNewBufferSize ];
550 out_data_r =
new float[ nNewBufferSize ];
551 memcpy( out_data_l, out_data_l_tmp, out_buffer_size *
sizeof(
float ) );
552 memcpy( out_data_r, out_data_r_tmp, out_buffer_size *
sizeof(
float ) );
553 delete [] out_data_l_tmp;
554 delete [] out_data_r_tmp;
556 out_buffer_size = nNewBufferSize;
559 obuf[0] = &out_data_l[retrieved];
560 obuf[1] = &out_data_r[retrieved];
561 int n = rubber.retrieve( obuf, available);
570 memcpy(
__data_l, out_data_l, retrieved*
sizeof(
float ) );
571 memcpy(
__data_r, out_data_r, retrieved*
sizeof(
float ) );
572 delete [] out_data_l;
573 delete [] out_data_r;
592 ERRORLOG( QString(
"Rubberband executable: File %1 not found" ).arg( program ) );
596 QString outfilePath = QDir::tempPath() +
"/tmp_rb_outfile.wav";
597 if( !
write( outfilePath ) ) {
598 ERRORLOG(
"unable to write sample" );
602 unsigned rubberoutframes = 0;
606 if ( induration != 0.0 ) {
607 ratio = durationtime / induration;
610 rubberoutframes = int(
__frames * ratio + 0.1 );
611 _INFOLOG( QString(
"ratio: %1, rubberoutframes: %2, rubberinframes: %3" ).arg( ratio ).arg ( rubberoutframes ).arg (
__frames ) );
613 QObject* pParent =
nullptr;
614 QProcess* pRubberbandProc =
new QProcess( pParent );
616 QStringList arguments;
619 QString rFs = QString(
" %1" ).arg( fFrequency );
620 QString rubberResultPath = QDir::tempPath() +
"/tmp_rb_result_file.wav";
622 arguments <<
"-D" << QString(
" %1" ).arg( durationtime )
630 pRubberbandProc->start( program, arguments );
632 while( pRubberbandProc->state() != QProcess::NotRunning
633 && !pRubberbandProc->waitForFinished() ) {
637 delete pRubberbandProc;
638 if ( QFile( rubberResultPath ).exists() ==
false ) {
639 _ERRORLOG( QString(
"Rubberband reimporter File %1 not found" ).arg( rubberResultPath ) );
644 if( p_Rubberbanded ==
nullptr ) {
648 QFile( outfilePath ).remove();
650 QFile( rubberResultPath ).remove();
652 __frames = p_Rubberbanded->get_frames();
654 __data_l = p_Rubberbanded->get_data_l();
655 __data_r = p_Rubberbanded->get_data_r();
656 p_Rubberbanded->__data_l =
nullptr;
657 p_Rubberbanded->__data_r =
nullptr;
666 if ( sMode ==
"forward" ) {
668 }
else if ( sMode ==
"reverse" ) {
670 }
else if ( sMode ==
"pingpong" ) {
680 for (
int i = 0; i <
__frames; ++i ) {
684 if ( value_l > 1.f ) {
686 }
else if ( value_l < -1.f ) {
688 }
else if ( value_r > 1.f ) {
690 }
else if ( value_r < -1.f ) {
701 sf_info.format = format;
702 if ( !sf_format_check( &sf_info ) ) {
708 SNDFILE* sf_file = sf_open( path.toLocal8Bit().data(), SFM_WRITE, &sf_info ) ;
710 if ( sf_file==
nullptr ) {
711 ___ERRORLOG( QString(
"sf_open error : %1" ).arg( sf_strerror( sf_file ) ) );
717 sf_count_t res = sf_writef_float( sf_file, obuf,
__frames );
720 ___ERRORLOG( QString(
"sf_writef_float error : %1" ).arg( sf_strerror( sf_file ) ) );
735 sOutput = QString(
"%1[Loops]\n" ).arg( sPrefix )
736 .append( QString(
"%1%2start_frame: %3\n" ).arg( sPrefix ).arg( s ).arg(
start_frame ) )
737 .append( QString(
"%1%2loop_frame: %3\n" ).arg( sPrefix ).arg( s ).arg(
loop_frame ) )
738 .append( QString(
"%1%2end_frame: %3\n" ).arg( sPrefix ).arg( s ).arg(
end_frame ) )
739 .append( QString(
"%1%2count: %3\n" ).arg( sPrefix ).arg( s ).arg(
count ) )
740 .append( QString(
"%1%2mode: %3\n" ).arg( sPrefix ).arg( s ).arg(
mode ) );
742 sOutput = QString(
"[Loops]" )
743 .append( QString(
" start_frame: %1" ).arg(
start_frame ) )
744 .append( QString(
", loop_frame: %1" ).arg(
loop_frame ) )
745 .append( QString(
", end_frame: %1" ).arg(
end_frame ) )
746 .append( QString(
", count: %1" ).arg(
count ) )
747 .append( QString(
", mode: %1" ).arg(
mode ) );
757 sOutput = QString(
"%1[Rubberband]\n" ).arg( sPrefix )
758 .append( QString(
"%1%2use: %3\n" ).arg( sPrefix ).arg( s ).arg( use ) )
759 .append( QString(
"%1%2divider: %3\n" ).arg( sPrefix ).arg( s ).arg( divider ) )
760 .append( QString(
"%1%2pitch: %3\n" ).arg( sPrefix ).arg( s ).arg( pitch ) )
761 .append( QString(
"%1%2c_settings: %3\n" ).arg( sPrefix ).arg( s ).arg( c_settings ) );
763 sOutput = QString(
"[Rubberband]" )
764 .append( QString(
" use: %1" ).arg( use ) )
765 .append( QString(
", divider: %1" ).arg( divider ) )
766 .append( QString(
", pitch: %1" ).arg( pitch ) )
767 .append( QString(
", c_settings: %1" ).arg( c_settings ) );
776 sOutput = QString(
"%1[Sample]\n" ).arg( sPrefix )
777 .append( QString(
"%1%2filepath: %3\n" ).arg( sPrefix ).arg( s ).arg(
__filepath ) )
778 .append( QString(
"%1%2frames: %3\n" ).arg( sPrefix ).arg( s ).arg(
__frames ) )
779 .append( QString(
"%1%2sample_rate: %3\n" ).arg( sPrefix ).arg( s ).arg(
__sample_rate ) )
780 .append( QString(
"%1%2is_modified: %3\n" ).arg( sPrefix ).arg( s ).arg(
__is_modified ) )
781 .append( QString(
"%1%2m_license: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_license.
toQString() ) )
785 sOutput = QString(
"[Sample]" )
786 .append( QString(
" filepath: %1" ).arg(
__filepath ) )
787 .append( QString(
", frames: %1" ).arg(
__frames ) )
788 .append( QString(
", sample_rate: %1" ).arg(
__sample_rate ) )
789 .append( QString(
", is_modified: %1" ).arg(
__is_modified ) )
791 .append( QString(
", [%1]" ).arg(
__loops.
toQString( sPrefix + s, bShort ) ) )
798#ifdef H2CORE_HAVE_RUBBERBAND
811 } detector = CompoundDetector;
814 BandLimitedTransients,
816 } transients = Transients;
817 bool lamination =
true;
818 bool longwin =
false;
819 bool shortwin =
false;
820 RubberBand::RubberBandStretcher::Options options = RubberBand::RubberBandStretcher::DefaultOptions;
822 int crispness = rb.c_settings;
824 switch ( crispness ) {
829 detector = CompoundDetector;
830 transients = NoTransients;
836 detector = SoftDetector;
837 transients = Transients;
843 detector = CompoundDetector;
844 transients = NoTransients;
850 detector = CompoundDetector;
851 transients = NoTransients;
857 detector = CompoundDetector;
858 transients = BandLimitedTransients;
864 detector = CompoundDetector;
865 transients = Transients;
871 detector = CompoundDetector;
872 transients = Transients;
880 options |= RubberBand::RubberBandStretcher::OptionProcessRealTime;
882 options |= RubberBand::RubberBandStretcher::OptionProcessOffline;
885 if ( !lamination ) options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent;
886 if ( longwin ) options |= RubberBand::RubberBandStretcher::OptionWindowLong;
887 if ( shortwin ) options |= RubberBand::RubberBandStretcher::OptionWindowShort;
888 options |= RubberBand::RubberBandStretcher::OptionStretchPrecise;
892 options |= RubberBand::RubberBandStretcher::OptionPitchHighQuality;
906 switch ( transients ) {
908 options |= RubberBand::RubberBandStretcher::OptionTransientsSmooth;
910 case BandLimitedTransients:
911 options |= RubberBand::RubberBandStretcher::OptionTransientsMixed;
914 options |= RubberBand::RubberBandStretcher::OptionTransientsCrisp;
static QString sPrintIndention
String used to format the debugging string output of some core classes.
A container for a sample, being able to apply modifications on it.
EnvelopePoint()
default constructor
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.
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
static double pitchToFrequency(double fPitch)
Convert a logarithmic pitch-space value in semitones to a frequency-domain value.
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
LoopMode
possible sample editing loop mode
int end_frame
the frame index where to end the new sample to
int start_frame
the frame index where to start the new sample from
LoopMode mode
one of the possible loop modes
int count
the counts of loops to apply
int loop_frame
the frame index where to start the loop from
set of rubberband configuration flags
QString toQString(const QString &sPrefix="", bool bShort=true) const
int c_settings
TODO should be crispness, see rubberband -h.
float divider
TODO should be ratio : desired time ratio.
bool use
is rubberband enabled
int __frames
number of frames in this sample
bool __is_modified
true if sample is modified
float * __data_l
left channel data
bool apply_loops()
apply __loops transformation to the sample
QString get_filepath() const
void apply_pan()
apply __pan_envelope transformation to the sample
int __sample_rate
samplerate for this sample
static Loops::LoopMode parse_loop_mode(const QString &string)
parse the given string and rturn the corresponding loop_mode
Rubberband __rubberband
set of rubberband parameters
void unload()
Flush the current content of the left and right channel and the current metadata.
double get_sample_duration() const
const QString get_filename() const
Loops __loops
set of loop parameters
License m_license
Transient property indicating the license associated with the sample.
float * __data_r
right channel data
static std::shared_ptr< Sample > load(const QString &filepath, const License &license=License())
bool exec_rubberband_cli(float fBpm)
call rubberband cli to modify the sample using __rubberband
VelocityEnvelope __velocity_envelope
velocity envelope vector
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.
void apply_velocity()
apply __velocity_envelope transformation to the sample
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
QString __filepath
filepath of the sample
bool write(const QString &path, int format=(SF_FORMAT_WAV|SF_FORMAT_PCM_16))
write sample to a file
PanEnvelope __pan_envelope
pan envelope vector
void apply_rubberband(float fBpm)
apply __rubberband transformation to the sample
std::vector< EnvelopePoint > PanEnvelope
define the type used to store pan envelope points
static const std::vector< QString > __loop_modes
loop modes string
void set_filename(const QString &filename)
#define MAX_BUFFER_SIZE
Maximum buffer size.
static double compute_pitch_scale(const Sample::Rubberband &r)
static RubberBand::RubberBandStretcher::Options compute_rubberband_options(const Sample::Rubberband &r)