From 17565caf535769a69eb06987e86220d2f9acb24d Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 28 Jul 2020 17:07:35 +0200 Subject: [PATCH] Improved relative paths (#5117) * Create PathUtils * Replace old SampleBuffer calls * Fix automatic track names * Fix things * Remove accidental duplicate file * Add includes * More incldues * PhysSong's code review + style * Fix vestige loading? Seems more reasonable to convert from relative on load and to relative on save than vice versa. * Typo fix * More Bases * Enable more bases * Add missing semicolons in prefixes * Nicer sample track tooltip * Use correct directories "userXDir" gives the default dir for ladspa, sf2, and gig. "xDir" gives the user dir. * Make relative to both default and custom locations Part 1 * Make relative to both default and custom locations Part 2 * Typofix * Typofix * Fix upgrade function after base renaming * Fix Tests * Update tests/src/core/RelativePathsTest.cpp Co-Authored-By: Hyunjin Song * Choose UserXBase over DefaultXBase if identical toShortestRelative sticks with the first base found if two bases give the same path length. By placing UserXBase Bases before DefaultXBase Bases in the relativeBases vector, toShortestRelative will prioritize them. * Ensure baseLocation always has trailing slash Otherwise, a user configuring a path without one will break things. * Move loc declaration out of switch * Semicolon * Apply suggestions from code review... * Include PathUtil and sort includes * More granular includes * Apply suggestions from code review Co-Authored-By: Hyunjin Song * Update include/PathUtil.h * Leave empty paths alone * Fix stupid merge * Really fix merge. Hopefully * Switch Base from enum to class enum * Don't pass Base by reference * Use QStringLiteral for static QString allocation in basePrefix method * Make VST loading more similar to previous implementation * Fix tests after enum change * Attempt to fix VST loading, nicer name for sample clips * Fix last review comment Don't append a "/" that will be removed by cleanPath later * Apply suggestions from code review Co-authored-by: Dominic Clark Co-authored-by: Hyunjin Song Co-authored-by: Dominic Clark --- include/PathUtil.h | 41 +++++ include/SampleBuffer.h | 5 +- plugins/GigPlayer/GigPlayer.cpp | 29 ++-- .../audio_file_processor.cpp | 29 ++-- plugins/patman/patman.cpp | 22 +-- plugins/sf2_player/sf2_player.cpp | 14 +- plugins/vestige/vestige.cpp | 18 +-- plugins/vst_base/VstPlugin.cpp | 12 +- src/core/CMakeLists.txt | 1 + src/core/PathUtil.cpp | 148 ++++++++++++++++++ src/core/SampleBuffer.cpp | 67 +------- src/tracks/SampleTrack.cpp | 30 ++-- tests/src/core/RelativePathsTest.cpp | 28 +++- 13 files changed, 288 insertions(+), 156 deletions(-) create mode 100644 include/PathUtil.h create mode 100644 src/core/PathUtil.cpp diff --git a/include/PathUtil.h b/include/PathUtil.h new file mode 100644 index 000000000..cc6b982a1 --- /dev/null +++ b/include/PathUtil.h @@ -0,0 +1,41 @@ +#ifndef PATHUTIL_H +#define PATHUTIL_H + +#include "lmms_export.h" + +#include + +namespace PathUtil +{ + enum class Base { Absolute, ProjectDir, FactorySample, UserSample, UserVST, Preset, + UserLADSPA, DefaultLADSPA, UserSoundfont, DefaultSoundfont, UserGIG, DefaultGIG }; + + //! Return the directory associated with a given base as a QString + QString LMMS_EXPORT baseLocation(const Base base); + //! Return the directory associated with a given base as a QDir + QDir LMMS_EXPORT baseQDir (const Base base); + //! Return the prefix used to denote this base in path strings + QString LMMS_EXPORT basePrefix(const Base base); + //! Check the prefix of a path and return the base it corresponds to + //! Defaults to Base::Absolute + Base LMMS_EXPORT baseLookup(const QString & path); + + //! Remove the prefix from a path, iff there is one + QString LMMS_EXPORT stripPrefix(const QString & path); + //! Get the filename for a path, handling prefixed paths correctly + QString LMMS_EXPORT cleanName(const QString & path); + + //! Upgrade prefix-less relative paths to the new format + QString LMMS_EXPORT oldRelativeUpgrade(const QString & input); + + //! Make this path absolute + QString LMMS_EXPORT toAbsolute(const QString & input); + //! Make this path relative to a given base, return an absolute path if that fails + QString LMMS_EXPORT relativeOrAbsolute(const QString & input, const Base base); + //! Make this path relative to any base, choosing the shortest if there are + //! multiple options. Defaults to an absolute path if all bases fail. + QString LMMS_EXPORT toShortestRelative(const QString & input); + +} + +#endif diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 26e856025..89a3add3b 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -84,7 +84,7 @@ public: { m_isBackwards = _backwards; } - + int interpolationMode() const { return m_interpolationMode; @@ -251,9 +251,6 @@ public: m_varLock.unlock(); } - static QString tryToMakeRelative( const QString & _file ); - static QString tryToMakeAbsolute(const QString & file); - public slots: void setAudioFile( const QString & _audio_file ); diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index b358e24e9..334e2bd77 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -28,6 +28,7 @@ * */ +#include "GigPlayer.h" #include #include @@ -35,18 +36,18 @@ #include #include -#include "FileDialog.h" -#include "GigPlayer.h" -#include "Engine.h" -#include "InstrumentTrack.h" -#include "InstrumentPlayHandle.h" -#include "Mixer.h" -#include "NotePlayHandle.h" -#include "Knob.h" -#include "SampleBuffer.h" -#include "Song.h" #include "ConfigManager.h" #include "endian_handling.h" +#include "Engine.h" +#include "FileDialog.h" +#include "InstrumentTrack.h" +#include "InstrumentPlayHandle.h" +#include "Knob.h" +#include "Mixer.h" +#include "NotePlayHandle.h" +#include "PathUtil.h" +#include "SampleBuffer.h" +#include "Song.h" #include "PatchesDialog.h" #include "ToolTip.h" @@ -211,8 +212,8 @@ void GigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) try { - m_instance = new GigInstance( SampleBuffer::tryToMakeAbsolute( _gigFile ) ); - m_filename = SampleBuffer::tryToMakeRelative( _gigFile ); + m_instance = new GigInstance( PathUtil::toAbsolute( _gigFile ) ); + m_filename = PathUtil::toShortestRelative( _gigFile ); } catch( ... ) { @@ -225,7 +226,7 @@ void GigInstrument::openFile( const QString & _gigFile, bool updateTrackName ) if( updateTrackName == true ) { - instrumentTrack()->setName( QFileInfo( _gigFile ).baseName() ); + instrumentTrack()->setName(PathUtil::cleanName( _gigFile ) ); updatePatch(); } } @@ -1057,7 +1058,7 @@ void GigInstrumentView::showFileDialog() if( k->m_filename != "" ) { - QString f = SampleBuffer::tryToMakeAbsolute( k->m_filename ); + QString f = PathUtil::toAbsolute( k->m_filename ); ofd.setDirectory( QFileInfo( f ).absolutePath() ); ofd.selectFile( QFileInfo( f ).fileName() ); } diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index c8850392e..51af71f55 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -22,6 +22,7 @@ * */ +#include "audio_file_processor.h" #include #include @@ -31,18 +32,18 @@ #include -#include "audio_file_processor.h" #include "ConfigManager.h" +#include "DataFile.h" #include "Engine.h" -#include "Song.h" +#include "gui_templates.h" #include "InstrumentTrack.h" +#include "interpolation.h" #include "Mixer.h" #include "NotePlayHandle.h" -#include "interpolation.h" -#include "gui_templates.h" -#include "ToolTip.h" +#include "PathUtil.h" +#include "Song.h" #include "StringPairDrag.h" -#include "DataFile.h" +#include "ToolTip.h" #include "embed.h" #include "plugin_export.h" @@ -97,13 +98,13 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) : this, SLOT( loopPointChanged() ) ); connect( &m_stutterModel, SIGNAL( dataChanged() ), this, SLOT( stutterModelChanged() ) ); - + //interpolation modes m_interpolationModel.addItem( tr( "None" ) ); m_interpolationModel.addItem( tr( "Linear" ) ); m_interpolationModel.addItem( tr( "Sinc" ) ); m_interpolationModel.setValue( 1 ); - + pointChanged(); } @@ -237,7 +238,7 @@ void audioFileProcessor::loadSettings( const QDomElement & _this ) { setAudioFile( _this.attribute( "src" ), false ); - QString absolutePath = m_sampleBuffer.tryToMakeAbsolute( m_sampleBuffer.audioFile() ); + QString absolutePath = PathUtil::toAbsolute( m_sampleBuffer.audioFile() ); if ( !QFileInfo( absolutePath ).exists() ) { QString message = tr( "Sample not found: %1" ).arg( m_sampleBuffer.audioFile() ); @@ -329,7 +330,7 @@ void audioFileProcessor::setAudioFile( const QString & _audio_file, m_sampleBuffer.audioFile().isEmpty() ) ) { // then set it to new one - instrumentTrack()->setName( QFileInfo( _audio_file).fileName() ); + instrumentTrack()->setName( PathUtil::cleanName( _audio_file ) ); } // else we don't touch the track-name, because the user named it self @@ -363,7 +364,7 @@ void audioFileProcessor::stutterModelChanged() } -void audioFileProcessor::startPointChanged( void ) +void audioFileProcessor::startPointChanged( void ) { // check if start is over end and swap values if so if( m_startPointModel.value() > m_endPointModel.value() ) @@ -390,7 +391,7 @@ void audioFileProcessor::startPointChanged( void ) { m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) ); } - + pointChanged(); } @@ -1284,7 +1285,3 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * model, void *) } - - - - diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index 8a0c71340..e51703834 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -3,7 +3,7 @@ * * Copyright (c) 2007-2008 Javier Serrano Polo * Copyright (c) 2009-2014 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -32,15 +32,15 @@ #include "ConfigManager.h" #include "endian_handling.h" #include "Engine.h" +#include "FileDialog.h" #include "gui_templates.h" #include "InstrumentTrack.h" #include "NotePlayHandle.h" +#include "PathUtil.h" #include "PixmapButton.h" #include "Song.h" #include "StringPairDrag.h" #include "ToolTip.h" -#include "FileDialog.h" -#include "ConfigManager.h" #include "embed.h" @@ -192,14 +192,13 @@ void patmanInstrument::setFile( const QString & _patch_file, bool _rename ) m_patchFile == "" ) ) { // then set it to new one - instrumentTrack()->setName( QFileInfo( _patch_file - ).fileName() ); + instrumentTrack()->setName( PathUtil::cleanName( _patch_file ) ); } // else we don't touch the instrument-track-name, because the user // named it self - m_patchFile = SampleBuffer::tryToMakeRelative( _patch_file ); - LoadErrors error = loadPatch( SampleBuffer::tryToMakeAbsolute( _patch_file ) ); + m_patchFile = PathUtil::toShortestRelative( _patch_file ); + LoadErrors error = loadPatch( PathUtil::toAbsolute( _patch_file ) ); if( error ) { printf("Load error\n"); @@ -625,8 +624,8 @@ void PatmanView::paintEvent( QPaintEvent * ) QPainter p( this ); p.setFont( pointSize<8>( font() ) ); - p.drawText( 8, 116, 235, 16, - Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, + p.drawText( 8, 116, 235, 16, + Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, m_displayFilename ); } @@ -641,8 +640,3 @@ void PatmanView::modelChanged( void ) connect( m_pi, SIGNAL( fileChanged() ), this, SLOT( updateFilename() ) ); } - - - - - diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 2f9456370..7065a0800 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -23,6 +23,8 @@ * */ +#include "sf2_player.h" + #include #include #include @@ -30,14 +32,14 @@ #include "ConfigManager.h" #include "FileDialog.h" -#include "sf2_player.h" #include "ConfigManager.h" #include "Engine.h" #include "InstrumentTrack.h" #include "InstrumentPlayHandle.h" +#include "Knob.h" #include "Mixer.h" #include "NotePlayHandle.h" -#include "Knob.h" +#include "PathUtil.h" #include "SampleBuffer.h" #include "Song.h" @@ -372,8 +374,8 @@ void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName ) emit fileLoading(); // Used for loading file - char * sf2Ascii = qstrdup( qPrintable( SampleBuffer::tryToMakeAbsolute( _sf2File ) ) ); - QString relativePath = SampleBuffer::tryToMakeRelative( _sf2File ); + char * sf2Ascii = qstrdup( qPrintable( PathUtil::toAbsolute( _sf2File ) ) ); + QString relativePath = PathUtil::toShortestRelative( _sf2File ); // free reference to soundfont if one is selected freeFont(); @@ -435,7 +437,7 @@ void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName ) if( updateTrackName || instrumentTrack()->displayName() == displayName() ) { - instrumentTrack()->setName( QFileInfo( _sf2File ).baseName() ); + instrumentTrack()->setName( PathUtil::cleanName( _sf2File ) ); } } @@ -1145,7 +1147,7 @@ void sf2InstrumentView::showFileDialog() if( k->m_filename != "" ) { - QString f = SampleBuffer::tryToMakeAbsolute( k->m_filename ); + QString f = PathUtil::toAbsolute( k->m_filename ); ofd.setDirectory( QFileInfo( f ).absolutePath() ); ofd.selectFile( QFileInfo( f ).fileName() ); } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 0d75992a4..b17a16845 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -40,24 +40,24 @@ #include -#include "ConfigManager.h" #include "BufferManager.h" #include "ConfigManager.h" #include "Engine.h" +#include "FileDialog.h" +#include "GuiApplication.h" #include "gui_templates.h" #include "InstrumentPlayHandle.h" #include "InstrumentTrack.h" #include "LocaleHelper.h" #include "MainWindow.h" #include "Mixer.h" -#include "GuiApplication.h" +#include "PathUtil.h" #include "PixmapButton.h" #include "SampleBuffer.h" #include "Song.h" #include "StringPairDrag.h" #include "TextFloat.h" #include "ToolTip.h" -#include "FileDialog.h" #include "embed.h" @@ -271,7 +271,7 @@ void vestigeInstrument::reloadPlugin() void vestigeInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - _this.setAttribute( "plugin", m_pluginDLL ); + _this.setAttribute( "plugin", PathUtil::toShortestRelative(m_pluginDLL) ); m_pluginMutex.lock(); if( m_plugin != NULL ) { @@ -338,14 +338,14 @@ void vestigeInstrument::loadFile( const QString & _file ) // if the same is loaded don't load again (for preview) if (instrumentTrack() != NULL && instrumentTrack()->isPreviewMode() && - m_pluginDLL == SampleBuffer::tryToMakeRelative( _file )) + m_pluginDLL == PathUtil::toShortestRelative( _file )) return; if ( m_plugin != NULL ) { closePlugin(); } - m_pluginDLL = SampleBuffer::tryToMakeRelative( _file ); + m_pluginDLL = PathUtil::toShortestRelative( _file ); TextFloat * tf = NULL; if( gui ) { @@ -686,7 +686,7 @@ void VestigeInstrumentView::openPlugin() if( m_vi->m_pluginDLL != "" ) { - QString f = SampleBuffer::tryToMakeAbsolute( m_vi->m_pluginDLL ); + QString f = PathUtil::toAbsolute( m_vi->m_pluginDLL ); ofd.setDirectory( QFileInfo( f ).absolutePath() ); ofd.selectFile( QFileInfo( f ).fileName() ); } @@ -1233,7 +1233,3 @@ Q_DECL_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) } - - - - diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index cd4844b81..7d6a45940 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -59,6 +59,7 @@ #include "LocaleHelper.h" #include "MainWindow.h" #include "Mixer.h" +#include "PathUtil.h" #include "Song.h" #include "FileDialog.h" @@ -121,7 +122,7 @@ private: VstPlugin::VstPlugin( const QString & _plugin ) : - m_plugin( _plugin ), + m_plugin( PathUtil::toAbsolute(_plugin) ), m_pluginWindowID( 0 ), m_embedMethod( gui ? ConfigManager::inst()->vstEmbedMethod() @@ -129,11 +130,6 @@ VstPlugin::VstPlugin( const QString & _plugin ) : m_version( 0 ), m_currentProgram() { - if( QDir::isRelativePath( m_plugin ) ) - { - m_plugin = ConfigManager::inst()->vstDir() + m_plugin; - } - setSplittedChannels( true ); PE::MachineType machineType; @@ -804,7 +800,3 @@ QString VstPlugin::embedMethod() const { return m_embedMethod; } - - - - diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 42d0f6784..730791bf7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -46,6 +46,7 @@ set(LMMS_SRCS core/Note.cpp core/NotePlayHandle.cpp core/Oscillator.cpp + core/PathUtil.cpp core/PeakController.cpp core/PerfLog.cpp core/Piano.cpp diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp new file mode 100644 index 000000000..ab81c4941 --- /dev/null +++ b/src/core/PathUtil.cpp @@ -0,0 +1,148 @@ +#include "PathUtil.h" + +#include +#include +#include + +#include "ConfigManager.h" + +namespace PathUtil +{ + Base relativeBases[] = { Base::ProjectDir, Base::FactorySample, Base::UserSample, Base::UserVST, Base::Preset, + Base::UserLADSPA, Base::DefaultLADSPA, Base::UserSoundfont, Base::DefaultSoundfont, Base::UserGIG, Base::DefaultGIG }; + + QString baseLocation(const Base base) + { + QString loc = ""; + switch (base) + { + case Base::ProjectDir : loc = ConfigManager::inst()->userProjectsDir(); break; + case Base::FactorySample : + { + QDir fsd = QDir(ConfigManager::inst()->factorySamplesDir()); + loc = fsd.absolutePath(); break; + } + case Base::UserSample : loc = ConfigManager::inst()->userSamplesDir(); break; + case Base::UserVST : loc = ConfigManager::inst()->userVstDir(); break; + case Base::Preset : loc = ConfigManager::inst()->userPresetsDir(); break; + case Base::UserLADSPA : loc = ConfigManager::inst()->ladspaDir(); break; + case Base::DefaultLADSPA : loc = ConfigManager::inst()->userLadspaDir(); break; + case Base::UserSoundfont : loc = ConfigManager::inst()->sf2Dir(); break; + case Base::DefaultSoundfont : loc = ConfigManager::inst()->userSf2Dir(); break; + case Base::UserGIG : loc = ConfigManager::inst()->gigDir(); break; + case Base::DefaultGIG : loc = ConfigManager::inst()->userGigDir(); break; + default : return QString(""); + } + return QDir::cleanPath(loc) + "/"; + } + + QDir baseQDir (const Base base) { return QDir(baseLocation(base)); } + + QString basePrefix(const Base base) + { + switch (base) + { + case Base::ProjectDir : return QStringLiteral("userprojects:"); + case Base::FactorySample : return QStringLiteral("factorysample:"); + case Base::UserSample : return QStringLiteral("usersample:"); + case Base::UserVST : return QStringLiteral("uservst:"); + case Base::Preset : return QStringLiteral("preset:"); + case Base::UserLADSPA : return QStringLiteral("userladspa:"); + case Base::DefaultLADSPA : return QStringLiteral("defaultladspa:"); + case Base::UserSoundfont : return QStringLiteral("usersoundfont:"); + case Base::DefaultSoundfont : return QStringLiteral("defaultsoundfont:"); + case Base::UserGIG : return QStringLiteral("usergig:"); + case Base::DefaultGIG : return QStringLiteral("defaultgig:"); + default : return QStringLiteral(""); + } + } + + Base baseLookup(const QString & path) + { + for (auto base: relativeBases) + { + QString prefix = basePrefix(base); + if ( path.startsWith(prefix) ) { return base; } + } + return Base::Absolute; + } + + + + + QString stripPrefix(const QString & path) + { + return path.mid( basePrefix(baseLookup(path)).length() ); + } + + QString cleanName(const QString & path) + { + return stripPrefix(QFileInfo(path).baseName()); + } + + + + + QString oldRelativeUpgrade(const QString & input) + { + if (input.isEmpty()) { return input; } + + //Start by assuming that the file is a user sample + Base assumedBase = Base::UserSample; + + //Check if it's a factory sample + QString factoryPath = baseLocation(Base::FactorySample) + input; + QFileInfo factoryInfo(factoryPath); + if (factoryInfo.exists()) { assumedBase = Base::FactorySample; } + + //Check if it's a VST + QString vstPath = baseLocation(Base::UserVST) + input; + QFileInfo vstInfo(vstPath); + if (vstInfo.exists()) { assumedBase = Base::UserVST; } + + //Assume we've found the correct base location, return the full path + return basePrefix(assumedBase) + input; + } + + + + + QString toAbsolute(const QString & input) + { + //First, do no harm to absolute paths + QFileInfo inputFileInfo = QFileInfo(input); + if (inputFileInfo.isAbsolute()) { return input; } + //Next, handle old relative paths with no prefix + QString upgraded = input.contains(":") ? input : oldRelativeUpgrade(input); + + Base base = baseLookup(upgraded); + return baseLocation(base) + upgraded.remove(0, basePrefix(base).length()); + } + + QString relativeOrAbsolute(const QString & input, const Base base) + { + if (input.isEmpty()) { return input; } + QString absolutePath = toAbsolute(input); + QString relativePath = baseQDir(base).relativeFilePath(absolutePath); + return relativePath.startsWith("..") ? absolutePath : relativePath; + } + + QString toShortestRelative(const QString & input) + { + QFileInfo inputFileInfo = QFileInfo(input); + QString absolutePath = inputFileInfo.isAbsolute() ? input : toAbsolute(input); + + Base shortestBase = Base::Absolute; + QString shortestPath = relativeOrAbsolute(absolutePath, shortestBase); + for (auto base: relativeBases) + { + QString otherPath = relativeOrAbsolute(absolutePath, base); + if (otherPath.length() < shortestPath.length()) + { + shortestBase = base; + shortestPath = otherPath; + } + } + return basePrefix(shortestBase) + relativeOrAbsolute(absolutePath, shortestBase); + } +} diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index d0c39b13a..cd943638d 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -24,7 +24,6 @@ #include "SampleBuffer.h" - #include #include #include @@ -55,11 +54,11 @@ #include "Engine.h" #include "GuiApplication.h" #include "Mixer.h" +#include "PathUtil.h" #include "FileDialog.h" - SampleBuffer::SampleBuffer() : m_audioFile( "" ), m_origData( NULL ), @@ -179,7 +178,7 @@ void SampleBuffer::update( bool _keep_settings ) } else if( !m_audioFile.isEmpty() ) { - QString file = tryToMakeAbsolute( m_audioFile ); + QString file = PathUtil::toAbsolute( m_audioFile ); int_sample_t * buf = NULL; sample_t * fbuf = NULL; ch_cnt_t channels = DEFAULT_CHANNELS; @@ -781,7 +780,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, } } - if( tmp != NULL ) + if( tmp != NULL ) { MM_FREE( tmp ); } @@ -1031,7 +1030,7 @@ QString SampleBuffer::openAudioFile() const { return QString(); } - return tryToMakeRelative( ofd.selectedFiles()[0] ); + return PathUtil::toShortestRelative( ofd.selectedFiles()[0] ); } return QString(); @@ -1222,7 +1221,7 @@ SampleBuffer * SampleBuffer::resample( const sample_rate_t _src_sr, void SampleBuffer::setAudioFile( const QString & _audio_file ) { - m_audioFile = tryToMakeRelative( _audio_file ); + m_audioFile = PathUtil::toShortestRelative( _audio_file ); update(); } @@ -1419,60 +1418,6 @@ void SampleBuffer::setReversed( bool _on ) -QString SampleBuffer::tryToMakeRelative( const QString & file ) -{ - if( QFileInfo( file ).isRelative() == false ) - { - // Normalize the path - QString f( QDir::cleanPath( file ) ); - - // First, look in factory samples - // Isolate "samples/" from "data:/samples/" - QString samplesSuffix = ConfigManager::inst()->factorySamplesDir().mid( ConfigManager::inst()->dataDir().length() ); - - // Iterate over all valid "data:/" searchPaths - for ( const QString & path : QDir::searchPaths( "data" ) ) - { - QString samplesPath = QDir::cleanPath( path + samplesSuffix ) + "/"; - if ( f.startsWith( samplesPath ) ) - { - return QString( f ).mid( samplesPath.length() ); - } - } - - // Next, look in user samples - QString usd = ConfigManager::inst()->userSamplesDir(); - usd.replace( QDir::separator(), '/' ); - if( f.startsWith( usd ) ) - { - return QString( f ).mid( usd.length() ); - } - } - return file; -} - - - - -QString SampleBuffer::tryToMakeAbsolute(const QString& file) -{ - QFileInfo f(file); - - if(f.isRelative()) - { - f = QFileInfo(ConfigManager::inst()->userSamplesDir() + file); - - if(! f.exists()) - { - f = QFileInfo(ConfigManager::inst()->factorySamplesDir() + file); - } - } - - if (f.exists()) { - return f.absoluteFilePath(); - } - return file; -} @@ -1488,7 +1433,7 @@ SampleBuffer::handleState::handleState( bool _varying_pitch, int interpolation_m { int error; m_interpolationMode = interpolation_mode; - + if( ( m_resamplingData = src_new( interpolation_mode, DEFAULT_CHANNELS, &error ) ) == NULL ) { qDebug( "Error: src_new() failed in sample_buffer.cpp!\n" ); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 9232cc8b0..86e1861c6 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -34,23 +34,24 @@ #include #include +#include "BBTrack.h" +#include "EffectRackView.h" +#include "embed.h" +#include "FxMixerView.h" #include "gui_templates.h" #include "GuiApplication.h" -#include "Song.h" -#include "embed.h" -#include "ToolTip.h" -#include "BBTrack.h" -#include "SamplePlayHandle.h" -#include "SampleRecordHandle.h" -#include "SongEditor.h" -#include "StringPairDrag.h" -#include "TimeLineWidget.h" #include "Knob.h" #include "MainWindow.h" #include "Mixer.h" -#include "EffectRackView.h" -#include "FxMixerView.h" +#include "PathUtil.h" +#include "SamplePlayHandle.h" +#include "SampleRecordHandle.h" +#include "Song.h" +#include "SongEditor.h" +#include "StringPairDrag.h" #include "TabWidget.h" +#include "TimeLineWidget.h" +#include "ToolTip.h" #include "TrackLabelButton.h" SampleTCO::SampleTCO( Track * _track ) : @@ -333,7 +334,7 @@ void SampleTCOView::updateSample() // set tooltip to filename so that user can see what sample this // sample-tco contains ToolTip::add( this, ( m_tco->m_sampleBuffer->audioFile() != "" ) ? - m_tco->m_sampleBuffer->audioFile() : + PathUtil::toAbsolute(m_tco->m_sampleBuffer->audioFile()) : tr( "Double-click to open sample" ) ); } @@ -535,9 +536,8 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) qMax( static_cast( m_tco->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); m_tco->m_sampleBuffer->visualize( p, r, pe->rect() ); - QFileInfo fileInfo(m_tco->m_sampleBuffer->audioFile()); - QString filename = fileInfo.fileName(); - paintTextLabel(filename, p); + QString name = PathUtil::cleanName(m_tco->m_sampleBuffer->audioFile()); + paintTextLabel(name, p); // disable antialiasing for borders, since its not needed p.setRenderHint( QPainter::Antialiasing, false ); diff --git a/tests/src/core/RelativePathsTest.cpp b/tests/src/core/RelativePathsTest.cpp index 6a7548377..3f1712e1b 100644 --- a/tests/src/core/RelativePathsTest.cpp +++ b/tests/src/core/RelativePathsTest.cpp @@ -26,6 +26,7 @@ #include "ConfigManager.h" #include "SampleBuffer.h" +#include "PathUtil.h" #include @@ -33,18 +34,35 @@ class RelativePathsTest : QTestSuite { Q_OBJECT private slots: - void RelativePathComparisonTests() + void PathUtilComparisonTests() { QFileInfo fi(ConfigManager::inst()->factorySamplesDir() + "/drums/kick01.ogg"); QVERIFY(fi.exists()); QString absPath = fi.absoluteFilePath(); - QString relPath = "drums/kick01.ogg"; + QString oldRelPath = "drums/kick01.ogg"; + QString relPath = PathUtil::basePrefix(PathUtil::Base::FactorySample) + "drums/kick01.ogg"; QString fuzPath = absPath; fuzPath.replace(relPath, "drums/.///kick01.ogg"); - QCOMPARE(SampleBuffer::tryToMakeRelative(absPath), relPath); - QCOMPARE(SampleBuffer::tryToMakeAbsolute(relPath), absPath); - QCOMPARE(SampleBuffer::tryToMakeRelative(fuzPath), relPath); + + //Test nicely formatted paths + QCOMPARE(PathUtil::toShortestRelative(absPath), relPath); + QCOMPARE(PathUtil::toAbsolute(relPath), absPath); + + //Test upgrading old paths + QCOMPARE(PathUtil::toShortestRelative(oldRelPath), relPath); + QCOMPARE(PathUtil::toAbsolute(oldRelPath), absPath); + + //Test weird but valid paths + QCOMPARE(PathUtil::toShortestRelative(fuzPath), relPath); + QCOMPARE(PathUtil::toAbsolute(fuzPath), absPath); + + //Empty paths should stay empty + QString empty = QString(""); + QCOMPARE(PathUtil::stripPrefix(""), empty); + QCOMPARE(PathUtil::cleanName(""), empty); + QCOMPARE(PathUtil::toAbsolute(""), empty); + QCOMPARE(PathUtil::toShortestRelative(""), empty); } } RelativePathTests;