/// Render a note /// Return false: the note is not ended /// Return true: the note is ended bool Sampler::__render_note( Note* pNote, unsigned nBufferSize, Song* pSong ) { //infoLog( "[renderNote] instr: " + pNote->getInstrument()->m_sName ); assert( pSong ); unsigned int nFramepos; Hydrogen* pEngine = Hydrogen::get_instance(); AudioOutput* audio_output = pEngine->getAudioOutput(); if ( pEngine->getState() == STATE_PLAYING ) { nFramepos = audio_output->m_transport.m_nFrames; } else { // use this to support realtime events when not playing nFramepos = pEngine->getRealtimeFrames(); } Instrument *pInstr = pNote->get_instrument(); if ( !pInstr ) { ERRORLOG( "NULL instrument" ); return 1; } bool nReturnValues [pInstr->get_components()->size()]; for(int i = 0; i < pInstr->get_components()->size(); i++){ nReturnValues[i] = false; } int nReturnValueIndex = 0; int nAlreadySelectedLayer = -1; for (std::vector<InstrumentComponent*>::iterator it = pInstr->get_components()->begin() ; it !=pInstr->get_components()->end(); ++it) { nReturnValues[nReturnValueIndex] = false; InstrumentComponent *pCompo = *it; DrumkitComponent* pMainCompo = pEngine->getSong()->get_component( pCompo->get_drumkit_componentID() ); if( pNote->get_specific_compo_id() != -1 && pNote->get_specific_compo_id() != pCompo->get_drumkit_componentID() ) continue; if( pInstr->is_preview_instrument() || pInstr->is_metronome_instrument()){ pMainCompo = pEngine->getSong()->get_components()->front(); } else { pMainCompo = pEngine->getSong()->get_component( pCompo->get_drumkit_componentID() ); } assert(pMainCompo); float fLayerGain = 1.0; float fLayerPitch = 0.0; // scelgo il sample da usare in base alla velocity Sample *pSample = nullptr; SelectedLayerInfo *pSelectedLayer = pNote->get_layer_selected( pCompo->get_drumkit_componentID() ); if ( !pSelectedLayer ) { QString dummy = QString( "NULL Layer Information for instrument %1. Component: %2" ).arg( pInstr->get_name() ).arg( pCompo->get_drumkit_componentID() ); WARNINGLOG( dummy ); nReturnValues[nReturnValueIndex] = true; continue; } if( pSelectedLayer->SelectedLayer != -1 ) { InstrumentLayer *pLayer = pCompo->get_layer( pSelectedLayer->SelectedLayer ); if( pLayer ) { pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); } } else { switch ( pInstr->sample_selection_alg() ) { case Instrument::VELOCITY: for ( unsigned nLayer = 0; nLayer < __maxLayers; ++nLayer ) { InstrumentLayer *pLayer = pCompo->get_layer( nLayer ); if ( pLayer == NULL ) continue; if ( ( pNote->get_velocity() >= pLayer->get_start_velocity() ) && ( pNote->get_velocity() <= pLayer->get_end_velocity() ) ) { pSelectedLayer->SelectedLayer = nLayer; pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); break; } } break; case Instrument::RANDOM: if( nAlreadySelectedLayer != -1 ) { InstrumentLayer *pLayer = pCompo->get_layer( nAlreadySelectedLayer ); if ( pLayer != NULL ) { pSelectedLayer->SelectedLayer = nAlreadySelectedLayer; pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); } } if( pSample == NULL ) { int __possibleIndex[ __maxLayers ]; int __poundSamples = 0; for ( unsigned nLayer = 0; nLayer < __maxLayers; ++nLayer ) { InstrumentLayer *pLayer = pCompo->get_layer( nLayer ); if ( pLayer == NULL ) continue; if ( ( pNote->get_velocity() >= pLayer->get_start_velocity() ) && ( pNote->get_velocity() <= pLayer->get_end_velocity() ) ) { __possibleIndex[__poundSamples] = nLayer; __poundSamples++; } } if( __poundSamples > 0 ) { nAlreadySelectedLayer = __possibleIndex[rand() % __poundSamples]; pSelectedLayer->SelectedLayer = nAlreadySelectedLayer; InstrumentLayer *pLayer = pCompo->get_layer( nAlreadySelectedLayer ); pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); } } break; case Instrument::ROUND_ROBIN: if( nAlreadySelectedLayer != -1 ) { InstrumentLayer *pLayer = pCompo->get_layer( nAlreadySelectedLayer ); if ( pLayer != NULL ) { pSelectedLayer->SelectedLayer = nAlreadySelectedLayer; pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); } } if( !pSample ) { int __possibleIndex[ __maxLayers ]; int __foundSamples = 0; float __roundRobinID; for ( unsigned nLayer = 0; nLayer < __maxLayers; ++nLayer ) { InstrumentLayer *pLayer = pCompo->get_layer( nLayer ); if ( pLayer == NULL ) continue; if ( ( pNote->get_velocity() >= pLayer->get_start_velocity() ) && ( pNote->get_velocity() <= pLayer->get_end_velocity() ) ) { __possibleIndex[__foundSamples] = nLayer; __roundRobinID = pLayer->get_start_velocity(); __foundSamples++; } } if( __foundSamples > 0 ) { __roundRobinID = pInstr->get_id() * 10 + __roundRobinID; int p_indexToUse = pSong->get_latest_round_robin(__roundRobinID)+1; if( p_indexToUse > __foundSamples - 1) p_indexToUse = 0; pSong->set_latest_round_robin(__roundRobinID, p_indexToUse); nAlreadySelectedLayer = __possibleIndex[p_indexToUse]; pSelectedLayer->SelectedLayer = nAlreadySelectedLayer; InstrumentLayer *pLayer = pCompo->get_layer( nAlreadySelectedLayer ); pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); } } break; } } if ( !pSample ) { QString dummy = QString( "NULL sample for instrument %1. Note velocity: %2" ).arg( pInstr->get_name() ).arg( pNote->get_velocity() ); WARNINGLOG( dummy ); nReturnValues[nReturnValueIndex] = true; continue; } if ( pSelectedLayer->SamplePosition >= pSample->get_frames() ) { WARNINGLOG( "sample position out of bounds. The layer has been resized during note play?" ); nReturnValues[nReturnValueIndex] = true; continue; } int noteStartInFrames = ( int ) ( pNote->get_position() * audio_output->m_transport.m_nTickSize ) + pNote->get_humanize_delay(); int nInitialSilence = 0; if ( noteStartInFrames > ( int ) nFramepos ) { // scrivo silenzio prima dell'inizio della nota nInitialSilence = noteStartInFrames - nFramepos; int nFrames = nBufferSize - nInitialSilence; if ( nFrames < 0 ) { int noteStartInFramesNoHumanize = ( int )pNote->get_position() * audio_output->m_transport.m_nTickSize; if ( noteStartInFramesNoHumanize > ( int )( nFramepos + nBufferSize ) ) { // this note is not valid. it's in the future...let's skip it.... ERRORLOG( QString( "Note pos in the future?? Current frames: %1, note frame pos: %2" ).arg( nFramepos ).arg(noteStartInFramesNoHumanize ) ); //pNote->dumpInfo(); nReturnValues[nReturnValueIndex] = true; continue; } // delay note execution //INFOLOG( "Delaying note execution. noteStartInFrames: " + to_string( noteStartInFrames ) + ", nFramePos: " + to_string( nFramepos ) ); //return 0; continue; } } float cost_L = 1.0f; float cost_R = 1.0f; float cost_track_L = 1.0f; float cost_track_R = 1.0f; assert(pMainCompo); bool isMutedForExport = (pEngine->getIsExportSessionActive() && !pInstr->is_currently_exported()); /* * Is instrument muted? * * This can be the case either if the song, instrument or component is muted or if we're in an * export session and we're doing per-instruments exports, but this instrument is not currently * beeing exported. */ if ( isMutedForExport || pInstr->is_muted() || pSong->__is_muted || pMainCompo->is_muted() ) { cost_L = 0.0; cost_R = 0.0; if ( Preferences::get_instance()->m_nJackTrackOutputMode == 0 ) { // Post-Fader cost_track_L = 0.0; cost_track_R = 0.0; } } else { // Precompute some values... if ( pInstr->get_apply_velocity() ) { cost_L = cost_L * pNote->get_velocity(); // note velocity cost_R = cost_R * pNote->get_velocity(); // note velocity } cost_L = cost_L * pNote->get_pan_l(); // note pan cost_L = cost_L * fLayerGain; // layer gain cost_L = cost_L * pInstr->get_pan_l(); // instrument pan cost_L = cost_L * pInstr->get_gain(); // instrument gain cost_L = cost_L * pCompo->get_gain(); // Component gain cost_L = cost_L * pMainCompo->get_volume(); // Component volument cost_L = cost_L * pInstr->get_volume(); // instrument volume if ( Preferences::get_instance()->m_nJackTrackOutputMode == 0 ) { // Post-Fader cost_track_L = cost_L * 2; } cost_L = cost_L * pSong->get_volume(); // song volume cost_L = cost_L * 2; // max pan is 0.5 cost_R = cost_R * pNote->get_pan_r(); // note pan cost_R = cost_R * fLayerGain; // layer gain cost_R = cost_R * pInstr->get_pan_r(); // instrument pan cost_R = cost_R * pInstr->get_gain(); // instrument gain cost_R = cost_R * pCompo->get_gain(); // Component gain cost_R = cost_R * pMainCompo->get_volume(); // Component volument cost_R = cost_R * pInstr->get_volume(); // instrument volume if ( Preferences::get_instance()->m_nJackTrackOutputMode == 0 ) { // Post-Fader cost_track_R = cost_R * 2; } cost_R = cost_R * pSong->get_volume(); // song pan cost_R = cost_R * 2; // max pan is 0.5 } // direct track outputs only use velocity if ( Preferences::get_instance()->m_nJackTrackOutputMode == 1 ) { cost_track_L = cost_track_L * pNote->get_velocity(); cost_track_L = cost_track_L * fLayerGain; cost_track_R = cost_track_L; } // Se non devo fare resample (drumkit) posso evitare di utilizzare i float e gestire il tutto in // maniera ottimizzata // constant^12 = 2, so constant = 2^(1/12) = 1.059463. // float nStep = 1.0;1.0594630943593 float fTotalPitch = pNote->get_total_pitch() + fLayerPitch; //_INFOLOG( "total pitch: " + to_string( fTotalPitch ) ); if( ( int )pSelectedLayer->SamplePosition == 0 ) { if( Hydrogen::get_instance()->getMidiOutput() != NULL ){ Hydrogen::get_instance()->getMidiOutput()->handleQueueNote( pNote ); } } if ( fTotalPitch == 0.0 && pSample->get_sample_rate() == audio_output->getSampleRate() ) // NO RESAMPLE nReturnValues[nReturnValueIndex] = __render_note_no_resample( pSample, pNote, pSelectedLayer, pCompo, pMainCompo, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track_L, cost_track_R, pSong ); else // RESAMPLE nReturnValues[nReturnValueIndex] = __render_note_resample( pSample, pNote, pSelectedLayer, pCompo, pMainCompo, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track_L, cost_track_R, fLayerPitch, pSong ); nReturnValueIndex++; } for ( unsigned i = 0 ; i < pInstr->get_components()->size() ; i++ ) if ( !nReturnValues[i] ) return false; return true; }
/// Render a note /// Return 0: the note is not ended /// Return 1: the note is ended unsigned Sampler::__render_note( Note* pNote, unsigned nBufferSize, Song* pSong ) { //infoLog( "[renderNote] instr: " + pNote->getInstrument()->m_sName ); assert( pSong ); unsigned int nFramepos; Hydrogen* pEngine = Hydrogen::get_instance(); AudioOutput* audio_output = pEngine->getAudioOutput(); if ( pEngine->getState() == STATE_PLAYING ) { nFramepos = audio_output->m_transport.m_nFrames; } else { // use this to support realtime events when not playing nFramepos = pEngine->getRealtimeFrames(); } Instrument *pInstr = pNote->get_instrument(); if ( !pInstr ) { ERRORLOG( "NULL instrument" ); return 1; } int nReturnValue = 0; for (std::vector<InstrumentComponent*>::iterator it = pInstr->get_components()->begin() ; it !=pInstr->get_components()->end(); ++it) { InstrumentComponent *pCompo = *it; DrumkitComponent* pMainCompo = 0; if( pInstr->is_preview_instrument() || pInstr->is_metronome_instrument()){ pMainCompo = pEngine->getSong()->get_components()->front(); } else { pMainCompo = pEngine->getSong()->get_component( pCompo->get_drumkit_componentID() ); } assert(pMainCompo); float fLayerGain = 1.0; float fLayerPitch = 0.0; // scelgo il sample da usare in base alla velocity Sample *pSample = NULL; for ( unsigned nLayer = 0; nLayer < MAX_LAYERS; ++nLayer ) { InstrumentLayer *pLayer = pCompo->get_layer( nLayer ); if ( pLayer == NULL ) continue; if ( ( pNote->get_velocity() >= pLayer->get_start_velocity() ) && ( pNote->get_velocity() <= pLayer->get_end_velocity() ) ) { pSample = pLayer->get_sample(); fLayerGain = pLayer->get_gain(); fLayerPitch = pLayer->get_pitch(); break; } } if ( !pSample ) { QString dummy = QString( "NULL sample for instrument %1. Note velocity: %2" ).arg( pInstr->get_name() ).arg( pNote->get_velocity() ); WARNINGLOG( dummy ); nReturnValue = 1; continue; } if ( pNote->get_sample_position( pCompo->get_drumkit_componentID() ) >= pSample->get_frames() ) { WARNINGLOG( "sample position out of bounds. The layer has been resized during note play?" ); nReturnValue = 1; continue; } int noteStartInFrames = ( int ) ( pNote->get_position() * audio_output->m_transport.m_nTickSize ) + pNote->get_humanize_delay(); int nInitialSilence = 0; if ( noteStartInFrames > ( int ) nFramepos ) { // scrivo silenzio prima dell'inizio della nota nInitialSilence = noteStartInFrames - nFramepos; int nFrames = nBufferSize - nInitialSilence; if ( nFrames < 0 ) { int noteStartInFramesNoHumanize = ( int )pNote->get_position() * audio_output->m_transport.m_nTickSize; if ( noteStartInFramesNoHumanize > ( int )( nFramepos + nBufferSize ) ) { // this note is not valid. it's in the future...let's skip it.... ERRORLOG( QString( "Note pos in the future?? Current frames: %1, note frame pos: %2" ).arg( nFramepos ).arg(noteStartInFramesNoHumanize ) ); //pNote->dumpInfo(); nReturnValue = 1; continue; } // delay note execution //INFOLOG( "Delaying note execution. noteStartInFrames: " + to_string( noteStartInFrames ) + ", nFramePos: " + to_string( nFramepos ) ); //return 0; continue; } } float cost_L = 1.0f; float cost_R = 1.0f; float cost_track_L = 1.0f; float cost_track_R = 1.0f; assert(pMainCompo); if ( pInstr->is_muted() || pSong->__is_muted || pMainCompo->is_muted() ) { // is instrument muted? cost_L = 0.0; cost_R = 0.0; if ( Preferences::get_instance()->m_nJackTrackOutputMode == 0 ) { // Post-Fader cost_track_L = 0.0; cost_track_R = 0.0; } } else { // Precompute some values... cost_L = cost_L * pNote->get_velocity(); // note velocity cost_L = cost_L * pNote->get_pan_l(); // note pan cost_L = cost_L * fLayerGain; // layer gain cost_L = cost_L * pInstr->get_pan_l(); // instrument pan cost_L = cost_L * pInstr->get_gain(); // instrument gain cost_L = cost_L * pCompo->get_gain(); // Component gain cost_L = cost_L * pMainCompo->get_volume(); // Component volument cost_L = cost_L * pInstr->get_volume(); // instrument volume if ( Preferences::get_instance()->m_nJackTrackOutputMode == 0 ) { // Post-Fader cost_track_L = cost_L * 2; } cost_L = cost_L * pSong->get_volume(); // song volume cost_L = cost_L * 2; // max pan is 0.5 cost_R = cost_R * pNote->get_velocity(); // note velocity cost_R = cost_R * pNote->get_pan_r(); // note pan cost_R = cost_R * fLayerGain; // layer gain cost_R = cost_R * pInstr->get_pan_r(); // instrument pan cost_R = cost_R * pInstr->get_gain(); // instrument gain cost_R = cost_R * pCompo->get_gain(); // Component gain cost_R = cost_R * pMainCompo->get_volume(); // Component volument cost_R = cost_R * pInstr->get_volume(); // instrument volume if ( Preferences::get_instance()->m_nJackTrackOutputMode == 0 ) { // Post-Fader cost_track_R = cost_R * 2; } cost_R = cost_R * pSong->get_volume(); // song pan cost_R = cost_R * 2; // max pan is 0.5 } // direct track outputs only use velocity if ( Preferences::get_instance()->m_nJackTrackOutputMode == 1 ) { cost_track_L = cost_track_L * pNote->get_velocity(); cost_track_L = cost_track_L * fLayerGain; cost_track_R = cost_track_L; } // Se non devo fare resample (drumkit) posso evitare di utilizzare i float e gestire il tutto in // maniera ottimizzata // constant^12 = 2, so constant = 2^(1/12) = 1.059463. // float nStep = 1.0;1.0594630943593 float fTotalPitch = pNote->get_total_pitch() + fLayerPitch; //_INFOLOG( "total pitch: " + to_string( fTotalPitch ) ); if( ( int )pNote->get_sample_position(pCompo->get_drumkit_componentID()) == 0 ) { if( Hydrogen::get_instance()->getMidiOutput() != NULL ){ Hydrogen::get_instance()->getMidiOutput()->handleQueueNote( pNote ); } } if ( fTotalPitch == 0.0 && pSample->get_sample_rate() == audio_output->getSampleRate() ) { // NO RESAMPLE if ( __render_note_no_resample( pSample, pNote, pCompo, pMainCompo, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track_L, cost_track_R, pSong ) == 1 ) nReturnValue = 1; } else { // RESAMPLE if ( __render_note_resample( pSample, pNote, pCompo, pMainCompo, nBufferSize, nInitialSilence, cost_L, cost_R, cost_track_L, cost_track_R, fLayerPitch, pSong ) == 1 ) nReturnValue = 1; } } return nReturnValue; }