| 1 | /* |
|---|
| 2 | * Hydrogen |
|---|
| 3 | * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net] |
|---|
| 4 | * |
|---|
| 5 | * http://www.hydrogen-music.org |
|---|
| 6 | * |
|---|
| 7 | * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | * it under the terms of the GNU General Public License as published by |
|---|
| 9 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | * (at your option) any later version. |
|---|
| 11 | * |
|---|
| 12 | * This program is distributed in the hope that it will be useful, |
|---|
| 13 | * but WITHOUT ANY WARRANTY, without even the implied warranty of |
|---|
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | * GNU General Public License for more details. |
|---|
| 16 | * |
|---|
| 17 | * You should have received a copy of the GNU General Public License |
|---|
| 18 | * along with this program; if not, write to the Free Software |
|---|
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 20 | * |
|---|
| 21 | */ |
|---|
| 22 | |
|---|
| 23 | #include "config.h" |
|---|
| 24 | #include "version.h" |
|---|
| 25 | |
|---|
| 26 | #include <cassert> |
|---|
| 27 | |
|---|
| 28 | #include "xml/tinyxml.h" |
|---|
| 29 | |
|---|
| 30 | #include <hydrogen/adsr.h> |
|---|
| 31 | #include <hydrogen/data_path.h> |
|---|
| 32 | #include <hydrogen/LocalFileMng.h> |
|---|
| 33 | |
|---|
| 34 | #include <hydrogen/fx/Effects.h> |
|---|
| 35 | #include <hydrogen/globals.h> |
|---|
| 36 | #include <hydrogen/Song.h> |
|---|
| 37 | #include <hydrogen/sample.h> |
|---|
| 38 | #include <hydrogen/instrument.h> |
|---|
| 39 | #include <hydrogen/Pattern.h> |
|---|
| 40 | #include <hydrogen/note.h> |
|---|
| 41 | #include <hydrogen/hydrogen.h> |
|---|
| 42 | |
|---|
| 43 | namespace H2Core |
|---|
| 44 | { |
|---|
| 45 | |
|---|
| 46 | Song::Song( const QString& name, const QString& author, float bpm, float volume ) |
|---|
| 47 | : Object( "Song" ) |
|---|
| 48 | , __is_muted( false ) |
|---|
| 49 | , __resolution( 48 ) |
|---|
| 50 | , __bpm( bpm ) |
|---|
| 51 | , __is_modified( false ) |
|---|
| 52 | , __name( name ) |
|---|
| 53 | , __author( author ) |
|---|
| 54 | , __volume( volume ) |
|---|
| 55 | , __metronome_volume( 0.5 ) |
|---|
| 56 | , __pattern_list( NULL ) |
|---|
| 57 | , __pattern_group_sequence( NULL ) |
|---|
| 58 | , __instrument_list( NULL ) |
|---|
| 59 | , __filename( "" ) |
|---|
| 60 | , __is_loop_enabled( false ) |
|---|
| 61 | , __humanize_time_value( 0.0 ) |
|---|
| 62 | , __humanize_velocity_value( 0.0 ) |
|---|
| 63 | , __swing_factor( 0.0 ) |
|---|
| 64 | , __song_mode( PATTERN_MODE ) |
|---|
| 65 | { |
|---|
| 66 | INFOLOG( QString( "INIT '%1'" ).arg( __name ) ); |
|---|
| 67 | |
|---|
| 68 | //m_bDelayFXEnabled = false; |
|---|
| 69 | //m_fDelayFXWetLevel = 0.8; |
|---|
| 70 | //m_fDelayFXFeedback = 0.5; |
|---|
| 71 | //m_nDelayFXTime = MAX_NOTES / 8; |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | |
|---|
| 75 | |
|---|
| 76 | Song::~Song() |
|---|
| 77 | { |
|---|
| 78 | // delete all patterns |
|---|
| 79 | delete __pattern_list; |
|---|
| 80 | |
|---|
| 81 | if ( __pattern_group_sequence ) { |
|---|
| 82 | for ( unsigned i = 0; i < __pattern_group_sequence->size(); ++i ) { |
|---|
| 83 | PatternList *pPatternList = ( *__pattern_group_sequence )[i]; |
|---|
| 84 | pPatternList->clear(); // pulisco tutto, i pattern non vanno distrutti qua |
|---|
| 85 | delete pPatternList; |
|---|
| 86 | } |
|---|
| 87 | delete __pattern_group_sequence; |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | delete __instrument_list; |
|---|
| 91 | |
|---|
| 92 | INFOLOG( QString( "DESTROY '%1'" ).arg( __name ) ); |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | void Song::purge_instrument( Instrument * I ) |
|---|
| 96 | { |
|---|
| 97 | for ( int nPattern = 0; nPattern < (int)__pattern_list->get_size(); ++nPattern ) { |
|---|
| 98 | __pattern_list->get( nPattern )->purge_instrument( I ); |
|---|
| 99 | } |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | |
|---|
| 103 | ///Load a song from file |
|---|
| 104 | Song* Song::load( const QString& filename ) |
|---|
| 105 | { |
|---|
| 106 | Song *song = NULL; |
|---|
| 107 | |
|---|
| 108 | SongReader reader; |
|---|
| 109 | song = reader.readSong( filename ); |
|---|
| 110 | |
|---|
| 111 | return song; |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | |
|---|
| 116 | /// Save a song to file |
|---|
| 117 | bool Song::save( const QString& filename ) |
|---|
| 118 | { |
|---|
| 119 | SongWriter writer; |
|---|
| 120 | writer.writeSong( this, filename ); |
|---|
| 121 | |
|---|
| 122 | return QFile::exists( filename ); |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | |
|---|
| 126 | |
|---|
| 127 | /// Return an empty song |
|---|
| 128 | Song* Song::get_empty_song() |
|---|
| 129 | { |
|---|
| 130 | QString dataDir = DataPath::get_data_path(); |
|---|
| 131 | QString filename = dataDir + "/DefaultSong.h2song"; |
|---|
| 132 | Song *song = Song::load( filename ); |
|---|
| 133 | |
|---|
| 134 | return song; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | |
|---|
| 138 | |
|---|
| 139 | void Song::set_swing_factor( float factor ) |
|---|
| 140 | { |
|---|
| 141 | if ( factor < 0.0 ) { |
|---|
| 142 | factor = 0.0; |
|---|
| 143 | } else if ( factor > 1.0 ) { |
|---|
| 144 | factor = 1.0; |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | __swing_factor = factor; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | //:::::::::::::::::::: |
|---|
| 153 | |
|---|
| 154 | |
|---|
| 155 | |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | |
|---|
| 159 | |
|---|
| 160 | //----------------------------------------------------------------------------- |
|---|
| 161 | // Implementation of SongReader class |
|---|
| 162 | //----------------------------------------------------------------------------- |
|---|
| 163 | |
|---|
| 164 | |
|---|
| 165 | SongReader::SongReader() |
|---|
| 166 | : Object( "SongReader" ) |
|---|
| 167 | { |
|---|
| 168 | // infoLog("init"); |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | |
|---|
| 173 | SongReader::~SongReader() |
|---|
| 174 | { |
|---|
| 175 | // infoLog("destroy"); |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | |
|---|
| 179 | |
|---|
| 180 | /// |
|---|
| 181 | /// Reads a song. |
|---|
| 182 | /// return NULL = error reading song file. |
|---|
| 183 | /// |
|---|
| 184 | Song* SongReader::readSong( const QString& filename ) |
|---|
| 185 | { |
|---|
| 186 | INFOLOG( filename ); |
|---|
| 187 | Song* song = NULL; |
|---|
| 188 | |
|---|
| 189 | if (QFile( filename ).exists() == false ) { |
|---|
| 190 | ERRORLOG( "Song file " + filename + " not found." ); |
|---|
| 191 | return NULL; |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | |
|---|
| 195 | #ifdef win32 |
|---|
| 196 | TiXmlDocument doc( filename.toAscii().constData() ); |
|---|
| 197 | #else |
|---|
| 198 | TiXmlDocument doc( filename.toUtf8().constData() ); |
|---|
| 199 | #endif |
|---|
| 200 | |
|---|
| 201 | |
|---|
| 202 | doc.LoadFile(); |
|---|
| 203 | |
|---|
| 204 | TiXmlNode* songNode; // root element |
|---|
| 205 | if ( !( songNode = doc.FirstChild( "song" ) ) ) { |
|---|
| 206 | ERRORLOG( "Error reading song: song node not found" ); |
|---|
| 207 | return NULL; |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | |
|---|
| 211 | m_sSongVersion = LocalFileMng::readXmlString( songNode, "version", "Unknown version" ); |
|---|
| 212 | if ( m_sSongVersion != QString( get_version().c_str() ) ) { |
|---|
| 213 | WARNINGLOG( "Trying to load a song created with a different version of hydrogen." ); |
|---|
| 214 | WARNINGLOG( "Song [" + filename + "] saved with version " + m_sSongVersion ); |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | float fBpm = LocalFileMng::readXmlFloat( songNode, "bpm", 120 ); |
|---|
| 218 | Hydrogen::get_instance()->setNewBpmJTM( fBpm ); |
|---|
| 219 | float fVolume = LocalFileMng::readXmlFloat( songNode, "volume", 0.5 ); |
|---|
| 220 | float fMetronomeVolume = LocalFileMng::readXmlFloat( songNode, "metronomeVolume", 0.5 ); |
|---|
| 221 | QString sName( LocalFileMng::readXmlString( songNode, "name", "Untitled Song" ) ); |
|---|
| 222 | QString sAuthor( LocalFileMng::readXmlString( songNode, "author", "Unknown Author" ) ); |
|---|
| 223 | QString sNotes( LocalFileMng::readXmlString( songNode, "notes", "..." ) ); |
|---|
| 224 | QString sLicense( LocalFileMng::readXmlString( songNode, "license", "Unknown license" ) ); |
|---|
| 225 | bool bLoopEnabled = LocalFileMng::readXmlBool( songNode, "loopEnabled", false ); |
|---|
| 226 | |
|---|
| 227 | Song::SongMode nMode = Song::PATTERN_MODE; // Mode (song/pattern) |
|---|
| 228 | QString sMode = LocalFileMng::readXmlString( songNode, "mode", "pattern" ); |
|---|
| 229 | if ( sMode == "song" ) { |
|---|
| 230 | nMode = Song::SONG_MODE; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | float fHumanizeTimeValue = LocalFileMng::readXmlFloat( songNode, "humanize_time", 0.0 ); |
|---|
| 234 | float fHumanizeVelocityValue = LocalFileMng::readXmlFloat( songNode, "humanize_velocity", 0.0 ); |
|---|
| 235 | float fSwingFactor = LocalFileMng::readXmlFloat( songNode, "swing_factor", 0.0 ); |
|---|
| 236 | |
|---|
| 237 | song = new Song( sName, sAuthor, fBpm, fVolume ); |
|---|
| 238 | song->set_metronome_volume( fMetronomeVolume ); |
|---|
| 239 | song->set_notes( sNotes ); |
|---|
| 240 | song->set_license( sLicense ); |
|---|
| 241 | song->set_loop_enabled( bLoopEnabled ); |
|---|
| 242 | song->set_mode( nMode ); |
|---|
| 243 | song->set_humanize_time_value( fHumanizeTimeValue ); |
|---|
| 244 | song->set_humanize_velocity_value( fHumanizeVelocityValue ); |
|---|
| 245 | song->set_swing_factor( fSwingFactor ); |
|---|
| 246 | |
|---|
| 247 | /* |
|---|
| 248 | song->m_bDelayFXEnabled = LocalFileMng::readXmlBool( songNode, "delayFXEnabled", false, false ); |
|---|
| 249 | song->m_fDelayFXWetLevel = LocalFileMng::readXmlFloat( songNode, "delayFXWetLevel", 1.0, false, false ); |
|---|
| 250 | song->m_fDelayFXFeedback= LocalFileMng::readXmlFloat( songNode, "delayFXFeedback", 0.4, false, false ); |
|---|
| 251 | song->m_nDelayFXTime = LocalFileMng::readXmlInt( songNode, "delayFXTime", MAX_NOTES / 4, false, false ); |
|---|
| 252 | */ |
|---|
| 253 | |
|---|
| 254 | |
|---|
| 255 | // Instrument List |
|---|
| 256 | LocalFileMng localFileMng; |
|---|
| 257 | InstrumentList *instrumentList = new InstrumentList(); |
|---|
| 258 | |
|---|
| 259 | TiXmlNode* instrumentListNode; |
|---|
| 260 | if ( ( instrumentListNode = songNode->FirstChild( "instrumentList" ) ) ) { |
|---|
| 261 | // INSTRUMENT NODE |
|---|
| 262 | int instrumentList_count = 0; |
|---|
| 263 | TiXmlNode* instrumentNode = 0; |
|---|
| 264 | for ( instrumentNode = instrumentListNode->FirstChild( "instrument" ); instrumentNode; instrumentNode = instrumentNode->NextSibling( "instrument" ) ) { |
|---|
| 265 | instrumentList_count++; |
|---|
| 266 | |
|---|
| 267 | QString sId = LocalFileMng::readXmlString( instrumentNode, "id", "" ); // instrument id |
|---|
| 268 | QString sDrumkit = LocalFileMng::readXmlString( instrumentNode, "drumkit", "" ); // drumkit |
|---|
| 269 | Hydrogen::get_instance()->setCurrentDrumkitname( sDrumkit ); |
|---|
| 270 | QString sName = LocalFileMng::readXmlString( instrumentNode, "name", "" ); // name |
|---|
| 271 | float fVolume = LocalFileMng::readXmlFloat( instrumentNode, "volume", 1.0 ); // volume |
|---|
| 272 | bool bIsMuted = LocalFileMng::readXmlBool( instrumentNode, "isMuted", false ); // is muted |
|---|
| 273 | float fPan_L = LocalFileMng::readXmlFloat( instrumentNode, "pan_L", 0.5 ); // pan L |
|---|
| 274 | float fPan_R = LocalFileMng::readXmlFloat( instrumentNode, "pan_R", 0.5 ); // pan R |
|---|
| 275 | float fFX1Level = LocalFileMng::readXmlFloat( instrumentNode, "FX1Level", 0.0 ); // FX level |
|---|
| 276 | float fFX2Level = LocalFileMng::readXmlFloat( instrumentNode, "FX2Level", 0.0 ); // FX level |
|---|
| 277 | float fFX3Level = LocalFileMng::readXmlFloat( instrumentNode, "FX3Level", 0.0 ); // FX level |
|---|
| 278 | float fFX4Level = LocalFileMng::readXmlFloat( instrumentNode, "FX4Level", 0.0 ); // FX level |
|---|
| 279 | float fGain = LocalFileMng::readXmlFloat( instrumentNode, "gain", 1.0, false, false ); // instrument gain |
|---|
| 280 | |
|---|
| 281 | int fAttack = LocalFileMng::readXmlInt( instrumentNode, "Attack", 0, false, false ); // Attack |
|---|
| 282 | int fDecay = LocalFileMng::readXmlInt( instrumentNode, "Decay", 0, false, false ); // Decay |
|---|
| 283 | float fSustain = LocalFileMng::readXmlFloat( instrumentNode, "Sustain", 1.0, false, false ); // Sustain |
|---|
| 284 | int fRelease = LocalFileMng::readXmlInt( instrumentNode, "Release", 1000, false, false ); // Release |
|---|
| 285 | |
|---|
| 286 | float fRandomPitchFactor = LocalFileMng::readXmlFloat( instrumentNode, "randomPitchFactor", 0.0f, false, false ); |
|---|
| 287 | |
|---|
| 288 | bool bFilterActive = LocalFileMng::readXmlBool( instrumentNode, "filterActive", false ); |
|---|
| 289 | float fFilterCutoff = LocalFileMng::readXmlFloat( instrumentNode, "filterCutoff", 1.0f, false ); |
|---|
| 290 | float fFilterResonance = LocalFileMng::readXmlFloat( instrumentNode, "filterResonance", 0.0f, false ); |
|---|
| 291 | QString sMuteGroup = LocalFileMng::readXmlString( instrumentNode, "muteGroup", "-1", false ); |
|---|
| 292 | int nMuteGroup = sMuteGroup.toInt(); |
|---|
| 293 | |
|---|
| 294 | |
|---|
| 295 | if ( sId == "" ) { |
|---|
| 296 | ERRORLOG( "Empty ID for instrument '" + sName + "'. skipping." ); |
|---|
| 297 | continue; |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | |
|---|
| 301 | // create a new instrument |
|---|
| 302 | Instrument *pInstrument = new Instrument( sId, sName, new ADSR( fAttack, fDecay, fSustain, fRelease ) ); |
|---|
| 303 | pInstrument->set_volume( fVolume ); |
|---|
| 304 | pInstrument->set_muted( bIsMuted ); |
|---|
| 305 | pInstrument->set_pan_l( fPan_L ); |
|---|
| 306 | pInstrument->set_pan_r( fPan_R ); |
|---|
| 307 | pInstrument->set_drumkit_name( sDrumkit ); |
|---|
| 308 | pInstrument->set_fx_level( fFX1Level, 0 ); |
|---|
| 309 | pInstrument->set_fx_level( fFX2Level, 1 ); |
|---|
| 310 | pInstrument->set_fx_level( fFX3Level, 2 ); |
|---|
| 311 | pInstrument->set_fx_level( fFX4Level, 3 ); |
|---|
| 312 | pInstrument->set_random_pitch_factor( fRandomPitchFactor ); |
|---|
| 313 | pInstrument->set_filter_active( bFilterActive ); |
|---|
| 314 | pInstrument->set_filter_cutoff( fFilterCutoff ); |
|---|
| 315 | pInstrument->set_filter_resonance( fFilterResonance ); |
|---|
| 316 | pInstrument->set_gain( fGain ); |
|---|
| 317 | pInstrument->set_mute_group( nMuteGroup ); |
|---|
| 318 | |
|---|
| 319 | QString drumkitPath = ""; |
|---|
| 320 | if ( ( sDrumkit != "" ) && ( sDrumkit != "-" ) ) { |
|---|
| 321 | // drumkitPath = localFileMng.getDrumkitDirectory( sDrumkit ) + sDrumkit + "/"; |
|---|
| 322 | drumkitPath = localFileMng.getDrumkitDirectory( sDrumkit ) + sDrumkit; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | // back compatibility code ( song version <= 0.9.0 ) |
|---|
| 326 | TiXmlNode* filenameNode = instrumentNode->FirstChild( "filename" ); |
|---|
| 327 | if ( filenameNode ) { |
|---|
| 328 | WARNINGLOG( "Using back compatibility code. filename node found" ); |
|---|
| 329 | QString sFilename = LocalFileMng::readXmlString( instrumentNode, "filename", "" ); |
|---|
| 330 | |
|---|
| 331 | if ( drumkitPath != "" ) { |
|---|
| 332 | sFilename = drumkitPath + "/" + sFilename; |
|---|
| 333 | } |
|---|
| 334 | Sample *pSample = Sample::load( sFilename ); |
|---|
| 335 | if ( pSample == NULL ) { |
|---|
| 336 | // nel passaggio tra 0.8.2 e 0.9.0 il drumkit di default e' cambiato. |
|---|
| 337 | // Se fallisce provo a caricare il corrispettivo file in formato flac |
|---|
| 338 | // warningLog( "[readSong] Error loading sample: " + sFilename + " not found. Trying to load a flac..." ); |
|---|
| 339 | sFilename = sFilename.left( sFilename.length() - 4 ); |
|---|
| 340 | sFilename += ".flac"; |
|---|
| 341 | pSample = Sample::load( sFilename ); |
|---|
| 342 | } |
|---|
| 343 | if ( pSample == NULL ) { |
|---|
| 344 | ERRORLOG( "Error loading sample: " + sFilename + " not found" ); |
|---|
| 345 | pInstrument->set_muted( true ); |
|---|
| 346 | } |
|---|
| 347 | InstrumentLayer *pLayer = new InstrumentLayer( pSample ); |
|---|
| 348 | pInstrument->set_layer( pLayer, 0 ); |
|---|
| 349 | } |
|---|
| 350 | //~ back compatibility code |
|---|
| 351 | else { |
|---|
| 352 | unsigned nLayer = 0; |
|---|
| 353 | for ( TiXmlNode* layerNode = instrumentNode->FirstChild( "layer" ); layerNode; layerNode = layerNode->NextSibling( "layer" ) ) { |
|---|
| 354 | if ( nLayer >= MAX_LAYERS ) { |
|---|
| 355 | ERRORLOG( "nLayer > MAX_LAYERS" ); |
|---|
| 356 | continue; |
|---|
| 357 | } |
|---|
| 358 | QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" ); |
|---|
| 359 | float fMin = LocalFileMng::readXmlFloat( layerNode, "min", 0.0 ); |
|---|
| 360 | float fMax = LocalFileMng::readXmlFloat( layerNode, "max", 1.0 ); |
|---|
| 361 | float fGain = LocalFileMng::readXmlFloat( layerNode, "gain", 1.0 ); |
|---|
| 362 | float fPitch = LocalFileMng::readXmlFloat( layerNode, "pitch", 0.0, false, false ); |
|---|
| 363 | |
|---|
| 364 | if ( drumkitPath != "" ) { |
|---|
| 365 | sFilename = drumkitPath + "/" + sFilename; |
|---|
| 366 | } |
|---|
| 367 | Sample *pSample = Sample::load( sFilename ); |
|---|
| 368 | if ( pSample == NULL ) { |
|---|
| 369 | ERRORLOG( "Error loading sample: " + sFilename + " not found" ); |
|---|
| 370 | pInstrument->set_muted( true ); |
|---|
| 371 | } |
|---|
| 372 | InstrumentLayer *pLayer = new InstrumentLayer( pSample ); |
|---|
| 373 | pLayer->set_start_velocity( fMin ); |
|---|
| 374 | pLayer->set_end_velocity( fMax ); |
|---|
| 375 | pLayer->set_gain( fGain ); |
|---|
| 376 | pLayer->set_pitch( fPitch ); |
|---|
| 377 | pInstrument->set_layer( pLayer, nLayer ); |
|---|
| 378 | nLayer++; |
|---|
| 379 | } |
|---|
| 380 | } |
|---|
| 381 | |
|---|
| 382 | instrumentList->add( pInstrument ); |
|---|
| 383 | } |
|---|
| 384 | if ( instrumentList_count == 0 ) { |
|---|
| 385 | WARNINGLOG( "0 instruments?" ); |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | song->set_instrument_list( instrumentList ); |
|---|
| 389 | } else { |
|---|
| 390 | ERRORLOG( "Error reading song: instrumentList node not found" ); |
|---|
| 391 | delete song; |
|---|
| 392 | return NULL; |
|---|
| 393 | } |
|---|
| 394 | |
|---|
| 395 | |
|---|
| 396 | |
|---|
| 397 | |
|---|
| 398 | |
|---|
| 399 | // Pattern list |
|---|
| 400 | TiXmlNode* patterns = songNode->FirstChild( "patternList" ); |
|---|
| 401 | |
|---|
| 402 | PatternList *patternList = new PatternList(); |
|---|
| 403 | int pattern_count = 0; |
|---|
| 404 | TiXmlNode* patternNode = 0; |
|---|
| 405 | for ( patternNode = patterns->FirstChild( "pattern" ); patternNode; patternNode = patternNode->NextSibling( "pattern" ) ) { |
|---|
| 406 | pattern_count++; |
|---|
| 407 | Pattern *pat = getPattern( patternNode, instrumentList ); |
|---|
| 408 | if ( pat ) { |
|---|
| 409 | patternList->add( pat ); |
|---|
| 410 | } else { |
|---|
| 411 | ERRORLOG( "Error loading pattern" ); |
|---|
| 412 | delete patternList; |
|---|
| 413 | delete song; |
|---|
| 414 | return NULL; |
|---|
| 415 | } |
|---|
| 416 | } |
|---|
| 417 | if ( pattern_count == 0 ) { |
|---|
| 418 | WARNINGLOG( "0 patterns?" ); |
|---|
| 419 | } |
|---|
| 420 | song->set_pattern_list( patternList ); |
|---|
| 421 | |
|---|
| 422 | |
|---|
| 423 | // Pattern sequence |
|---|
| 424 | TiXmlNode* patternSequenceNode = songNode->FirstChild( "patternSequence" ); |
|---|
| 425 | |
|---|
| 426 | std::vector<PatternList*>* pPatternGroupVector = new std::vector<PatternList*>; |
|---|
| 427 | |
|---|
| 428 | // back-compatibility code.. |
|---|
| 429 | for ( TiXmlNode* pPatternIDNode = patternSequenceNode->FirstChild( "patternID" ); pPatternIDNode; pPatternIDNode = pPatternIDNode->NextSibling( "patternID" ) ) { |
|---|
| 430 | WARNINGLOG( "Using old patternSequence code for back compatibility" ); |
|---|
| 431 | PatternList *patternSequence = new PatternList(); |
|---|
| 432 | QString patId = pPatternIDNode->FirstChild()->Value(); |
|---|
| 433 | |
|---|
| 434 | Pattern *pat = NULL; |
|---|
| 435 | for ( unsigned i = 0; i < patternList->get_size(); i++ ) { |
|---|
| 436 | Pattern *tmp = patternList->get( i ); |
|---|
| 437 | if ( tmp ) { |
|---|
| 438 | if ( tmp->get_name() == patId ) { |
|---|
| 439 | pat = tmp; |
|---|
| 440 | break; |
|---|
| 441 | } |
|---|
| 442 | } |
|---|
| 443 | } |
|---|
| 444 | if ( pat == NULL ) { |
|---|
| 445 | WARNINGLOG( "patternid not found in patternSequence" ); |
|---|
| 446 | continue; |
|---|
| 447 | } |
|---|
| 448 | patternSequence->add( pat ); |
|---|
| 449 | |
|---|
| 450 | pPatternGroupVector->push_back( patternSequence ); |
|---|
| 451 | } |
|---|
| 452 | |
|---|
| 453 | for ( TiXmlNode* groupNode = patternSequenceNode->FirstChild( "group" ); groupNode; groupNode = groupNode->NextSibling( "group" ) ) { |
|---|
| 454 | PatternList *patternSequence = new PatternList(); |
|---|
| 455 | for ( TiXmlNode* patternId = groupNode->FirstChild( "patternID" ); patternId; patternId = patternId->NextSibling( "patternID" ) ) { |
|---|
| 456 | QString patId = patternId->FirstChild()->Value(); |
|---|
| 457 | |
|---|
| 458 | Pattern *pat = NULL; |
|---|
| 459 | for ( unsigned i = 0; i < patternList->get_size(); i++ ) { |
|---|
| 460 | Pattern *tmp = patternList->get( i ); |
|---|
| 461 | if ( tmp ) { |
|---|
| 462 | if ( tmp->get_name() == patId ) { |
|---|
| 463 | pat = tmp; |
|---|
| 464 | break; |
|---|
| 465 | } |
|---|
| 466 | } |
|---|
| 467 | } |
|---|
| 468 | if ( pat == NULL ) { |
|---|
| 469 | WARNINGLOG( "patternid not found in patternSequence" ); |
|---|
| 470 | continue; |
|---|
| 471 | } |
|---|
| 472 | patternSequence->add( pat ); |
|---|
| 473 | } |
|---|
| 474 | pPatternGroupVector->push_back( patternSequence ); |
|---|
| 475 | } |
|---|
| 476 | |
|---|
| 477 | song->set_pattern_group_vector( pPatternGroupVector ); |
|---|
| 478 | |
|---|
| 479 | #ifdef LADSPA_SUPPORT |
|---|
| 480 | // reset FX |
|---|
| 481 | for ( int fx = 0; fx < MAX_FX; ++fx ) { |
|---|
| 482 | //LadspaFX* pFX = Effects::getInstance()->getLadspaFX( fx ); |
|---|
| 483 | //delete pFX; |
|---|
| 484 | Effects::getInstance()->setLadspaFX( NULL, fx ); |
|---|
| 485 | } |
|---|
| 486 | #endif |
|---|
| 487 | |
|---|
| 488 | // LADSPA FX |
|---|
| 489 | TiXmlNode* ladspaNode = songNode->FirstChild( "ladspa" ); |
|---|
| 490 | if ( ladspaNode ) { |
|---|
| 491 | int nFX = 0; |
|---|
| 492 | TiXmlNode* fxNode; |
|---|
| 493 | for ( fxNode = ladspaNode->FirstChild( "fx" ); fxNode; fxNode = fxNode->NextSibling( "fx" ) ) { |
|---|
| 494 | QString sName = LocalFileMng::readXmlString( fxNode, "name", "" ); |
|---|
| 495 | QString sFilename = LocalFileMng::readXmlString( fxNode, "filename", "" ); |
|---|
| 496 | bool bEnabled = LocalFileMng::readXmlBool( fxNode, "enabled", false ); |
|---|
| 497 | float fVolume = LocalFileMng::readXmlFloat( fxNode, "volume", 1.0 ); |
|---|
| 498 | |
|---|
| 499 | if ( sName != "no plugin" ) { |
|---|
| 500 | // FIXME: il caricamento va fatto fare all'engine, solo lui sa il samplerate esatto |
|---|
| 501 | #ifdef LADSPA_SUPPORT |
|---|
| 502 | LadspaFX* pFX = LadspaFX::load( sFilename, sName, 44100 ); |
|---|
| 503 | Effects::getInstance()->setLadspaFX( pFX, nFX ); |
|---|
| 504 | if ( pFX ) { |
|---|
| 505 | pFX->setEnabled( bEnabled ); |
|---|
| 506 | pFX->setVolume( fVolume ); |
|---|
| 507 | TiXmlNode* inputControlNode; |
|---|
| 508 | for ( inputControlNode = fxNode->FirstChild( "inputControlPort" ); inputControlNode; inputControlNode = inputControlNode->NextSibling( "inputControlPort" ) ) { |
|---|
| 509 | QString sName = LocalFileMng::readXmlString( inputControlNode, "name", "" ); |
|---|
| 510 | float fValue = LocalFileMng::readXmlFloat( inputControlNode, "value", 0.0 ); |
|---|
| 511 | |
|---|
| 512 | for ( unsigned nPort = 0; nPort < pFX->inputControlPorts.size(); nPort++ ) { |
|---|
| 513 | LadspaControlPort *port = pFX->inputControlPorts[ nPort ]; |
|---|
| 514 | if ( QString( port->sName ) == sName ) { |
|---|
| 515 | port->fControlValue = fValue; |
|---|
| 516 | } |
|---|
| 517 | } |
|---|
| 518 | } |
|---|
| 519 | |
|---|
| 520 | TiXmlNode* outputControlNode; |
|---|
| 521 | for ( outputControlNode = fxNode->FirstChild( "outputControlPort" ); outputControlNode; outputControlNode = outputControlNode->NextSibling( "outputControlPort" ) ) { |
|---|
| 522 | } |
|---|
| 523 | } |
|---|
| 524 | #endif |
|---|
| 525 | } |
|---|
| 526 | nFX++; |
|---|
| 527 | } |
|---|
| 528 | } else { |
|---|
| 529 | WARNINGLOG( "ladspa node not found" ); |
|---|
| 530 | } |
|---|
| 531 | |
|---|
| 532 | |
|---|
| 533 | song->__is_modified = false; |
|---|
| 534 | song->set_filename( filename ); |
|---|
| 535 | |
|---|
| 536 | return song; |
|---|
| 537 | } |
|---|
| 538 | |
|---|
| 539 | |
|---|
| 540 | |
|---|
| 541 | Pattern* SongReader::getPattern( TiXmlNode* pattern, InstrumentList* instrList ) |
|---|
| 542 | { |
|---|
| 543 | Pattern *pPattern = NULL; |
|---|
| 544 | |
|---|
| 545 | QString sName = ""; // name |
|---|
| 546 | sName = LocalFileMng::readXmlString( pattern, "name", sName ); |
|---|
| 547 | |
|---|
| 548 | QString sCategory = ""; // category |
|---|
| 549 | sCategory = LocalFileMng::readXmlString( pattern, "category", sCategory ); |
|---|
| 550 | int nSize = -1; |
|---|
| 551 | nSize = LocalFileMng::readXmlInt( pattern, "size", nSize, false, false ); |
|---|
| 552 | |
|---|
| 553 | pPattern = new Pattern( sName, sCategory, nSize ); |
|---|
| 554 | |
|---|
| 555 | |
|---|
| 556 | |
|---|
| 557 | TiXmlNode* pNoteListNode = pattern->FirstChild( "noteList" ); |
|---|
| 558 | if ( pNoteListNode ) { |
|---|
| 559 | // new code :) |
|---|
| 560 | for ( TiXmlNode* noteNode = pNoteListNode->FirstChild( "note" ); noteNode; noteNode = noteNode->NextSibling( "note" ) ) { |
|---|
| 561 | |
|---|
| 562 | Note* pNote = NULL; |
|---|
| 563 | |
|---|
| 564 | unsigned nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 ); |
|---|
| 565 | float fLeadLag = LocalFileMng::readXmlFloat( noteNode, "leadlag", 0.0 ); |
|---|
| 566 | float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f ); |
|---|
| 567 | float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 ); |
|---|
| 568 | float fPan_R = LocalFileMng::readXmlFloat( noteNode, "pan_R", 0.5 ); |
|---|
| 569 | int nLength = LocalFileMng::readXmlInt( noteNode, "length", -1, true ); |
|---|
| 570 | float nPitch = LocalFileMng::readXmlFloat( noteNode, "pitch", 0.0, false, false ); |
|---|
| 571 | QString sKey = LocalFileMng::readXmlString( noteNode, "key", "C0", false, false ); |
|---|
| 572 | |
|---|
| 573 | QString instrId = LocalFileMng::readXmlString( noteNode, "instrument", "" ); |
|---|
| 574 | |
|---|
| 575 | Instrument *instrRef = NULL; |
|---|
| 576 | // search instrument by ref |
|---|
| 577 | for ( unsigned i = 0; i < instrList->get_size(); i++ ) { |
|---|
| 578 | Instrument *instr = instrList->get( i ); |
|---|
| 579 | if ( instrId == instr->get_id() ) { |
|---|
| 580 | instrRef = instr; |
|---|
| 581 | break; |
|---|
| 582 | } |
|---|
| 583 | } |
|---|
| 584 | if ( !instrRef ) { |
|---|
| 585 | ERRORLOG( "Instrument with ID: '" + instrId + "' not found. Note skipped." ); |
|---|
| 586 | continue; |
|---|
| 587 | } |
|---|
| 588 | //assert( instrRef ); |
|---|
| 589 | |
|---|
| 590 | pNote = new Note( instrRef, nPosition, fVelocity, fPan_L, fPan_R, nLength, nPitch, Note::stringToKey( sKey ) ); |
|---|
| 591 | pNote->set_leadlag(fLeadLag); |
|---|
| 592 | pPattern->note_map.insert( std::make_pair( pNote->get_position(), pNote ) ); |
|---|
| 593 | } |
|---|
| 594 | } else { |
|---|
| 595 | // Back compatibility code. Version < 0.9.4 |
|---|
| 596 | TiXmlNode* sequenceListNode = pattern->FirstChild( "sequenceList" ); |
|---|
| 597 | |
|---|
| 598 | int sequence_count = 0; |
|---|
| 599 | TiXmlNode* sequenceNode = 0; |
|---|
| 600 | for ( sequenceNode = sequenceListNode->FirstChild( "sequence" ); sequenceNode; sequenceNode = sequenceNode->NextSibling( "sequence" ) ) { |
|---|
| 601 | sequence_count++; |
|---|
| 602 | |
|---|
| 603 | TiXmlNode* noteListNode = sequenceNode->FirstChild( "noteList" ); |
|---|
| 604 | for ( TiXmlNode* noteNode = noteListNode->FirstChild( "note" ); noteNode; noteNode = noteNode->NextSibling( "note" ) ) { |
|---|
| 605 | |
|---|
| 606 | Note* pNote = NULL; |
|---|
| 607 | |
|---|
| 608 | unsigned nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 ); |
|---|
| 609 | float fLeadLag = LocalFileMng::readXmlFloat( noteNode, "leadlag", 0.0 ); |
|---|
| 610 | float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f ); |
|---|
| 611 | float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 ); |
|---|
| 612 | float fPan_R = LocalFileMng::readXmlFloat( noteNode, "pan_R", 0.5 ); |
|---|
| 613 | int nLength = LocalFileMng::readXmlInt( noteNode, "length", -1, true ); |
|---|
| 614 | float nPitch = LocalFileMng::readXmlFloat( noteNode, "pitch", 0.0, false, false ); |
|---|
| 615 | |
|---|
| 616 | QString instrId = LocalFileMng::readXmlString( noteNode, "instrument", "" ); |
|---|
| 617 | |
|---|
| 618 | Instrument *instrRef = NULL; |
|---|
| 619 | // search instrument by ref |
|---|
| 620 | for ( unsigned i = 0; i < instrList->get_size(); i++ ) { |
|---|
| 621 | Instrument *instr = instrList->get( i ); |
|---|
| 622 | if ( instrId == instr->get_id() ) { |
|---|
| 623 | instrRef = instr; |
|---|
| 624 | break; |
|---|
| 625 | } |
|---|
| 626 | } |
|---|
| 627 | assert( instrRef ); |
|---|
| 628 | |
|---|
| 629 | pNote = new Note( instrRef, nPosition, fVelocity, fPan_L, fPan_R, nLength, nPitch ); |
|---|
| 630 | pNote->set_leadlag(fLeadLag); |
|---|
| 631 | |
|---|
| 632 | //infoLog( "new note!! pos: " + toString( pNote->m_nPosition ) + "\t instr: " + instrId ); |
|---|
| 633 | pPattern->note_map.insert( std::make_pair( pNote->get_position(), pNote ) ); |
|---|
| 634 | } |
|---|
| 635 | } |
|---|
| 636 | } |
|---|
| 637 | |
|---|
| 638 | return pPattern; |
|---|
| 639 | } |
|---|
| 640 | |
|---|
| 641 | }; |
|---|
| 642 | |
|---|
| 643 | |
|---|
| 644 | |
|---|
| 645 | |
|---|