bool setInstrumentPitch( int instrumentNumber, int value ) { //helper function to set cutOff levels Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); Instrument *instr = instrList->get( instrumentNumber ); if ( instr == NULL) return false; if( value < 63 ){ instr->set_instrument_pitch( (float) ((value-62) / 62.0 * MAX_INSTRUMENT_PITCH ) ); } else if ( value > 64 ) { instr->set_instrument_pitch( (float) ((value-65) / 62.0 * MAX_INSTRUMENT_PITCH ) ); } else { instr->set_instrument_pitch( 0 ); } Hydrogen::get_instance()->setSelectedInstrumentNumber( instrList->index(instr) ); return true; }
void PatternEditorPanel::moveUpBtnClicked(Button *) { Hydrogen *engine = Hydrogen::get_instance(); int nSelectedInstrument = engine->getSelectedInstrumentNumber(); AudioEngine::get_instance()->lock( RIGHT_HERE ); Song *pSong = engine->getSong(); InstrumentList *pInstrumentList = pSong->get_instrument_list(); if ( ( nSelectedInstrument - 1 ) >= 0 ) { pInstrumentList->swap( nSelectedInstrument -1, nSelectedInstrument ); AudioEngine::get_instance()->unlock(); engine->setSelectedInstrumentNumber( nSelectedInstrument - 1 ); pSong->set_is_modified( true ); } else { AudioEngine::get_instance()->unlock(); } }
bool setAbsoluteFXLevel( int nLine, int fx_channel , int fx_param) { //helper function to set fx levels Hydrogen::get_instance()->setSelectedInstrumentNumber( nLine ); Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); Instrument *instr = instrList->get( nLine ); if ( instr == NULL) return false; if( fx_param != 0 ){ instr->set_fx_level( ( (float) (fx_param / 127.0 ) ), fx_channel ); } else { instr->set_fx_level( 0 , fx_channel ); } Hydrogen::get_instance()->setSelectedInstrumentNumber(nLine); return true; }
void JackOutput::jack_session_callback_impl(jack_session_event_t *event) { INFOLOG("jack session calback"); enum session_events{ SAVE_SESSION, SAVE_AND_QUIT, SAVE_TEMPLATE }; Hydrogen* H = Hydrogen::get_instance(); Song* S = H->getSong(); Preferences* P = Preferences::get_instance(); EventQueue* EQ = EventQueue::get_instance(); jack_session_event_t *ev = (jack_session_event_t *) event; QString jackSessionDirectory = (QString) ev->session_dir; QString retval = P->getJackSessionApplicationPath() + " --jacksessionid " + ev->client_uuid; /* Playlist mode */ if ( H->m_PlayList.size() > 0 ) { Playlist* PL = Playlist::get_instance(); if ( PL->get_filename().isEmpty() ) PL->set_filename( "untitled.h2playlist" ); QString FileName = baseName ( PL->get_filename() ); FileName.replace ( QString(" "), QString("_") ); retval += " -p \"${SESSION_DIR}" + FileName + "\""; /* Copy all songs to Session Directory and update playlist */ SongReader reader; for ( uint i = 0; i < H->m_PlayList.size(); ++i ) { QString BaseName = baseName ( H->m_PlayList[i].m_hFile ); QString newName = jackSessionDirectory + BaseName; QString SongPath = reader.getPath ( H->m_PlayList[i].m_hFile ); if ( SongPath != NULL && QFile::copy ( SongPath, newName ) ) { /* Keep only filename on list for relative read */ H->m_PlayList[i].m_hFile = BaseName; //H->m_PlayList[i].m_hScript; } else { /* Note - we leave old path in playlist */ ERRORLOG ( "Can't copy " + H->m_PlayList[i].m_hFile + " to " + newName ); ev->flags = JackSessionSaveError; } } /* Save updated playlist */ if ( ! PL->save ( jackSessionDirectory + FileName ) ) ev->flags = JackSessionSaveError; /* Song Mode */ } else { /* Valid Song is needed */ if ( S->get_filename().isEmpty() ) S->set_filename("untitled.h2song"); QString FileName = baseName ( S->get_filename() ); FileName.replace ( QString(" "), QString("_") ); S->set_filename(jackSessionDirectory + FileName); /* SongReader will look into SESSION DIR anyway */ retval += " -s \"" + FileName + "\""; switch (ev->type) { case JackSessionSave: EQ->push_event(EVENT_JACK_SESSION, SAVE_SESSION); break; case JackSessionSaveAndQuit: EQ->push_event(EVENT_JACK_SESSION, SAVE_SESSION); EQ->push_event(EVENT_JACK_SESSION, SAVE_AND_QUIT); break; default: ERRORLOG( "JackSession: Unknown event type" ); ev->flags = JackSessionSaveError; } } ev->command_line = strdup( retval.toUtf8().constData() ); jack_session_reply (client, ev ); jack_session_event_free (ev); }
void JackAudioDriver::relocateBBT() { Preferences* pPref = Preferences::get_instance(); // If Hydrogen itself is the JACK timebase master, this // function is not relevant. if ( m_transport.m_status != TransportInfo::ROLLING || pPref->m_bJackMasterMode == Preferences::USE_JACK_TIME_MASTER || ! ( m_JackTransportPos.valid & JackPositionBBT ) ) { // WARNINGLOG( "Relocate: Call it off" ); return; } // WARNINGLOG( "Relocate..." ); Hydrogen * H = Hydrogen::get_instance(); Song * S = H->getSong(); // m_JackTransportPos.beat_type contains the denominator of // the time signature and is given in number of ticks per // quarter. Thus, hydrogen_TPB contains the number of beats // per denominator of the JACK time signature. (Ticks per bar) float hydrogen_TPB = ( float )( S->__resolution / m_JackTransportPos.beat_type * 4 ); long bar_ticks = 0; //long beat_ticks = 0; if ( S->get_mode() == Song::SONG_MODE ) { // m_JackTransportPos.bar accesses the current bar at // the transport position. (Reasonable?) assumption // that one pattern is _always_ 1 bar long! bar_ticks = H->getTickForPosition( m_JackTransportPos.bar-1 ); // If a tick corresponding to the position of the // current bar-1 could not be found, we will use the // tick at the very beginning of the song instead. if ( bar_ticks < 0 ) bar_ticks = 0; } // m_JackTransportPos.beat refers to the current beat within a // bar, m_JackTransportPos.tick to the current tick within a // beat, and m_JackTransportPos.ticks_per_beat to the number // of ticks per beat. // Using hydrogen_TPB as conversion factor, the relocation // position in ticks will be calculated by combining the // current bar, beat, and tick. float hydrogen_ticks_to_locate = bar_ticks + ( m_JackTransportPos.beat-1 ) * hydrogen_TPB + m_JackTransportPos.tick * ( hydrogen_TPB / m_JackTransportPos.ticks_per_beat ); INFOLOG( QString( "Position from Timebase Master: BBT [%1,%2,%3]" ).arg( m_JackTransportPos.bar ).arg( m_JackTransportPos.beat ).arg( m_JackTransportPos.tick ) ); // WARNINGLOG( "Tx/Beat = "+to_string(m_JackTransportPos.ticks_per_beat)+", Meter "+to_string(m_JackTransportPos.beats_per_bar)+"/"+to_string(m_JackTransportPos.beat_type)+" =>tick " + to_string( hydrogen_ticks_to_locate ) ); // Before calling this function in updateTransportInfo() the // speed obtained by the JACK server query was already written // to m_transport.m_nBPM. This value can thus be considered // clean. float fNewTickSize = getSampleRate() * 60.0 / m_transport.m_nBPM / S->__resolution; if ( fNewTickSize == 0 ) return; // Note that this will prevent // audioEngine_process_checkBPMChanged() in Hydrogen.cpp from // recalculating things unless m_transport.m_nBpm and S->__bpm // aren't equal. m_transport.m_nTickSize = fNewTickSize; // New transport position in frames long long nNewFrames = ( long long )( hydrogen_ticks_to_locate * fNewTickSize ); #ifndef JACK_NO_BBT_OFFSET // m_JackTransportPos.bbt_offset is a frame offset for the BBT // fields (the given bar, beat, and tick values actually refer // to a time frame_offset frames before the start of the // cycle), should be assumed to be 0 if JackBBTFrameOffset is // not set. If JackBBTFrameOffset is set and this value is // zero, the BBT time refers to the first frame of this // cycle. If the value is positive, the BBT time refers to a // frame that many frames before the start of the cycle. // // Due to the if clause in the beginning of the function, the // following if clause is true at all times. if ( m_JackTransportPos.valid & JackBBTFrameOffset ) nNewFrames += m_JackTransportPos.bbt_offset; #endif m_transport.m_nFrames = nNewFrames; }
int main(int argc, char *argv[]) { try { // Options... char *cp; struct option *op; char opts[NELEM(long_opts) * 3 + 1]; // Build up the short option QString cp = opts; for (op = long_opts; op < &long_opts[NELEM(long_opts)]; op++) { *cp++ = op->val; if (op->has_arg) *cp++ = ':'; if (op->has_arg == optional_argument ) *cp++ = ':'; // gets another one } // Deal with the options QString songFilename; QString playlistFilename; QString outFilename = NULL; QString sSelectedDriver; bool showVersionOpt = false; const char* logLevelOpt = "Error"; bool showHelpOpt = false; QString drumkitName; QString drumkitToLoad; short bits = 16; int rate = 44100; short interpolation = 0; #ifdef H2CORE_HAVE_JACKSESSION QString sessionId; #endif int c; while ( 1 ) { c = getopt_long(argc, argv, opts, long_opts, NULL); if ( c == -1 ) break; switch(c) { case 'd': sSelectedDriver = QString::fromLocal8Bit(optarg); break; case 's': songFilename = QString::fromLocal8Bit(optarg); break; case 'p': playlistFilename = QString::fromLocal8Bit(optarg); break; case 'o': outFilename = QString::fromLocal8Bit(optarg); break; case 'i': //install h2drumkit drumkitName = QString::fromLocal8Bit(optarg); break; case 'k': //load Drumkit drumkitToLoad = QString::fromLocal8Bit(optarg); break; case 'r': rate = strtol(optarg, NULL, 10); break; case 'b': bits = strtol(optarg, NULL, 10); break; case 'v': showVersionOpt = true; break; case 'V': logLevelOpt = (optarg) ? optarg : "Warning"; break; #ifdef H2CORE_HAVE_JACKSESSION case 'S': sessionId = QString::fromLocal8Bit(optarg); break; #endif case 'h': case '?': showHelpOpt = true; break; } } if ( showVersionOpt ) { cout << get_version() << endl; exit(0); } showInfo(); if ( showHelpOpt ) { showUsage(); exit(0); } // Man your battle stations... this is not a drill. Logger* logger = Logger::bootstrap( Logger::parse_log_level( logLevelOpt ) ); Object::bootstrap( logger, logger->should_log( Logger::Debug ) ); Filesystem::bootstrap( logger ); MidiMap::create_instance(); Preferences::create_instance(); Preferences* preferences = Preferences::get_instance(); // See below for Hydrogen. ___INFOLOG( QString("Using QT version ") + QString( qVersion() ) ); ___INFOLOG( "Using data path: " + Filesystem::sys_data_path() ); #ifdef H2CORE_HAVE_LASH LashClient::create_instance("hydrogen", "Hydrogen", &argc, &argv); LashClient* lashClient = LashClient::get_instance(); #endif if ( ! drumkitName.isEmpty() ){ Drumkit::install( drumkitName ); exit(0); } if (sSelectedDriver == "auto") { preferences->m_sAudioDriver = "Auto"; } else if (sSelectedDriver == "jack") { preferences->m_sAudioDriver = "Jack"; } else if ( sSelectedDriver == "oss" ) { preferences->m_sAudioDriver = "Oss"; } else if ( sSelectedDriver == "alsa" ) { preferences->m_sAudioDriver = "Alsa"; } else if (sSelectedDriver == "CoreAudio") { preferences->m_sAudioDriver = "CoreAudio"; } else if (sSelectedDriver == "PulseAudio") { preferences->m_sAudioDriver = "PulseAudio"; } #ifdef H2CORE_HAVE_LASH if ( preferences->useLash() && lashClient->isConnected() ) { lash_event_t* lash_event = lashClient->getNextEvent(); if (lash_event && lash_event_get_type(lash_event) == LASH_Restore_File) { // notify client that this project was not a new one lashClient->setNewProject(false); songFilename = ""; songFilename.append( QString::fromLocal8Bit(lash_event_get_string(lash_event)) ); songFilename.append("/hydrogen.h2song"); //Logger::get_instance()->log("[LASH] Restore file: " + songFilename); lash_event_destroy(lash_event); } else if (lash_event) { //Logger::get_instance()->log("[LASH] ERROR: Instead of restore file got event: " + lash_event_get_type(lash_event)); lash_event_destroy(lash_event); } } #endif #ifdef H2CORE_HAVE_JACKSESSION if (!sessionId.isEmpty()) { preferences->setJackSessionUUID ( sessionId ); /* imo, jack sessions use jack as default audio driver. * hydrogen remember last used audiodriver. * here we make it save that hydrogen start in a jacksession case * every time with jack as audio driver */ preferences->m_sAudioDriver = "Jack"; } /* the use of applicationFilePath() make it * possible to use different executables. * for example if you start hydrogen from a local * build directory. */ // QString path = pQApp->applicationFilePath(); // preferences->setJackSessionApplicationPath ( path ); #endif Hydrogen::create_instance(); Hydrogen *pHydrogen = Hydrogen::get_instance(); Song *pSong = NULL; Playlist *pPlaylist = NULL; // Load playlist if ( ! playlistFilename.isEmpty() ) { pPlaylist = Playlist::load ( playlistFilename ); if ( ! pPlaylist ) { ___ERRORLOG( "Error loading the playlist" ); return 0; } /* Load first song */ preferences->setLastPlaylistFilename( playlistFilename ); pPlaylist->loadSong( 0 ); pSong = pHydrogen->getSong(); show_playlist ( pHydrogen, pPlaylist->getActiveSongNumber() ); } // Load song - if wasn't already loaded with playlist if ( ! pSong ) { if ( !songFilename.isEmpty() ) { pSong = Song::load( songFilename ); } else { /* Try load last song */ bool restoreLastSong = preferences->isRestoreLastSongEnabled(); QString filename = preferences->getLastSongFilename(); if ( restoreLastSong && ( !filename.isEmpty() )) pSong = Song::load( filename ); } /* Still not loaded */ if (! pSong) { ___INFOLOG("Starting with empty song"); pSong = Song::get_empty_song(); pSong->set_filename( "" ); } pHydrogen->setSong( pSong ); preferences->setLastSongFilename( songFilename ); } if ( ! drumkitToLoad.isEmpty() ){ Drumkit* drumkitInfo = Drumkit::load_by_name( drumkitToLoad, true ); if ( drumkitInfo ) { pHydrogen->loadDrumkit( drumkitInfo ); } else { ___ERRORLOG ( "Error loading the drumkit" ); } } AudioEngine* AudioEngine = AudioEngine::get_instance(); Sampler* sampler = AudioEngine->get_sampler(); switch ( interpolation ) { case 1: sampler->setInterpolateMode( Sampler::COSINE ); break; case 2: sampler->setInterpolateMode( Sampler::THIRD ); break; case 3: sampler->setInterpolateMode( Sampler::CUBIC ); break; case 4: sampler->setInterpolateMode( Sampler::HERMITE ); break; case 0: default: sampler->setInterpolateMode( Sampler::LINEAR ); } EventQueue *pQueue = EventQueue::get_instance(); signal(SIGINT, signal_handler); bool ExportMode = false; if ( ! outFilename.isEmpty() ) { pHydrogen->startExportSong ( outFilename, rate, bits ); cout << "Export Progress ... "; bool ExportMode = true; } // Interactive mode while ( ! quit ) { /* FIXME: Someday here will be The Real CLI ;-) */ Event event = pQueue->pop_event(); // if ( event.type > 0) cout << "EVENT TYPE: " << event.type << endl; /* Event handler */ switch ( event.type ) { case EVENT_PROGRESS: /* event used only in export mode */ if ( ! ExportMode ) break; if ( event.value < 100 ) { cout << "\rExport Progress ... " << event.value << "%"; } else { cout << "\rExport Progress ... DONE" << endl; quit = true; } break; case EVENT_PLAYLIST_LOADSONG: /* Load new song on MIDI event */ if ( pPlaylist->loadSong ( event.value ) ) { pSong = pHydrogen->getSong(); show_playlist ( pHydrogen, pPlaylist->getActiveSongNumber() ); } break; case EVENT_NONE: /* Sleep if there is no more events */ Sleeper::msleep ( 100 ); break; } } if ( pHydrogen->getState() == STATE_PLAYING ) pHydrogen->sequencer_stop(); delete pSong; delete pPlaylist; delete pQueue; delete pHydrogen; delete preferences; delete AudioEngine; delete MidiMap::get_instance(); delete MidiActionManager::get_instance(); ___INFOLOG( "Quitting..." ); delete Logger::get_instance(); int nObj = Object::objects_count(); if (nObj != 0) { cerr << "\n\n\n " << nObj << " alive objects\n\n" << endl << endl; Object::write_objects_map_to_cerr(); } } catch ( const H2Exception& ex ) { cerr << "[main] Exception: " << ex.what() << endl; } catch (...) { cerr << "[main] Unknown exception X-(" << endl; } return 0; }
void SongEditorPanel::automationPathChanged() { Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); song->set_is_modified(true); }
/// 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; }
bool Sampler::processPlaybackTrack(int nBufferSize) { Hydrogen* pEngine = Hydrogen::get_instance(); AudioOutput* pAudioOutput = Hydrogen::get_instance()->getAudioOutput(); Song* pSong = pEngine->getSong(); if( !pSong->get_playback_track_enabled() || pEngine->getState() != STATE_PLAYING || pSong->get_mode() != Song::SONG_MODE) { return false; } InstrumentComponent *pCompo = __playback_instrument->get_components()->front(); Sample *pSample = pCompo->get_layer(0)->get_sample(); float fVal_L; float fVal_R; float *pSample_data_L = pSample->get_data_l(); float *pSample_data_R = pSample->get_data_r(); float fInstrPeak_L = __playback_instrument->get_peak_l(); // this value will be reset to 0 by the mixer.. float fInstrPeak_R = __playback_instrument->get_peak_r(); // this value will be reset to 0 by the mixer.. assert(pSample); int nAvail_bytes = 0; int nInitialBufferPos = 0; if(pSample->get_sample_rate() == pAudioOutput->getSampleRate()){ //No resampling __playBackSamplePosition = pAudioOutput->m_transport.m_nFrames; nAvail_bytes = pSample->get_frames() - ( int )__playBackSamplePosition; if ( nAvail_bytes > nBufferSize ) { nAvail_bytes = nBufferSize; } int nInitialSamplePos = ( int ) __playBackSamplePosition; int nSamplePos = nInitialSamplePos; int nTimes = nInitialBufferPos + nAvail_bytes; if(__playBackSamplePosition > pSample->get_frames()){ //playback track has ended.. return true; } for ( int nBufferPos = nInitialBufferPos; nBufferPos < nTimes; ++nBufferPos ) { fVal_L = pSample_data_L[ nSamplePos ]; fVal_R = pSample_data_R[ nSamplePos ]; fVal_L = fVal_L * 1.0f * pSong->get_playback_track_volume(); //costr fVal_R = fVal_R * 1.0f * pSong->get_playback_track_volume(); //cost l //pDrumCompo->set_outs( nBufferPos, fVal_L, fVal_R ); // to main mix if ( fVal_L > fInstrPeak_L ) { fInstrPeak_L = fVal_L; } if ( fVal_R > fInstrPeak_R ) { fInstrPeak_R = fVal_R; } __main_out_L[nBufferPos] += fVal_L; __main_out_R[nBufferPos] += fVal_R; ++nSamplePos; } } else { //Perform resampling double fSamplePos = 0; int nSampleFrames = pSample->get_frames(); float fStep = 1.0594630943593; fStep *= ( float )pSample->get_sample_rate() / pAudioOutput->getSampleRate(); // Adjust for audio driver sample rate if(pAudioOutput->m_transport.m_nFrames == 0){ fSamplePos = 0; } else { fSamplePos = ( (pAudioOutput->m_transport.m_nFrames/nBufferSize) * (nBufferSize * fStep)); } nAvail_bytes = ( int )( ( float )( pSample->get_frames() - fSamplePos ) / fStep ); if ( nAvail_bytes > nBufferSize ) { nAvail_bytes = nBufferSize; } int nTimes = nInitialBufferPos + nAvail_bytes; for ( int nBufferPos = nInitialBufferPos; nBufferPos < nTimes; ++nBufferPos ) { int nSamplePos = ( int ) fSamplePos; double fDiff = fSamplePos - nSamplePos; if ( ( nSamplePos + 1 ) >= nSampleFrames ) { //we reach the last audioframe. //set this last frame to zero do nothin wrong. fVal_L = 0.0; fVal_R = 0.0; } else { // some interpolation methods need 4 frames data. float last_l; float last_r; if ( ( nSamplePos + 2 ) >= nSampleFrames ) { last_l = 0.0; last_r = 0.0; } else { last_l = pSample_data_L[nSamplePos + 2]; last_r = pSample_data_R[nSamplePos + 2]; } switch( __interpolateMode ){ case LINEAR: fVal_L = pSample_data_L[nSamplePos] * (1 - fDiff ) + pSample_data_L[nSamplePos + 1] * fDiff; fVal_R = pSample_data_R[nSamplePos] * (1 - fDiff ) + pSample_data_R[nSamplePos + 1] * fDiff; break; case COSINE: fVal_L = cosine_Interpolate( pSample_data_L[nSamplePos], pSample_data_L[nSamplePos + 1], fDiff); fVal_R = cosine_Interpolate( pSample_data_R[nSamplePos], pSample_data_R[nSamplePos + 1], fDiff); break; case THIRD: fVal_L = third_Interpolate( pSample_data_L[ nSamplePos -1], pSample_data_L[nSamplePos], pSample_data_L[nSamplePos + 1], last_l, fDiff); fVal_R = third_Interpolate( pSample_data_R[ nSamplePos -1], pSample_data_R[nSamplePos], pSample_data_R[nSamplePos + 1], last_r, fDiff); break; case CUBIC: fVal_L = cubic_Interpolate( pSample_data_L[ nSamplePos -1], pSample_data_L[nSamplePos], pSample_data_L[nSamplePos + 1], last_l, fDiff); fVal_R = cubic_Interpolate( pSample_data_R[ nSamplePos -1], pSample_data_R[nSamplePos], pSample_data_R[nSamplePos + 1], last_r, fDiff); break; case HERMITE: fVal_L = hermite_Interpolate( pSample_data_L[ nSamplePos -1], pSample_data_L[nSamplePos], pSample_data_L[nSamplePos + 1], last_l, fDiff); fVal_R = hermite_Interpolate( pSample_data_R[ nSamplePos -1], pSample_data_R[nSamplePos], pSample_data_R[nSamplePos + 1], last_r, fDiff); break; } } if ( fVal_L > fInstrPeak_L ) { fInstrPeak_L = fVal_L; } if ( fVal_R > fInstrPeak_R ) { fInstrPeak_R = fVal_R; } __main_out_L[nBufferPos] += fVal_L; __main_out_R[nBufferPos] += fVal_R; fSamplePos += fStep; } //for } __playback_instrument->set_peak_l( fInstrPeak_L ); __playback_instrument->set_peak_r( fInstrPeak_R ); return true; }
void Mixer::updateMixer() { Preferences *pPref = Preferences::get_instance(); bool bShowPeaks = pPref->showInstrumentPeaks(); Hydrogen *pEngine = Hydrogen::get_instance(); Song *pSong = pEngine->getSong(); InstrumentList *pInstrList = pSong->get_instrument_list(); std::vector<DrumkitComponent*>* compoList = pSong->get_components(); uint nSelectedInstr = pEngine->getSelectedInstrumentNumber(); float fallOff = pPref->getMixerFalloffSpeed(); uint nMuteClicked = 0; int nInstruments = pInstrList->size(); int nCompo = compoList->size(); for ( unsigned nInstr = 0; nInstr < MAX_INSTRUMENTS; ++nInstr ) { if ( nInstr >= nInstruments ) { // unused instrument! let's hide and destroy the mixerline! if ( m_pMixerLine[ nInstr ] ) { delete m_pMixerLine[ nInstr ]; m_pMixerLine[ nInstr ] = NULL; int newWidth = MIXER_STRIP_WIDTH * ( nInstruments + nCompo ); if ( m_pFaderPanel->width() != newWidth ) { m_pFaderPanel->resize( newWidth, height() ); } } continue; } else { if ( m_pMixerLine[ nInstr ] == NULL ) { // the mixerline doesn't exists..I'll create a new one! m_pMixerLine[ nInstr ] = createMixerLine( nInstr ); m_pFaderHBox->insertWidget( nInstr, m_pMixerLine[ nInstr ] ); int newWidth = MIXER_STRIP_WIDTH * ( nInstruments + nCompo ); if ( m_pFaderPanel->width() != newWidth ) { m_pFaderPanel->resize( newWidth, height() ); } } MixerLine *pLine = m_pMixerLine[ nInstr ]; Instrument *pInstr = pInstrList->get( nInstr ); assert( pInstr ); float fNewPeak_L = pInstr->get_peak_l(); pInstr->set_peak_l( 0.0f ); // reset instrument peak float fNewPeak_R = pInstr->get_peak_r(); pInstr->set_peak_r( 0.0f ); // reset instrument peak float fNewVolume = pInstr->get_volume(); bool bMuted = pInstr->is_muted(); QString sName = pInstr->get_name(); float fPan_L = pInstr->get_pan_l(); float fPan_R = pInstr->get_pan_r(); // fader float fOldPeak_L = pLine->getPeak_L(); float fOldPeak_R = pLine->getPeak_R(); if (!bShowPeaks) { fNewPeak_L = 0.0f; fNewPeak_R = 0.0f; } if ( fNewPeak_L >= fOldPeak_L) { // LEFT peak pLine->setPeak_L( fNewPeak_L ); } else { pLine->setPeak_L( fOldPeak_L / fallOff ); } if ( fNewPeak_R >= fOldPeak_R) { // Right peak pLine->setPeak_R( fNewPeak_R ); } else { pLine->setPeak_R( fOldPeak_R / fallOff ); } // fader position pLine->setVolume( fNewVolume ); // mute if ( bMuted ) { nMuteClicked++; } pLine->setMuteClicked( bMuted ); // instr name pLine->setName( sName ); // pan float fPanValue = 0.0; if (fPan_R == 1.0) { fPanValue = 1.0 - (fPan_L / 2.0); } else { fPanValue = fPan_R / 2.0; } pLine->setPan( fPanValue ); // activity if ( pLine->getActivity() > 0 ) { pLine->setActivity( m_pMixerLine[ nInstr ]->getActivity() - 30 ); pLine->setPlayClicked( true ); } else { pLine->setPlayClicked( false ); } for (uint nFX = 0; nFX < MAX_FX; nFX++) { pLine->setFXLevel( nFX, pInstr->get_fx_level( nFX ) ); } pLine->setSelected( nInstr == nSelectedInstr ); pLine->updateMixerLine(); } } for (std::vector<DrumkitComponent*>::iterator it = compoList->begin() ; it != compoList->end(); ++it) { DrumkitComponent* p_compo = *it; if( m_pComponentMixerLine.find(p_compo->get_id()) == m_pComponentMixerLine.end() ) { // the mixerline doesn't exists..I'll create a new one! m_pComponentMixerLine[ p_compo->get_id() ] = createComponentMixerLine( p_compo->get_id() ); m_pFaderHBox->addWidget( m_pComponentMixerLine[ p_compo->get_id() ] ); int newWidth = MIXER_STRIP_WIDTH * ( nInstruments + nCompo ); if ( m_pFaderPanel->width() != newWidth ) { m_pFaderPanel->resize( newWidth, height() ); } } ComponentMixerLine *pLine = m_pComponentMixerLine[ p_compo->get_id() ]; float fNewPeak_L = p_compo->get_peak_l(); p_compo->set_peak_l( 0.0f ); // reset instrument peak float fNewPeak_R = p_compo->get_peak_r(); p_compo->set_peak_r( 0.0f ); // reset instrument peak float fNewVolume = p_compo->get_volume(); bool bMuted = p_compo->is_muted(); QString sName = p_compo->get_name(); float fOldPeak_L = pLine->getPeak_L(); float fOldPeak_R = pLine->getPeak_R(); if (!bShowPeaks) { fNewPeak_L = 0.0f; fNewPeak_R = 0.0f; } if ( fNewPeak_L >= fOldPeak_L) { // LEFT peak pLine->setPeak_L( fNewPeak_L ); } else { pLine->setPeak_L( fOldPeak_L / fallOff ); } if ( fNewPeak_R >= fOldPeak_R) { // Right peak pLine->setPeak_R( fNewPeak_R ); } else { pLine->setPeak_R( fOldPeak_R / fallOff ); } // fader position pLine->setVolume( fNewVolume ); // mute if ( bMuted ) { nMuteClicked++; } pLine->setMuteClicked( bMuted ); // instr name pLine->setName( sName ); pLine->updateMixerLine(); } if( compoList->size() < m_pComponentMixerLine.size() ) { std::vector<int>* p_ids_to_delete = new std::vector<int>(); for (std::map<int, ComponentMixerLine*>::iterator it=m_pComponentMixerLine.begin(); it!=m_pComponentMixerLine.end(); ++it) { bool p_foundExistingRelatedComponent = false; for ( std::vector<DrumkitComponent*>::iterator it2 = compoList->begin() ; it2 != compoList->end(); ++it2 ) { DrumkitComponent* p_compo = *it2; if( p_compo->get_id() == it->first ) { p_foundExistingRelatedComponent = true; break; } } if( !p_foundExistingRelatedComponent ) p_ids_to_delete->push_back( it->first ) ; } for ( std::vector<int>::iterator it = p_ids_to_delete->begin() ; it != p_ids_to_delete->end(); ++it ) { int p_compoID = *it; delete m_pComponentMixerLine[p_compoID]; m_pComponentMixerLine.erase( p_compoID ); int newWidth = MIXER_STRIP_WIDTH * ( nInstruments + nCompo ); if ( m_pFaderPanel->width() != newWidth ) { m_pFaderPanel->resize( newWidth, height() ); } } } if (nMuteClicked == nInstruments - 1) { // find the not muted button for (uint i = 0; i < nInstruments; i++) { Instrument *instr = pInstrList->get(i); if (instr->is_muted() == false) { m_pMixerLine[i]->setSoloClicked(true); break; } } } else { for (uint i = 0; i < nInstruments; i++) { m_pMixerLine[i]->setSoloClicked(false); } } // update MasterPeak float oldPeak_L = m_pMasterLine->getPeak_L(); float newPeak_L = pEngine->getMasterPeak_L(); pEngine->setMasterPeak_L(0.0); float oldPeak_R = m_pMasterLine->getPeak_R(); float newPeak_R = pEngine->getMasterPeak_R(); pEngine->setMasterPeak_R(0.0); if (!bShowPeaks) { newPeak_L = 0.0; newPeak_R = 0.0; } if (newPeak_L >= oldPeak_L) { m_pMasterLine->setPeak_L( newPeak_L ); } else { m_pMasterLine->setPeak_L( oldPeak_L / fallOff ); } if (newPeak_R >= oldPeak_R) { m_pMasterLine->setPeak_R(newPeak_R); } else { m_pMasterLine->setPeak_R( oldPeak_R / fallOff ); } // set master fader position float newVolume = pSong->get_volume(); float oldVolume = m_pMasterLine->getVolume(); if (oldVolume != newVolume) { m_pMasterLine->setVolume(newVolume); } m_pMasterLine->updateMixerLine(); #ifdef H2CORE_HAVE_LADSPA // LADSPA for (uint nFX = 0; nFX < MAX_FX; nFX++) { LadspaFX *pFX = Effects::get_instance()->getLadspaFX( nFX ); if ( pFX ) { m_pLadspaFXLine[nFX]->setName( pFX->getPluginName() ); float fNewPeak_L = 0.0; float fNewPeak_R = 0.0; float fOldPeak_L = 0.0; float fOldPeak_R = 0.0; m_pLadspaFXLine[nFX]->getPeaks( &fOldPeak_L, &fOldPeak_R ); if (fNewPeak_L < fOldPeak_L) fNewPeak_L = fOldPeak_L / fallOff; if (fNewPeak_R < fOldPeak_R) fNewPeak_R = fOldPeak_R / fallOff; m_pLadspaFXLine[nFX]->setPeaks( fNewPeak_L, fNewPeak_R ); m_pLadspaFXLine[nFX]->setFxActive( pFX->isEnabled() ); m_pLadspaFXLine[nFX]->setVolume( pFX->getVolume() ); } else { m_pLadspaFXLine[nFX]->setName( "No plugin" ); m_pLadspaFXLine[nFX]->setFxActive( false ); m_pLadspaFXLine[nFX]->setVolume( 0.0 ); } } // ~LADSPA #endif }
void InstrumentEditor::loadLayer() { static QString lastUsedDir = QDir::homePath(); Hydrogen *engine = Hydrogen::get_instance(); AudioFileBrowser *fb = new AudioFileBrowser( NULL ); QStringList filename; filename << "false" << "false" << ""; if (fb->exec() == QDialog::Accepted) { filename = fb->selectedFile(); } delete fb; if ( filename[2].isEmpty() ) return; bool fnc = false; if ( filename[0] == "true" ){ fnc = true; } //use auto velocity if we want to work with multiple filenames if ( filename.size() > 3) filename[1] = "true"; int selectedLayer = m_nSelectedLayer; int firstSelection = selectedLayer; if (filename.size() > 2) { for(int i=2;i < filename.size();++i) { selectedLayer = m_nSelectedLayer + i - 2; if( ( i-2 >= MAX_LAYERS ) || ( selectedLayer + 1 > MAX_LAYERS ) ) break; Sample *newSample = Sample::load( filename[i] ); H2Core::Instrument *pInstr = NULL; AudioEngine::get_instance()->lock( RIGHT_HERE ); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); pInstr = instrList->get( engine->getSelectedInstrumentNumber() ); /* if we're using multiple layers, we start inserting the first layer at m_nSelectedLayer and the next layer at m_nSelectedLayer+1 */ H2Core::InstrumentLayer *pLayer = pInstr->get_layer( selectedLayer ); if (pLayer != NULL) { // delete old sample Sample *oldSample = pLayer->get_sample(); delete oldSample; // insert new sample from newInstrument pLayer->set_sample( newSample ); } else { pLayer = new H2Core::InstrumentLayer(newSample); pInstr->set_layer( pLayer, selectedLayer ); } if ( fnc ){ QString newFilename = filename[i].section( '/', -1 ); newFilename.replace( "." + newFilename.section( '.', -1 ), ""); m_pInstrument->set_name( newFilename ); } //set automatic velocity if ( filename[1] == "true" ){ setAutoVelocity(); } //pInstr->set_drumkit_name( "" ); // external sample, no drumkit info AudioEngine::get_instance()->unlock(); } } selectedInstrumentChangedEvent(); // update all selectLayer( firstSelection ); m_pLayerPreview->updateAll(); }
void InstrumentEditor::compoChangeAddDelete(QAction* pAction) { QString sSelectedAction = pAction->text(); Hydrogen * pEngine = Hydrogen::get_instance(); if( sSelectedAction.compare("add") == 0 ) { if ( m_pInstrument ) { bool bIsOkPressed; QString sNewName = QInputDialog::getText( this, "Hydrogen", trUtf8( "Component name" ), QLineEdit::Normal, "New Component", &bIsOkPressed ); if ( bIsOkPressed ) { DrumkitComponent* pDrumkitComponent = new DrumkitComponent( findFreeDrumkitComponentId(), sNewName ); pEngine->getSong()->get_components()->push_back( pDrumkitComponent ); //InstrumentComponent* instrument_component = new InstrumentComponent( dm_component->get_id() ); //instrument_component->set_gain( 1.0f ); //m_pInstrument->get_components()->push_back( instrument_component ); m_nSelectedComponent = pDrumkitComponent->get_id(); m_pLayerPreview->set_selected_component( pDrumkitComponent->get_id() ); selectedInstrumentChangedEvent(); // this will force an update... EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 ); #ifdef H2CORE_HAVE_JACK pEngine->renameJackPorts(pEngine->getSong()); #endif } else { // user entered nothing or pressed Cancel } } } else if( sSelectedAction.compare("delete") == 0 ) { std::vector<DrumkitComponent*>* pDrumkitComponents = pEngine->getSong()->get_components(); if(pDrumkitComponents->size() == 1){ return; } DrumkitComponent* pDrumkitComponent = pEngine->getSong()->get_component( m_nSelectedComponent ); InstrumentList* pInstruments = pEngine->getSong()->get_instrument_list(); for ( int n = ( int )pInstruments->size() - 1; n >= 0; n-- ) { Instrument* pInstrument = pInstruments->get( n ); for( int o = 0 ; o < pInstrument->get_components()->size() ; o++ ) { InstrumentComponent* pInstrumentComponent = pInstrument->get_components()->at( o ); if( pInstrumentComponent->get_drumkit_componentID() == pDrumkitComponent->get_id() ) { for( int nLayer = 0; nLayer < MAX_LAYERS; nLayer++ ) { InstrumentLayer* pLayer = pInstrumentComponent->get_layer( nLayer ); if( pLayer ) delete pLayer; } pInstrument->get_components()->erase( pInstrument->get_components()->begin() + o );; break; } } } for ( int n = 0 ; n < pDrumkitComponents->size() ; n++ ) { DrumkitComponent* pTmpDrumkitComponent = pDrumkitComponents->at( n ); if( pTmpDrumkitComponent->get_id() == pDrumkitComponent->get_id() ) { pDrumkitComponents->erase( pDrumkitComponents->begin() + n ); break; } } m_nSelectedComponent = pDrumkitComponents->front()->get_id(); selectedInstrumentChangedEvent(); // this will force an update... EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 ); } else if( sSelectedAction.compare("rename") == 0 ) { labelCompoClicked( NULL ); } else { m_nSelectedComponent = -1; std::vector<DrumkitComponent*>* pDrumkitComponents = pEngine->getSong()->get_components(); for (std::vector<DrumkitComponent*>::iterator it = pDrumkitComponents->begin() ; it != pDrumkitComponents->end(); ++it) { DrumkitComponent* pDrumkitComponent = *it; if( pDrumkitComponent->get_name().compare( sSelectedAction ) == 0) { m_nSelectedComponent = pDrumkitComponent->get_id(); m_pCompoNameLbl->setText( pDrumkitComponent->get_name() ); break; } } if( m_pInstrument && !m_pInstrument->get_component(m_nSelectedComponent)) { INFOLOG("Component needs to be added"); InstrumentComponent* pInstrComponent = new InstrumentComponent( m_nSelectedComponent ); pInstrComponent->set_gain( 1.0f ); m_pInstrument->get_components()->push_back( pInstrComponent ); #ifdef H2CORE_HAVE_JACK pEngine->renameJackPorts(pEngine->getSong()); #endif } m_pLayerPreview->set_selected_component(m_nSelectedComponent); selectedInstrumentChangedEvent(); // this will force an update... EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 ); } }
/// 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; }
void* diskWriterDriver_thread( void* param ) { Object* __object = ( Object* )param; DiskWriterDriver *pDriver = ( DiskWriterDriver* )param; EventQueue::get_instance()->push_event( EVENT_PROGRESS, 0 ); pDriver->setBpm( Hydrogen::get_instance()->getSong()->__bpm ); pDriver->audioEngine_process_checkBPMChanged(); __INFOLOG( "DiskWriterDriver thread start" ); // always rolling, no user interaction pDriver->m_transport.m_status = TransportInfo::ROLLING; SF_INFO soundInfo; soundInfo.samplerate = pDriver->m_nSampleRate; // soundInfo.frames = -1;//getNFrames(); ///\todo: da terminare soundInfo.channels = 2; //default format int sfformat = 0x010000; //wav format (default) int bits = 0x0002; //16 bit PCM (default) //sf_format switch if( pDriver->m_sFilename.endsWith(".aiff") || pDriver->m_sFilename.endsWith(".AIFF") ){ sfformat = 0x020000; //Apple/SGI AIFF format (big endian) } if( pDriver->m_sFilename.endsWith(".flac") || pDriver->m_sFilename.endsWith(".FLAC") ){ sfformat = 0x170000; //FLAC lossless file format } if( ( pDriver->m_nSampleDepth == 8 ) && ( pDriver->m_sFilename.endsWith(".aiff") || pDriver->m_sFilename.endsWith(".AIFF") ) ){ bits = 0x0001; //Signed 8 bit data works with aiff } if( ( pDriver->m_nSampleDepth == 8 ) && ( pDriver->m_sFilename.endsWith(".wav") || pDriver->m_sFilename.endsWith(".WAV") ) ){ bits = 0x0005; //Unsigned 8 bit data needed for Microsoft WAV format } if( pDriver->m_nSampleDepth == 16 ){ bits = 0x0002; //Signed 16 bit data } if( pDriver->m_nSampleDepth == 24 ){ bits = 0x0003; //Signed 24 bit data } if( pDriver->m_nSampleDepth == 32 ){ bits = 0x0004; ////Signed 32 bit data } soundInfo.format = sfformat|bits; // #ifdef HAVE_OGGVORBIS //ogg vorbis option if( pDriver->m_sFilename.endsWith( ".ogg" ) | pDriver->m_sFilename.endsWith( ".OGG" ) ) soundInfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS; // #endif ///formats // SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian). */ // SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ // SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ // SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ // SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ // SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ // SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ // SF_FORMAT_VOC = 0x080000, /* VOC files. */ // SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ // SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ // SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ // SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ // SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ // SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ // SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ // SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ // SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ // SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ // SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ // SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ // SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ // SF_FORMAT_OGG ///bits // SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ // SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ // SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ // SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ ///used for ogg // SF_FORMAT_VORBIS if ( !sf_format_check( &soundInfo ) ) { __ERRORLOG( "Error in soundInfo" ); return 0; } SNDFILE* m_file = sf_open( pDriver->m_sFilename.toLocal8Bit(), SFM_WRITE, &soundInfo ); float *pData = new float[ pDriver->m_nBufferSize * 2 ]; // always stereo float *pData_L = pDriver->m_pOut_L; float *pData_R = pDriver->m_pOut_R; Hydrogen* engine = Hydrogen::get_instance(); std::vector<PatternList*> *pPatternColumns = Hydrogen::get_instance()->getSong()->get_pattern_group_vector(); int nColumns = pPatternColumns->size(); int nPatternSize; int validBpm = engine->getSong()->__bpm; float oldBPM = 0; float ticksize = 0; for ( int patternposition = 0; patternposition < nColumns; ++patternposition ) { PatternList *pColumn = ( *pPatternColumns )[ patternposition ]; if ( pColumn->size() != 0 ) { nPatternSize = pColumn->get( 0 )->get_length(); } else { nPatternSize = MAX_NOTES; } ticksize = pDriver->m_nSampleRate * 60.0 / engine->getSong()->__bpm / engine->getSong()->__resolution; // check pattern bpm if timeline bpm is in use Timeline* pTimeline = engine->getTimeline(); if(Preferences::get_instance()->getUseTimelineBpm() ){ if( pTimeline->m_timelinevector.size() >= 1 ){ for ( int t = 0; t < pTimeline->m_timelinevector.size(); t++){ if(pTimeline->m_timelinevector[t].m_htimelinebeat == patternposition && pTimeline->m_timelinevector[t].m_htimelinebpm != validBpm){ validBpm = pTimeline->m_timelinevector[t].m_htimelinebpm; } } } pDriver->setBpm(validBpm); ticksize = pDriver->m_nSampleRate * 60.0 / validBpm / Hydrogen::get_instance()->getSong()->__resolution; pDriver->audioEngine_process_checkBPMChanged(); engine->setPatternPos(patternposition); // delay needed time to calculate all rubberband samples if( Preferences::get_instance()->getRubberBandBatchMode() && validBpm != oldBPM ){ EventQueue::get_instance()->push_event( EVENT_RECALCULATERUBBERBAND, -1); int sleepTime = Preferences::get_instance()->getRubberBandCalcTime()+1; while ((sleepTime = sleep(sleepTime)) > 0); } oldBPM = validBpm; } else { ticksize = pDriver->m_nSampleRate * 60.0 / Hydrogen::get_instance()->getSong()->__bpm / Hydrogen::get_instance()->getSong()->__resolution; //pDriver->m_transport.m_nTickSize = ticksize; } //here we have the pattern length in frames dependent from bpm and samplerate unsigned patternLengthInFrames = ticksize * nPatternSize; unsigned frameNumber = 0; int lastRun = 0; while ( frameNumber < patternLengthInFrames ) { int usedBuffer = pDriver->m_nBufferSize; //this will calculate the the size from -last- (end of pattern) used frame buffer, //which is mostly smaller than pDriver->m_nBufferSize if( patternLengthInFrames - frameNumber < pDriver->m_nBufferSize ){ lastRun = patternLengthInFrames - frameNumber; usedBuffer = lastRun; }; frameNumber += usedBuffer; int ret = pDriver->m_processCallback( usedBuffer, NULL ); for ( unsigned i = 0; i < usedBuffer; i++ ) { if(pData_L[i] > 1){ pData[i * 2] = 1; } else if(pData_L[i] < -1){ pData[i * 2] = -1; }else { pData[i * 2] = pData_L[i]; } if(pData_R[i] > 1){ pData[i * 2 + 1] = 1; } else if(pData_R[i] < -1){ pData[i * 2 + 1] = -1; }else { pData[i * 2 + 1] = pData_R[i]; } } int res = sf_writef_float( m_file, pData, usedBuffer ); if ( res != ( int )usedBuffer ) { __ERRORLOG( "Error during sf_write_float" ); } } // this progress bar methode is not exact but ok enough to give users a usable visible progress feedback float fPercent = ( float )(patternposition +1) / ( float )nColumns * 100.0; EventQueue::get_instance()->push_event( EVENT_PROGRESS, ( int )fPercent ); } delete[] pData; pData = NULL; sf_close( m_file ); __INFOLOG( "DiskWriterDriver thread end" ); pthread_exit( NULL ); return NULL; }
/** * The handleAction method is the heard of the MidiActionManager class. * It executes the operations that are needed to carry the desired action. */ bool MidiActionManager::handleAction( MidiAction * pAction ){ Hydrogen *pEngine = Hydrogen::get_instance(); /* return false if action is null (for example if no Action exists for an event) */ if( pAction == NULL ) return false; QString sActionString = pAction->getType(); if( sActionString == "PLAY" ) { int nState = pEngine->getState(); if ( nState == STATE_READY ){ pEngine->sequencer_play(); } return true; } if( sActionString == "PLAY/STOP_TOGGLE" || sActionString == "PLAY/PAUSE_TOGGLE" ) { int nState = pEngine->getState(); switch ( nState ) { case STATE_READY: pEngine->sequencer_play(); break; case STATE_PLAYING: if( sActionString == "PLAY/STOP_TOGGLE" ) pEngine->setPatternPos( 0 ); pEngine->sequencer_stop(); pEngine->setTimelineBpm(); break; default: ERRORLOG( "[Hydrogen::ActionManager(PLAY): Unhandled case" ); } return true; } if( sActionString == "PAUSE" ) { pEngine->sequencer_stop(); return true; } if( sActionString == "STOP" ) { pEngine->sequencer_stop(); pEngine->setPatternPos( 0 ); pEngine->setTimelineBpm(); return true; } if( sActionString == "MUTE" ){ //mutes the master, not a single strip pEngine->getSong()->__is_muted = true; return true; } if( sActionString == "UNMUTE" ){ pEngine->getSong()->__is_muted = false; return true; } if( sActionString == "MUTE_TOGGLE" ){ pEngine->getSong()->__is_muted = !Hydrogen::get_instance()->getSong()->__is_muted; return true; } if( sActionString == "BEATCOUNTER" ){ pEngine->handleBeatCounter(); return true; } if( sActionString == "TAP_TEMPO" ){ pEngine->onTapTempoAccelEvent(); return true; } if( sActionString == "SELECT_NEXT_PATTERN" ){ bool ok; int row = pAction->getParameter1().toInt(&ok,10); if( row> pEngine->getSong()->get_pattern_list()->size() -1 ) return false; if(Preferences::get_instance()->patternModePlaysSelected()) pEngine->setSelectedPatternNumber( row ); else pEngine->sequencer_setNextPattern( row, false, true ); return true; } if( sActionString == "SELECT_NEXT_PATTERN_RELATIVE" ){ bool ok; if(!Preferences::get_instance()->patternModePlaysSelected()) { return true; } int row = pEngine->getSelectedPatternNumber() + pAction->getParameter1().toInt(&ok,10); if( row> pEngine->getSong()->get_pattern_list()->size() -1 ) { return false; } pEngine->setSelectedPatternNumber( row ); return true; } if( sActionString == "SELECT_PREV_PATTERN_RELATIVE" ){ bool ok; if(!Preferences::get_instance()->patternModePlaysSelected()) return true; int row = pEngine->getSelectedPatternNumber() - pAction->getParameter1().toInt(&ok,10); if( row < 0 ) return false; pEngine->setSelectedPatternNumber( row ); return true; } if( sActionString == "SELECT_NEXT_PATTERN_CC_ABSOLUT" ){ bool ok; int row = pAction->getParameter2().toInt(&ok,10); if( row> pEngine->getSong()->get_pattern_list()->size() -1 ) return false; if(Preferences::get_instance()->patternModePlaysSelected()) pEngine->setSelectedPatternNumber( row ); else return true;// only usefully in normal pattern mode return true; } if( sActionString == "SELECT_NEXT_PATTERN_PROMPTLY" ){// obsolete, use SELECT_NEXT_PATTERN_CC_ABSOLUT instead bool ok; int row = pAction->getParameter2().toInt(&ok,10); pEngine->setSelectedPatternNumberWithoutGuiEvent( row ); return true; } if( sActionString == "SELECT_AND_PLAY_PATTERN"){ bool ok; int row = pAction->getParameter1().toInt(&ok,10); pEngine->setSelectedPatternNumber( row ); pEngine->sequencer_setNextPattern( row, false, true ); int nState = pEngine->getState(); if ( nState == STATE_READY ){ pEngine->sequencer_play(); } return true; } if( sActionString == "SELECT_INSTRUMENT" ){ bool ok; int instrument_number = pAction->getParameter2().toInt(&ok,10) ; if ( pEngine->getSong()->get_instrument_list()->size() < instrument_number ) instrument_number = pEngine->getSong()->get_instrument_list()->size() -1; pEngine->setSelectedInstrumentNumber( instrument_number ); return true; } if( sActionString == "EFFECT1_LEVEL_ABSOLUTE" ){ bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int fx_param = pAction->getParameter2().toInt(&ok,10); setAbsoluteFXLevel( nLine, 0 , fx_param ); } if( sActionString == "EFFECT2_LEVEL_ABSOLUTE" ){ bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int fx_param = pAction->getParameter2().toInt(&ok,10); setAbsoluteFXLevel( nLine, 1 , fx_param ); } if( sActionString == "EFFECT3_LEVEL_ABSOLUTE" ){ bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int fx_param = pAction->getParameter2().toInt(&ok,10); setAbsoluteFXLevel( nLine, 2 , fx_param ); } if( sActionString == "EFFECT4_LEVEL_ABSOLUTE" ){ bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int fx_param = pAction->getParameter2().toInt(&ok,10); setAbsoluteFXLevel( nLine, 3 , fx_param ); } if( sActionString == "MASTER_VOLUME_RELATIVE" ){ //increments/decrements the volume of the whole song bool ok; int vol_param = pAction->getParameter2().toInt(&ok,10); Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); if( vol_param != 0 ){ if ( vol_param == 1 && song->get_volume() < 1.5 ){ song->set_volume( song->get_volume() + 0.05 ); } else { if( song->get_volume() >= 0.0 ){ song->set_volume( song->get_volume() - 0.05 ); } } } else { song->set_volume( 0 ); } } if( sActionString == "MASTER_VOLUME_ABSOLUTE" ){ //sets the volume of a master output to a given level (percentage) bool ok; int vol_param = pAction->getParameter2().toInt(&ok,10); Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); if( vol_param != 0 ){ song->set_volume( 1.5* ( (float) (vol_param / 127.0 ) )); } else { song->set_volume( 0 ); } } if( sActionString == "STRIP_VOLUME_RELATIVE" ){ //increments/decrements the volume of one mixer strip bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int vol_param = pAction->getParameter2().toInt(&ok,10); Hydrogen::get_instance()->setSelectedInstrumentNumber( nLine ); Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); Instrument *instr = instrList->get( nLine ); if ( instr == NULL) return 0; if( vol_param != 0 ){ if ( vol_param == 1 && instr->get_volume() < 1.5 ){ instr->set_volume( instr->get_volume() + 0.1 ); } else { if( instr->get_volume() >= 0.0 ){ instr->set_volume( instr->get_volume() - 0.1 ); } } } else { instr->set_volume( 0 ); } Hydrogen::get_instance()->setSelectedInstrumentNumber(nLine); } if( sActionString == "STRIP_VOLUME_ABSOLUTE" ){ //sets the volume of a mixer strip to a given level (percentage) bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int vol_param = pAction->getParameter2().toInt(&ok,10); Hydrogen::get_instance()->setSelectedInstrumentNumber( nLine ); Hydrogen *engine = Hydrogen::get_instance(); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); Instrument *instr = instrList->get( nLine ); if ( instr == NULL) return 0; if( vol_param != 0 ){ instr->set_volume( 1.5* ( (float) (vol_param / 127.0 ) )); } else { instr->set_volume( 0 ); } Hydrogen::get_instance()->setSelectedInstrumentNumber(nLine); } if( sActionString == "PAN_ABSOLUTE" ){ // sets the absolute panning of a given mixer channel bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int pan_param = pAction->getParameter2().toInt(&ok,10); float pan_L; float pan_R; Hydrogen *engine = Hydrogen::get_instance(); engine->setSelectedInstrumentNumber( nLine ); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); Instrument *instr = instrList->get( nLine ); if( instr == NULL ) return false; pan_L = instr->get_pan_l(); pan_R = instr->get_pan_r(); // pan float fPanValue = 0.0; if (pan_R == 1.0) { fPanValue = 1.0 - (pan_L / 2.0); } else { fPanValue = pan_R / 2.0; } fPanValue = 1 * ( ((float) pan_param) / 127.0 ); if (fPanValue >= 0.5) { pan_L = (1.0 - fPanValue) * 2; pan_R = 1.0; } else { pan_L = 1.0; pan_R = fPanValue * 2; } instr->set_pan_l( pan_L ); instr->set_pan_r( pan_R ); Hydrogen::get_instance()->setSelectedInstrumentNumber(nLine); return true; } if( sActionString == "PAN_RELATIVE" ){ // changes the panning of a given mixer channel // this is useful if the panning is set by a rotary control knob bool ok; int nLine = pAction->getParameter1().toInt(&ok,10); int pan_param = pAction->getParameter2().toInt(&ok,10); float pan_L; float pan_R; Hydrogen *engine = Hydrogen::get_instance(); engine->setSelectedInstrumentNumber( nLine ); Song *song = engine->getSong(); InstrumentList *instrList = song->get_instrument_list(); Instrument *instr = instrList->get( nLine ); if( instr == NULL ) return false; pan_L = instr->get_pan_l(); pan_R = instr->get_pan_r(); // pan float fPanValue = 0.0; if (pan_R == 1.0) { fPanValue = 1.0 - (pan_L / 2.0); } else { fPanValue = pan_R / 2.0; } if( pan_param == 1 && fPanValue < 1 ){ fPanValue += 0.05; } if( pan_param != 1 && fPanValue > 0 ){ fPanValue -= 0.05; } if (fPanValue >= 0.5) { pan_L = (1.0 - fPanValue) * 2; pan_R = 1.0; } else { pan_L = 1.0; pan_R = fPanValue * 2; } instr->set_pan_l( pan_L ); instr->set_pan_r( pan_R ); Hydrogen::get_instance()->setSelectedInstrumentNumber(nLine); return true; } if( sActionString == "BPM_CC_RELATIVE" ){ /* * increments/decrements the BPM * this is useful if the bpm is set by a rotary control knob */ AudioEngine::get_instance()->lock( RIGHT_HERE ); int mult = 1; //second parameter of cc command //this value should be 1 to decrement and something other then 1 to increment the bpm int cc_param = 1; //this Action should be triggered only by CC commands bool ok; mult = pAction->getParameter1().toInt(&ok,10); cc_param = pAction->getParameter2().toInt(&ok,10); if( lastBpmChangeCCParameter == -1) { lastBpmChangeCCParameter = cc_param; } Song* pSong = pEngine->getSong(); if ( lastBpmChangeCCParameter >= cc_param && pSong->__bpm < 300) { pEngine->setBPM( pSong->__bpm - 1*mult ); } if ( lastBpmChangeCCParameter < cc_param && pSong->__bpm > 40 ) { pEngine->setBPM( pSong->__bpm + 1*mult ); } lastBpmChangeCCParameter = cc_param; AudioEngine::get_instance()->unlock(); return true; } if( sActionString == "BPM_FINE_CC_RELATIVE" ){ /* * increments/decrements the BPM * this is useful if the bpm is set by a rotary control knob */ AudioEngine::get_instance()->lock( RIGHT_HERE ); int mult = 1; //second parameter of cc command //this value should be 1 to decrement and something other then 1 to increment the bpm int cc_param = 1; //this Action should be triggered only by CC commands bool ok; mult = pAction->getParameter1().toInt(&ok,10); cc_param = pAction->getParameter2().toInt(&ok,10); if( lastBpmChangeCCParameter == -1) { lastBpmChangeCCParameter = cc_param; } Song* pSong = pEngine->getSong(); if ( lastBpmChangeCCParameter >= cc_param && pSong->__bpm < 300) { pEngine->setBPM( pSong->__bpm - 0.01*mult ); } if ( lastBpmChangeCCParameter < cc_param && pSong->__bpm > 40 ) { pEngine->setBPM( pSong->__bpm + 0.01*mult ); } lastBpmChangeCCParameter = cc_param; AudioEngine::get_instance()->unlock(); return true; } if( sActionString == "BPM_INCR" ){ AudioEngine::get_instance()->lock( RIGHT_HERE ); int mult = 1; bool ok; mult = pAction->getParameter1().toInt(&ok,10); Song* pSong = pEngine->getSong(); if (pSong->__bpm < 300) { pEngine->setBPM( pSong->__bpm + 1*mult ); } AudioEngine::get_instance()->unlock(); return true; } if( sActionString == "BPM_DECR" ){ AudioEngine::get_instance()->lock( RIGHT_HERE ); int mult = 1; bool ok; mult = pAction->getParameter1().toInt(&ok,10); Song* pSong = pEngine->getSong(); if (pSong->__bpm > 40 ) { pEngine->setBPM( pSong->__bpm - 1*mult ); } AudioEngine::get_instance()->unlock(); return true; } if( sActionString == ">>_NEXT_BAR"){ pEngine->setPatternPos(pEngine->getPatternPos() +1 ); pEngine->setTimelineBpm(); return true; } if( sActionString == "<<_PREVIOUS_BAR"){ pEngine->setPatternPos(pEngine->getPatternPos() -1 ); pEngine->setTimelineBpm(); return true; } if( sActionString == "PLAYLIST_SONG"){ bool ok; int songnumber = pAction->getParameter2().toInt(&ok,10); return setSong( songnumber ); } if( sActionString == "PLAYLIST_NEXT_SONG"){ int songnumber = Playlist::get_instance()->getActiveSongNumber(); return setSong( ++songnumber ); } if( sActionString == "PLAYLIST_PREV_SONG"){ int songnumber = Playlist::get_instance()->getActiveSongNumber(); return setSong( --songnumber ); } if( sActionString == "RECORD_READY"){ if ( pEngine->getState() != STATE_PLAYING ) { if (!Preferences::get_instance()->getRecordEvents()) { Preferences::get_instance()->setRecordEvents(true); } else { Preferences::get_instance()->setRecordEvents(false); } } return true; } if( sActionString == "RECORD/STROBE_TOGGLE"){ if (!Preferences::get_instance()->getRecordEvents()) { Preferences::get_instance()->setRecordEvents(true); } else { Preferences::get_instance()->setRecordEvents(false); } return true; } if( sActionString == "RECORD_STROBE"){ if (!Preferences::get_instance()->getRecordEvents()) { Preferences::get_instance()->setRecordEvents(true); } return true; } if( sActionString == "RECORD_EXIT"){ if (Preferences::get_instance()->getRecordEvents()) { Preferences::get_instance()->setRecordEvents(false); } return true; } if( sActionString == "TOGGLE_METRONOME"){ Preferences::get_instance()->m_bUseMetronome = !Preferences::get_instance()->m_bUseMetronome; return true; } if( sActionString == "UNDO_ACTION"){ EventQueue::get_instance()->push_event( EVENT_UNDO_REDO, 0);// 0 = undo return true; } if( sActionString == "REDO_ACTION"){ EventQueue::get_instance()->push_event( EVENT_UNDO_REDO, 1);// 1 = redo return true; } return false; }
void Sampler::setPlayingNotelength( Instrument* instrument, unsigned long ticks, unsigned long noteOnTick ) { if ( instrument ) { // stop all notes using this instrument Hydrogen *pEngine = Hydrogen::get_instance(); Song* mSong = pEngine->getSong(); int selectedpattern = pEngine->__get_selected_PatterNumber(); Pattern* currentPattern = NULL; if ( mSong->get_mode() == Song::PATTERN_MODE || ( pEngine->getState() != STATE_PLAYING )){ PatternList *pPatternList = mSong->get_pattern_list(); if ( ( selectedpattern != -1 ) && ( selectedpattern < ( int )pPatternList->size() ) ) { currentPattern = pPatternList->get( selectedpattern ); } }else { std::vector<PatternList*> *pColumns = mSong->get_pattern_group_vector(); // Pattern *pPattern = NULL; int pos = pEngine->getPatternPos() +1; for ( int i = 0; i < pos; ++i ) { PatternList *pColumn = ( *pColumns )[i]; currentPattern = pColumn->get( 0 ); } } if ( currentPattern ) { int patternsize = currentPattern->get_length(); for ( unsigned nNote = 0; nNote < currentPattern->get_length(); nNote++ ) { const Pattern::notes_t* notes = currentPattern->get_notes(); FOREACH_NOTE_CST_IT_BOUND(notes,it,nNote) { Note *pNote = it->second; if ( pNote!=NULL ) { if( !Preferences::get_instance()->__playselectedinstrument ){ if ( pNote->get_instrument() == instrument && pNote->get_position() == noteOnTick ) { AudioEngine::get_instance()->lock( RIGHT_HERE ); if ( ticks > patternsize ) ticks = patternsize - noteOnTick; pNote->set_length( ticks ); Hydrogen::get_instance()->getSong()->__is_modified = true; AudioEngine::get_instance()->unlock(); // unlock the audio engine } }else { if ( pNote->get_instrument() == pEngine->getSong()->get_instrument_list()->get( pEngine->getSelectedInstrumentNumber()) && pNote->get_position() == noteOnTick ) { AudioEngine::get_instance()->lock( RIGHT_HERE ); if ( ticks > patternsize ) ticks = patternsize - noteOnTick; pNote->set_length( ticks ); Hydrogen::get_instance()->getSong()->__is_modified = true; AudioEngine::get_instance()->unlock(); // unlock the audio engine } } } } } } }
void MainForm::action_file_export_pattern_as() { if ( ( Hydrogen::get_instance()->getState() == STATE_PLAYING ) ) { Hydrogen::get_instance()->sequencer_stop(); } Hydrogen *engine = Hydrogen::get_instance(); int selectedpattern = engine->getSelectedPatternNumber(); Song *song = engine->getSong(); Pattern *pat = song->get_pattern_list()->get ( selectedpattern ); Instrument *instr = song->get_instrument_list()->get ( 0 ); assert ( instr ); QDir dir = Preferences::get_instance()->__lastspatternDirectory; QFileDialog fd(this); fd.setFileMode ( QFileDialog::AnyFile ); fd.setFilter ( trUtf8 ( "Hydrogen Pattern (*.h2pattern)" ) ); fd.setAcceptMode ( QFileDialog::AcceptSave ); fd.setWindowTitle ( trUtf8 ( "Save Pattern as ..." ) ); fd.setDirectory ( dir ); fd.setSidebarUrls( fd.sidebarUrls() << QUrl::fromLocalFile( Filesystem::patterns_dir() ) ); QString defaultPatternname = QString ( pat->get_name() ); fd.selectFile ( defaultPatternname ); LocalFileMng fileMng; QString filename; if ( fd.exec() == QDialog::Accepted ) { filename = fd.selectedFiles().first(); QString tmpfilename = filename; QString toremove = tmpfilename.section( '/', -1 ); QString newdatapath = tmpfilename.replace( toremove, "" ); Preferences::get_instance()->__lastspatternDirectory = newdatapath; } if ( !filename.isEmpty() ) { QString sNewFilename = filename; if(sNewFilename.endsWith( ".h2pattern" ) ){ sNewFilename += ""; } else{ sNewFilename += ".h2pattern"; } QString patternname = sNewFilename; QString realpatternname = filename; QString realname = realpatternname.mid( realpatternname.lastIndexOf( "/" ) + 1 ); if ( realname.endsWith( ".h2pattern" ) ) realname.replace( ".h2pattern", "" ); pat->set_name(realname); HydrogenApp::get_instance()->getSongEditorPanel()->updateAll(); int err = fileMng.savePattern ( song, engine->getCurrentDrumkitname(), selectedpattern, patternname, realname, 2 ); if ( err != 0 ) { QMessageBox::warning( this, "Hydrogen", trUtf8("Could not export pattern.") ); _ERRORLOG ( "Error saving the pattern" ); } } h2app->setStatusBarMessage ( trUtf8 ( "Pattern saved." ), 10000 ); //update SoundlibraryPanel HydrogenApp::get_instance()->getInstrumentRack()->getSoundLibraryPanel()->test_expandedItems(); HydrogenApp::get_instance()->getInstrumentRack()->getSoundLibraryPanel()->updateDrumkitList(); }
SongEditorPanel::SongEditorPanel(QWidget *pParent) : QWidget( pParent ) , Object( __class_name ) , m_actionMode( DRAW_ACTION ) { m_nInitialWidth = 600; m_nInitialHeight = 250; Preferences *pPref = Preferences::get_instance(); Hydrogen* pEngine = Hydrogen::get_instance(); Song* pSong = pEngine->getSong(); setWindowTitle( trUtf8( "Song Editor" ) ); // background PixmapWidget *pBackPanel = new PixmapWidget( NULL ); pBackPanel->setFixedSize( 196, 49 ); pBackPanel->setPixmap( "/songEditor/bg_topPanel.png" ); // time line toggle button m_pTimeLineToggleBtn = new ToggleButton( pBackPanel, "/songEditor/btn_bpm_on.png", "/songEditor/btn_bpm_off.png", "/songEditor/btn_bpm_over.png", QSize( 54, 13 ) ); m_pTimeLineToggleBtn->move( 133, 6 ); m_pTimeLineToggleBtn->setToolTip( trUtf8( "Enable time line edit") ); connect( m_pTimeLineToggleBtn, SIGNAL( clicked( Button* ) ), this, SLOT( timeLineBtnPressed(Button* ) ) ); m_pTimeLineToggleBtn->setPressed( pPref->getUseTimelineBpm() ); // clear sequence button m_pClearPatternSeqBtn = new Button( pBackPanel, "/songEditor/btn_clear_on.png", "/songEditor/btn_clear_off.png", "/songEditor/btn_clear_over.png", QSize(53,13) ); m_pClearPatternSeqBtn->move( 6, 5 + 25 ); m_pClearPatternSeqBtn->setToolTip( trUtf8("Clear pattern sequence") ); connect( m_pClearPatternSeqBtn, SIGNAL( clicked( Button* ) ), this, SLOT( clearSequence(Button*) ) ); // new pattern button Button *newPatBtn = new Button( pBackPanel, "/songEditor/btn_new_on.png", "/songEditor/btn_new_off.png", "/songEditor/btn_new_over.png", QSize(19, 13) ); newPatBtn->move( 64, 5 + 25); newPatBtn->setToolTip( trUtf8("Create new pattern") ); connect( newPatBtn, SIGNAL( clicked( Button* ) ), this, SLOT( newPatBtnClicked( Button* ) ) ); // down button m_pDownBtn = new Button( pBackPanel, "/songEditor/btn_down_on.png", "/songEditor/btn_down_off.png", "/songEditor/btn_down_over.png", QSize(18,13) ); m_pDownBtn->move( 89, 5 + 25); m_pDownBtn->setToolTip( trUtf8("Move the selected pattern down") ); connect( m_pDownBtn, SIGNAL( clicked( Button* ) ), this, SLOT( downBtnClicked( Button* ) ) ); // up button m_pUpBtn = new Button( pBackPanel, "/songEditor/btn_up_on.png", "/songEditor/btn_up_off.png", "/songEditor/btn_up_over.png", QSize(18,13) ); m_pUpBtn->move( 106, 5 + 25 ); m_pUpBtn->setToolTip( trUtf8("Move the selected pattern up") ); connect( m_pUpBtn, SIGNAL( clicked( Button* ) ), this, SLOT( upBtnClicked( Button* ) ) ); // select toggle button m_pPointerActionBtn = new ToggleButton( pBackPanel, "/songEditor/btn_select_on.png", "/songEditor/btn_select_off.png", "/songEditor/btn_select_over.png", QSize( 18, 13 ) ); m_pPointerActionBtn->move( 128, 5 + 25 ); m_pPointerActionBtn->setToolTip( trUtf8( "Select mode" ) ); connect( m_pPointerActionBtn, SIGNAL( clicked( Button* ) ), this, SLOT( pointerActionBtnPressed(Button*) ) ); // draw toggle button m_pDrawActionBtn = new ToggleButton( pBackPanel, "/songEditor/btn_draw_on.png", "/songEditor/btn_draw_off.png", "/songEditor/btn_draw_over.png", QSize( 18, 13 ) ); m_pDrawActionBtn->move( 147, 5 + 25 ); m_pDrawActionBtn->setToolTip( trUtf8( "Draw mode") ); connect( m_pDrawActionBtn, SIGNAL( clicked( Button* ) ), this, SLOT( drawActionBtnPressed(Button* ) ) ); m_pDrawActionBtn->setPressed( true ); m_pModeActionBtn = new ToggleButton( pBackPanel, "/songEditor/btn_mode_on.png", "/songEditor/btn_mode_off.png", "/songEditor/btn_mode_over.png", QSize( 18, 13 ) ); m_pModeActionBtn->move( 169, 5 + 25 ); m_pModeActionBtn->setToolTip( trUtf8( "stacked mode") ); m_pModeActionBtn->setPressed( pPref->patternModePlaysSelected() ); connect( m_pModeActionBtn, SIGNAL( clicked( Button* ) ), this, SLOT( modeActionBtnPressed() ) ); // ZOOM m_pHScrollBar = new QScrollBar( Qt::Horizontal,NULL ); connect( m_pHScrollBar, SIGNAL(valueChanged(int)), this, SLOT( syncToExternalScrollBar() ) ); // zoom-in btn Button* pZoomInBtn = new Button( NULL, "/songEditor/btn_new_on.png", "/songEditor/btn_new_off.png", "/songEditor/btn_new_over.png", QSize( 19, 13 ) ); connect( pZoomInBtn, SIGNAL( clicked( Button* ) ), this, SLOT( zoomOutBtnPressed(Button* ) ) ); // zoom-out btn Button* pZoomOutBtn = new Button( NULL, "/songEditor/btn_minus_on.png", "/songEditor/btn_minus_off.png", "/songEditor/btn_minus_over.png", QSize( 19, 13 ) ); connect( pZoomOutBtn, SIGNAL( clicked( Button* ) ), this, SLOT( zoomInBtnPressed(Button* ) ) ); // view playback track toggle button m_pViewPlaybackToggleBtn = new ToggleButton( NULL, "/songEditor/btn_viewPL_on.png", "/songEditor/btn_viewPL_off.png", "/songEditor/btn_viewPL_over.png", QSize( 19, 13 ) ); m_pViewPlaybackToggleBtn->setToolTip( trUtf8( "View playback track") ); connect( m_pViewPlaybackToggleBtn, SIGNAL( clicked( Button* ) ), this, SLOT( viewPlaybackTrackBtnPressed(Button* ) ) ); m_pViewPlaybackToggleBtn->setPressed( false ); // Playback Fader m_pPlaybackTrackFader = new VerticalFader( pBackPanel, false, false ); m_pPlaybackTrackFader->move( 6, 2 ); m_pPlaybackTrackFader->setMinValue( 0.0 ); m_pPlaybackTrackFader->setMaxValue( 1.5 ); m_pPlaybackTrackFader->setValue( pSong->get_playback_track_volume() ); m_pPlaybackTrackFader->hide(); connect( m_pPlaybackTrackFader, SIGNAL( valueChanged(Fader*) ), this, SLOT( faderChanged(Fader*) ) ); // mute playback track toggle button m_pMutePlaybackToggleBtn = new ToggleButton( pBackPanel, "/mixerPanel/master_mute_on.png", "/mixerPanel/master_mute_off.png", "/mixerPanel/master_mute_over.png", QSize( 42, 13 ) ); m_pMutePlaybackToggleBtn->setToolTip( trUtf8( "Mute playback track") ); m_pMutePlaybackToggleBtn->move( 151, 6 ); m_pMutePlaybackToggleBtn->hide(); connect( m_pMutePlaybackToggleBtn, SIGNAL( clicked( Button* ) ), this, SLOT( mutePlaybackTrackBtnPressed(Button* ) ) ); m_pMutePlaybackToggleBtn->setPressed( !pSong->get_playback_track_enabled() ); // edit playback track toggle button m_pEditPlaybackBtn = new Button( pBackPanel, "/mixerPanel/edit_on.png", "/mixerPanel/edit_off.png", "/mixerPanel/edit_over.png", QSize( 42, 13 ) ); m_pEditPlaybackBtn->setToolTip( trUtf8( "Choose playback track") ); m_pEditPlaybackBtn->move( 124, 6 ); m_pEditPlaybackBtn->hide(); connect( m_pEditPlaybackBtn, SIGNAL( clicked( Button* ) ), this, SLOT( editPlaybackTrackBtnPressed(Button* ) ) ); m_pEditPlaybackBtn->setPressed( false ); // timeline view toggle button m_pViewTimeLineToggleBtn = new ToggleButton( NULL, "/songEditor/btn_viewTL_on.png", "/songEditor/btn_viewTL_off.png", "/songEditor/btn_viewTL_over.png", QSize( 19, 13 ) ); m_pViewTimeLineToggleBtn->setToolTip( trUtf8( "View timeline") ); connect( m_pViewTimeLineToggleBtn, SIGNAL( clicked( Button* ) ), this, SLOT( viewTimeLineBtnPressed(Button* ) ) ); m_pViewTimeLineToggleBtn->setPressed( true ); QHBoxLayout *pHZoomLayout = new QHBoxLayout(); pHZoomLayout->setSpacing( 0 ); pHZoomLayout->setMargin( 0 ); pHZoomLayout->addWidget( m_pViewPlaybackToggleBtn ); pHZoomLayout->addWidget( m_pViewTimeLineToggleBtn ); pHZoomLayout->addWidget( m_pHScrollBar ); pHZoomLayout->addWidget( pZoomInBtn ); pHZoomLayout->addWidget( pZoomOutBtn ); QWidget *pHScrollbarPanel = new QWidget(); pHScrollbarPanel->setLayout( pHZoomLayout ); //~ ZOOM // PATTERN LIST m_pPatternListScrollView = new QScrollArea( NULL ); m_pPatternListScrollView->setFrameShape( QFrame::NoFrame ); m_pPatternListScrollView->setFixedWidth( m_nPatternListWidth ); m_pPatternListScrollView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pPatternListScrollView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); connect( m_pPatternListScrollView->verticalScrollBar(), SIGNAL( valueChanged(int) ), this, SLOT( on_patternListScroll() ) ); m_pPatternList = new SongEditorPatternList( m_pPatternListScrollView->viewport() ); m_pPatternListScrollView->setWidget( m_pPatternList ); // EDITOR m_pEditorScrollView = new QScrollArea( NULL ); m_pEditorScrollView->setFrameShape( QFrame::NoFrame ); m_pEditorScrollView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pEditorScrollView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pSongEditor = new SongEditor( m_pEditorScrollView->viewport() ); m_pEditorScrollView->setWidget( m_pSongEditor ); connect( m_pEditorScrollView->horizontalScrollBar(), SIGNAL( valueChanged(int) ), this, SLOT( on_EditorScroll() ) ); connect( m_pEditorScrollView->verticalScrollBar(), SIGNAL( valueChanged(int) ), this, SLOT( on_EditorScroll() ) ); // POSITION RULER m_pPositionRulerScrollView = new QScrollArea( NULL ); m_pPositionRulerScrollView->setFrameShape( QFrame::NoFrame ); m_pPositionRulerScrollView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pPositionRulerScrollView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pPositionRulerScrollView->setFixedHeight( 50 ); m_pWidgetStack = new QStackedWidget( m_pPositionRulerScrollView ); m_pPositionRuler = new SongEditorPositionRuler( NULL ); m_pWaveDisplay = new WaveDisplay( m_pPositionRulerScrollView->viewport() ); InstrumentComponent *pCompo = AudioEngine::get_instance()->get_sampler()->__preview_instrument->get_components()->front(); assert(pCompo); m_pWaveDisplay->updateDisplay( pCompo->get_layer(0) ); m_pWidgetStack->addWidget( m_pPositionRuler ); m_pWidgetStack->addWidget( m_pWaveDisplay ); m_pPositionRulerScrollView->setWidgetResizable(true); m_pPositionRulerScrollView->setWidget( m_pWidgetStack ); m_pAutomationPathScrollView = new QScrollArea( NULL ); m_pAutomationPathScrollView->setFrameShape( QFrame::NoFrame ); m_pAutomationPathScrollView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pAutomationPathScrollView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_pAutomationPathView = new AutomationPathView( m_pAutomationPathScrollView->viewport() ); m_pAutomationPathScrollView->setWidget( m_pAutomationPathView ); m_pAutomationPathScrollView->setFixedHeight( 64 ); connect( m_pAutomationPathView, SIGNAL( valueChanged() ), this, SLOT( automationPathChanged() ) ); connect( m_pAutomationPathView, SIGNAL( pointAdded(float, float) ), this, SLOT( automationPathPointAdded(float,float) ) ); connect( m_pAutomationPathView, SIGNAL( pointRemoved(float, float) ), this, SLOT( automationPathPointRemoved(float,float) ) ); connect( m_pAutomationPathView, SIGNAL( pointMoved(float, float, float, float) ), this, SLOT( automationPathPointMoved(float,float, float, float) ) ); m_pAutomationCombo = new LCDCombo( NULL, 22 ); m_pAutomationCombo->setToolTip( trUtf8("Adjust parameter values in time") ); m_pAutomationCombo->addItem( trUtf8("Velocity") ); m_pAutomationCombo->set_text( trUtf8("Velocity") ); m_pAutomationCombo->update(); m_pVScrollBar = new QScrollBar( Qt::Vertical, NULL ); connect( m_pVScrollBar, SIGNAL(valueChanged(int)), this, SLOT( syncToExternalScrollBar() ) ); // ok...let's build the layout QGridLayout *pGridLayout = new QGridLayout(); pGridLayout->setSpacing( 0 ); pGridLayout->setMargin( 0 ); pGridLayout->addWidget( pBackPanel, 0, 0 ); pGridLayout->addWidget( m_pPositionRulerScrollView, 0, 1 ); pGridLayout->addWidget( m_pPatternListScrollView, 1, 0 ); pGridLayout->addWidget( m_pEditorScrollView, 1, 1 ); pGridLayout->addWidget( m_pVScrollBar, 1, 2, 2, 1 ); pGridLayout->addWidget( m_pAutomationPathScrollView, 2, 1); pGridLayout->addWidget( m_pAutomationCombo, 2, 0, Qt::AlignTop | Qt::AlignRight ); pGridLayout->addWidget( pHScrollbarPanel, 3, 1 ); if( !pPref->getShowAutomationArea() ){ m_pAutomationPathScrollView->hide(); m_pAutomationCombo->hide(); } this->setLayout( pGridLayout ); QPalette defaultPalette; defaultPalette.setColor( QPalette::Background, QColor( 58, 62, 72 ) ); this->setPalette( defaultPalette ); show(); updateAll(); HydrogenApp::get_instance()->addEventListener( this ); m_pTimer = new QTimer(this); connect(m_pTimer, SIGNAL(timeout()), this, SLOT( updatePlayHeadPosition() ) ); connect(m_pTimer, SIGNAL(timeout()), this, SLOT( updatePlaybackFaderPeaks() ) ); m_pTimer->start(100); }