Files
lmms/plugins/flp_import/FlpImport.cpp
Tobias Doerffel 9fd3dacb67 Fixed spelling errors
Fixed some spelling errors found by Lintian while checking the Debian
package of LMMS. Thanks to Patrick Winnertz for pointing out this issue.
2010-01-25 23:24:42 +01:00

2043 lines
47 KiB
C++

/*
* flp_import.cpp - support for importing FLP-files
*
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include <QtXml/QDomDocument>
#include <QtGui/QApplication>
#include <QtGui/QProgressDialog>
#include <QtCore/QDir>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include "FlpImport.h"
#include "note_play_handle.h"
#include "automation_pattern.h"
#include "basic_filters.h"
#include "bb_track.h"
#include "bb_track_container.h"
#include "combobox.h"
#include "config_mgr.h"
#include "debug.h"
#include "Effect.h"
#include "engine.h"
#include "FxMixer.h"
#include "FxMixerView.h"
#include "group_box.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
#include "EnvelopeAndLfoParameters.h"
#include "knob.h"
#include "Oscillator.h"
#include "pattern.h"
#include "Piano.h"
#include "ProjectJournal.h"
#include "project_notes.h"
#include "song.h"
#include "track_container.h"
#include "embed.h"
#include "lmmsconfig.h"
#ifdef LMMS_HAVE_ZIP
#include <QDir>
#include <QTemporaryFile>
#include <unistd.h>
#include <zip.h>
#endif
#ifdef LMMS_HAVE_CTYPE_H
#include <ctype.h>
#endif
#define makeID(_c0, _c1, _c2, _c3) \
( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) )
extern "C"
{
Plugin::Descriptor PLUGIN_EXPORT flpimport_plugin_descriptor =
{
STRINGIFY( PLUGIN_NAME ),
"FLP Import",
QT_TRANSLATE_NOOP( "pluginBrowser",
"Filter for importing FL Studio projects into LMMS" ),
"Tobias Doerffel <tobydox/at/users/dot/sf/dot/net>",
0x0100,
Plugin::ImportFilter,
NULL,
NULL,
NULL
} ;
// unrtf-stuff
#include "defs.h"
#include "main.h"
#include "html.h"
#include "word.h"
#include "hash.h"
#include "convert.h"
#include "attr.h"
extern OutputPersonality * op;
extern int lineno;
extern QString outstring;
}
const int NumFLFxChannels = 64;
static void dump_mem( const void * buffer, uint n_bytes )
{
uchar * cp = (uchar *) buffer;
for( uint k = 0; k < n_bytes; ++k )
{
qDebug( "%02x ", (unsigned int)cp[k] );//( cp[k] > 31 || cp[k] < 7 ) ? cp[k] : '.' );
}
qDebug( "\n" );
}
enum FLP_Events
{
// BYTE EVENTS
FLP_Byte = 0,
FLP_Enabled = 0,
FLP_NoteOn = 1, //+pos (byte)
FLP_Vol = 2,
FLP_Pan = 3,
FLP_MIDIChan = 4,
FLP_MIDINote = 5,
FLP_MIDIPatch = 6,
FLP_MIDIBank = 7,
FLP_LoopActive = 9,
FLP_ShowInfo = 10,
FLP_Shuffle = 11,
FLP_MainVol = 12,
FLP_Stretch = 13, // old byte version
FLP_Pitchable = 14,
FLP_Zipped = 15,
FLP_Delay_Flags = 16,
FLP_PatLength = 17,
FLP_BlockLength = 18,
FLP_UseLoopPoints = 19,
FLP_LoopType = 20,
FLP_ChanType = 21,
FLP_MixSliceNum = 22,
FLP_EffectChannelMuted = 27,
// WORD EVENTS
FLP_Word = 64,
FLP_NewChan = FLP_Word,
FLP_NewPat = FLP_Word + 1, //+PatNum (word)
FLP_Tempo = FLP_Word + 2,
FLP_CurrentPatNum = FLP_Word + 3,
FLP_PatData = FLP_Word + 4,
FLP_FX = FLP_Word + 5,
FLP_Fade_Stereo = FLP_Word + 6,
FLP_CutOff = FLP_Word + 7,
FLP_DotVol = FLP_Word + 8,
FLP_DotPan = FLP_Word + 9,
FLP_PreAmp = FLP_Word + 10,
FLP_Decay = FLP_Word + 11,
FLP_Attack = FLP_Word + 12,
FLP_DotNote = FLP_Word + 13,
FLP_DotPitch = FLP_Word + 14,
FLP_DotMix = FLP_Word + 15,
FLP_MainPitch = FLP_Word + 16,
FLP_RandChan = FLP_Word + 17,
FLP_MixChan = FLP_Word + 18,
FLP_Resonance = FLP_Word + 19,
FLP_LoopBar = FLP_Word + 20,
FLP_StDel = FLP_Word + 21,
FLP_FX3 = FLP_Word + 22,
FLP_DotReso = FLP_Word + 23,
FLP_DotCutOff = FLP_Word + 24,
FLP_ShiftDelay = FLP_Word + 25,
FLP_LoopEndBar = FLP_Word + 26,
FLP_Dot = FLP_Word + 27,
FLP_DotShift = FLP_Word + 28,
FLP_LayerChans = FLP_Word + 30,
// DWORD EVENTS
FLP_Int = 128,
FLP_Color = FLP_Int,
FLP_PlayListItem = FLP_Int + 1, //+Pos (word) +PatNum (word)
FLP_Echo = FLP_Int + 2,
FLP_FXSine = FLP_Int + 3,
FLP_CutCutBy = FLP_Int + 4,
FLP_WindowH = FLP_Int + 5,
FLP_MiddleNote = FLP_Int + 7,
FLP_Reserved = FLP_Int + 8, // may contain an invalid
// version info
FLP_MainResoCutOff = FLP_Int + 9,
FLP_DelayReso = FLP_Int + 10,
FLP_Reverb = FLP_Int + 11,
FLP_IntStretch = FLP_Int + 12,
FLP_SSNote = FLP_Int + 13,
FLP_FineTune = FLP_Int + 14,
// TEXT EVENTS
FLP_Undef = 192, //+Size (var length)
FLP_Text = FLP_Undef, //+Size (var length)+Text
// (Null Term. String)
FLP_Text_ChanName = FLP_Text, // name for the current channel
FLP_Text_PatName = FLP_Text + 1, // name for the current pattern
FLP_Text_Title = FLP_Text + 2, // title of the loop
FLP_Text_Comment = FLP_Text + 3, // old comments in text format.
// Not used anymore
FLP_Text_SampleFileName = FLP_Text + 4, // filename for the sample in
// the current channel, stored
// as relative path
FLP_Text_URL = FLP_Text + 5,
FLP_Text_CommentRTF = FLP_Text + 6, // new comments in Rich Text
// format
FLP_Text_Version = FLP_Text + 7,
FLP_Text_PluginName = FLP_Text + 9, // plugin file name
// (without path)
FLP_Text_EffectChanName = FLP_Text + 12,
FLP_Text_MIDICtrls = FLP_Text + 16,
FLP_Text_Delay = FLP_Text + 17,
FLP_Text_TS404Params = FLP_Text + 18,
FLP_Text_DelayLine = FLP_Text + 19,
FLP_Text_NewPlugin = FLP_Text + 20,
FLP_Text_PluginParams = FLP_Text + 21,
FLP_Text_ChanParams = FLP_Text + 23,// block of various channel
// params (can grow)
FLP_Text_EnvLfoParams = FLP_Text + 26,
FLP_Text_BasicChanParams= FLP_Text + 27,
FLP_Text_OldFilterParams= FLP_Text + 28,
FLP_Text_AutomationData = FLP_Text + 31,
FLP_Text_PatternNotes = FLP_Text + 32,
FLP_Text_ChanGroupName = FLP_Text + 39,
FLP_Text_PlayListItems = FLP_Text + 41,
FLP_CmdCount
} ;
struct FL_Automation
{
FL_Automation() :
pos( 0 ),
value( 0 ),
channel( 0 ),
control( 0 )
{
}
enum Controls
{
ControlVolume = 0,
ControlPanning = 1,
ControlFilterCut = 2,
ControlFilterRes = 3,
ControlPitch = 4,
ControlFilterType = 5,
ControlFXChannel = 8,
ControlVolPredelay = 4354,
ControlVolAttack,
ControlVolHold,
ControlVolDecay,
ControlVolSustain,
ControlVolRelease,
ControlVolLfoPredelay = ControlVolPredelay+7,
ControlVolLfoAttack,
ControlVolLfoAmount,
ControlVolLfoSpeed,
ControlVolAttackTension = ControlVolPredelay+12,
ControlVolDecayTension,
ControlVolReleaseTension,
ControlCutPredelay = 4610,
ControlCutAttack,
ControlCutHold,
ControlCutDecay,
ControlCutSustain,
ControlCutRelease,
ControlCutAmount,
ControlCutLfoPredelay = ControlCutPredelay+7,
ControlCutLfoAttack,
ControlCutLfoAmount,
ControlCutLfoSpeed,
ControlCutAttackTension = ControlCutPredelay+12,
ControlCutDecayTension,
ControlCutReleaseTension,
ControlResPredelay = 4866,
ControlResAttack,
ControlResHold,
ControlResDecay,
ControlResSustain,
ControlResRelease,
ControlResAmount,
ControlResLfoPredelay = ControlResPredelay+7,
ControlResLfoAttack,
ControlResLfoAmount,
ControlResLfoSpeed,
ControlResAttackTension = ControlResPredelay+12,
ControlResDecayTension,
ControlResReleaseTension
} ;
int pos;
int value;
int channel;
int control;
} ;
struct FL_Channel_Envelope
{
InstrumentSoundShaping::Targets target;
float predelay;
float attack;
float hold;
float decay;
float sustain;
float release;
float amount;
} ;
struct FL_Plugin
{
enum PluginTypes
{
UnknownPlugin,
InstrumentPlugin,
Sampler,
TS404,
Fruity_3x_Osc,
Layer,
BeepMap,
BuzzGeneratorAdapter,
FruitKick,
FruityDrumSynthLive,
FruityDX10,
FruityGranulizer,
FruitySlicer,
FruitySoundfontPlayer,
FruityVibrator,
MidiOut,
Plucked,
SimSynth,
Sytrus,
WASP,
EffectPlugin,
Fruity7BandEq,
FruityBalance,
FruityBassBoost,
FruityBigClock,
FruityBloodOverdrive,
FruityCenter,
FruityChorus,
FruityCompressor,
FruityDbMeter,
FruityDelay,
FruityDelay2,
FruityFastDist,
FruityFastLP,
FruityFilter,
FruityFlanger,
FruityFormulaController,
FruityFreeFilter,
FruityHTMLNotebook,
FruityLSD,
FruityMute2,
FruityNotebook,
FruityPanOMatic,
FruityParametricEQ,
FruityPeakController,
FruityPhaseInverter,
FruityPhaser,
FruityReeverb,
FruityScratcher,
FruitySend,
FruitySoftClipper,
FruitySpectroman,
FruityStereoEnhancer,
FruityXYController
} ;
FL_Plugin( PluginTypes _pt = UnknownPlugin ) :
pluginType( _pt ),
name(),
pluginSettings( NULL ),
pluginSettingsLength( 0 )
{
}
~FL_Plugin()
{
delete[] pluginSettings;
}
PluginTypes pluginType;
QString name;
char * pluginSettings;
int pluginSettingsLength;
} ;
struct FL_Channel : public FL_Plugin
{
QList<FL_Automation> automationData;
int volume;
int panning;
int baseNote;
int fxChannel;
int layerParent;
typedef QList<QPair<int, note> > noteVector;
noteVector notes;
QList<int> dots;
QString sampleFileName;
int sampleAmp;
bool sampleReversed;
bool sampleReverseStereo;
bool sampleUseLoopPoints;
Instrument * instrumentPlugin;
QList<FL_Channel_Envelope> envelopes;
int filterType;
float filterCut;
float filterRes;
bool filterEnabled;
int arpDir;
int arpRange;
int selectedArp;
float arpTime;
float arpGate;
bool arpEnabled;
QRgb color;
FL_Channel( PluginTypes _pt = UnknownPlugin ) :
FL_Plugin( _pt ),
automationData(),
volume( DefaultVolume ),
panning( DefaultPanning ),
baseNote( DefaultKey ),
fxChannel( 0 ),
layerParent( -1 ),
notes(),
dots(),
sampleFileName(),
sampleAmp( 100 ),
sampleReversed( false ),
sampleReverseStereo( false ),
sampleUseLoopPoints( false ),
instrumentPlugin( NULL ),
envelopes(),
filterType( basicFilters<>::LowPass ),
filterCut( 10000 ),
filterRes( 0.1 ),
filterEnabled( false ),
arpDir( Arpeggiator::ArpDirUp ),
arpRange( 0 ),
selectedArp( 0 ),
arpTime( 100 ),
arpGate( 100 ),
arpEnabled( false ),
color( qRgb( 64, 128, 255 ) )
{
}
} ;
struct FL_Effect : public FL_Plugin
{
FL_Effect( PluginTypes _pt = UnknownPlugin ) :
FL_Plugin( _pt ),
fxChannel( 0 ),
fxPos( 0 )
{
}
int fxChannel;
int fxPos;
} ;
struct FL_PlayListItem
{
FL_PlayListItem() :
position( 0 ),
length( 1 ),
pattern( 0 )
{
}
int position;
int length;
int pattern;
} ;
struct FL_EffectChannel
{
FL_EffectChannel() :
name(),
volume( DefaultVolume ),
isMuted( false )
{
}
QString name;
int volume;
bool isMuted;
} ;
struct FL_Project
{
int mainVolume;
int mainPitch;
bpm_t tempo;
int numChannels;
QList<FL_Channel> channels;
QList<FL_Effect> effects;
QList<FL_PlayListItem> playListItems;
QMap<int, QString> patternNames;
int maxPatterns;
int currentPattern;
int activeEditPattern;
FL_EffectChannel effectChannels[NumFLFxChannels+1];
int currentEffectChannel;
QString projectNotes;
QString projectTitle;
QString versionString;
int version;
int versionSpecificFactor;
FL_Project() :
mainVolume( DefaultVolume ),
mainPitch( 0 ),
tempo( DefaultTempo ),
numChannels( 0 ),
channels(),
effects(),
playListItems(),
patternNames(),
maxPatterns( 0 ),
currentPattern( 0 ),
activeEditPattern( 0 ),
effectChannels(),
currentEffectChannel( -1 ),
projectNotes(),
projectTitle(),
versionString(),
version( 0x100 ),
versionSpecificFactor( 1 )
{
}
} ;
FlpImport::FlpImport( const QString & _file ) :
ImportFilter( _file, &flpimport_plugin_descriptor )
{
m_fileBase = QFileInfo( _file ).path();
}
FlpImport::~FlpImport()
{
}
bool FlpImport::tryImport( trackContainer * _tc )
{
#ifdef LMMS_HAVE_ZIP
return tryFLPImport( _tc ) || tryZIPImport( _tc );
#else
return tryFLPImport( _tc );
#endif
}
#ifdef LMMS_HAVE_ZIP
bool FlpImport::tryZIPImport( trackContainer * _tc )
{
// see if the file is a zip file
closeFile();
const QFile &thefile = file();
int err = 0;
struct zip *zfile = zip_open(qPrintable(thefile.fileName()), 0, &err);
if( zfile == NULL )
{
if( err == ZIP_ER_NOZIP )
{
printf("flp import (zip): not a valid zip file\n");
}
else if( err == ZIP_ER_OPEN )
{
printf("flp import (zip): error opening file\n");
}
else if( err == ZIP_ER_READ || err == ZIP_ER_SEEK )
{
printf("flp import (zip): error reading file\n");
}
else
{
printf("flp import (zip): unknown error opening zip file\n");
}
return false;
}
// valid zip file
// get temporary directory
QString tmpName;
{
QTemporaryFile qtmp;
if( qtmp.open() )
{
tmpName = qtmp.fileName();
}
else
{
zip_close(zfile);
printf("flp import (zip): error getting temporary folder\n");
return false;
}
}
QDir tmpDir = QDir::temp();
tmpDir.mkdir(tmpName);
tmpDir.cd(tmpName);
// unzip everything to the temp folder
int buf_size = getpagesize();
char buf[buf_size];
int numFiles = zip_get_num_files(zfile);
int i;
bool foundFLP = false;
QString flpFile = "";
for( i=0; i<numFiles; ++i )
{
const char* fileName = zip_get_name(zfile, i, 0);
if( fileName != NULL )
{
struct zip_file *unzipFile = zip_fopen_index(zfile, i, 0);
if( unzipFile != NULL )
{
// we have a handle to read, now get a handle to the outfile
QFile out(tmpDir.absolutePath() + QDir::separator() +
fileName);
out.open(QIODevice::WriteOnly);
// ok we have handles on both, now do buffered writing
bool readErr = false;
while(true)
{
// read into buffer
int numRead = zip_fread(unzipFile, buf, buf_size);
if( numRead == -1 )
{
readErr = true;
printf("flp import (zip): error while reading %s "
"from zip file\n", fileName);
break;
}
out.write( buf, numRead );
if( numRead < buf_size ) break;
}
// we successfully read this file, check if it is
// the .flp file
QString qFileName(fileName);
if( qFileName.endsWith(".flp") )
{
foundFLP = true;
flpFile = out.fileName();
}
// clean up
out.close();
zip_fclose(unzipFile);
}
else
{
printf("flp import (zip): unable to get %s out of %s\n",
fileName, qPrintable(thefile.fileName()) );
}
}
}
zip_close(zfile);
// make sure there was a .flp file in the archive
if( ! foundFLP )
{
printf("flp import (zip): no .flp file found in archive\n");
return false;
}
// use the flp_import plugin to load the .flp file
// which was extracted to the temp dir
setFile(flpFile);
m_fileBase = QFileInfo( flpFile ).path();
return tryFLPImport( _tc );
}
#endif
bool FlpImport::tryFLPImport( trackContainer * _tc )
{
const int mappedFilter[] =
{
basicFilters<>::LowPass,// fast LP
basicFilters<>::LowPass,
basicFilters<>::BandPass_CSG,
basicFilters<>::HiPass,
basicFilters<>::Notch,
basicFilters<>::NumFilters+basicFilters<>::LowPass,
basicFilters<>::LowPass,
basicFilters<>::NumFilters+basicFilters<>::LowPass
} ;
const Arpeggiator::ArpDirections mappedArpDir[] =
{
Arpeggiator::ArpDirUp,
Arpeggiator::ArpDirUp,
Arpeggiator::ArpDirDown,
Arpeggiator::ArpDirUpAndDown,
Arpeggiator::ArpDirUpAndDown,
Arpeggiator::ArpDirRandom
} ;
QMap<QString, int> mappedPluginTypes;
// instruments
mappedPluginTypes["sampler"] = FL_Plugin::Sampler;
mappedPluginTypes["ts404"] = FL_Plugin::TS404;
mappedPluginTypes["3x osc"] = FL_Plugin::Fruity_3x_Osc;
mappedPluginTypes["beepmap"] = FL_Plugin::BeepMap;
mappedPluginTypes["buzz generator adapter"] = FL_Plugin::BuzzGeneratorAdapter;
mappedPluginTypes["fruit kick"] = FL_Plugin::FruitKick;
mappedPluginTypes["fruity drumsynth live"] = FL_Plugin::FruityDrumSynthLive;
mappedPluginTypes["fruity dx10"] = FL_Plugin::FruityDX10;
mappedPluginTypes["fruity granulizer"] = FL_Plugin::FruityGranulizer;
mappedPluginTypes["fruity slicer"] = FL_Plugin::FruitySlicer;
mappedPluginTypes["fruity soundfont player"] = FL_Plugin::FruitySoundfontPlayer;
mappedPluginTypes["fruity vibrator"] = FL_Plugin::FruityVibrator;
mappedPluginTypes["midi out"] = FL_Plugin::MidiOut;
mappedPluginTypes["plucked!"] = FL_Plugin::Plucked;
mappedPluginTypes["simsynth"] = FL_Plugin::SimSynth;
mappedPluginTypes["sytrus"] = FL_Plugin::Sytrus;
mappedPluginTypes["wasp"] = FL_Plugin::WASP;
// effects
mappedPluginTypes["fruity 7 band EQ"] = FL_Plugin::Fruity7BandEq;
mappedPluginTypes["fruity balance"] = FL_Plugin::FruityBalance;
mappedPluginTypes["fruity bass boost"] = FL_Plugin::FruityBassBoost;
mappedPluginTypes["fruity big clock"] = FL_Plugin::FruityBigClock;
mappedPluginTypes["fruity blood overdrive"] = FL_Plugin::FruityBloodOverdrive;
mappedPluginTypes["fruity center"] = FL_Plugin::FruityCenter;
mappedPluginTypes["fruity chorus"] = FL_Plugin::FruityChorus;
mappedPluginTypes["fruity compressor"] = FL_Plugin::FruityCompressor;
mappedPluginTypes["fruity db meter"] = FL_Plugin::FruityDbMeter;
mappedPluginTypes["fruity delay"] = FL_Plugin::FruityDelay;
mappedPluginTypes["fruity delay 2"] = FL_Plugin::FruityDelay2;
mappedPluginTypes["fruity fast dist"] = FL_Plugin::FruityFastDist;
mappedPluginTypes["fruity fast lp"] = FL_Plugin::FruityFastLP;
mappedPluginTypes["fruity filter"] = FL_Plugin::FruityFilter;
mappedPluginTypes["fruity flanger"] = FL_Plugin::FruityFlanger;
mappedPluginTypes["fruity formula controller"] = FL_Plugin::FruityFormulaController;
mappedPluginTypes["fruity free filter"] = FL_Plugin::FruityFreeFilter;
mappedPluginTypes["fruity html notebook"] = FL_Plugin::FruityHTMLNotebook;
mappedPluginTypes["fruity lsd"] = FL_Plugin::FruityLSD;
mappedPluginTypes["fruity mute 2"] = FL_Plugin::FruityMute2;
mappedPluginTypes["fruity notebook"] = FL_Plugin::FruityNotebook;
mappedPluginTypes["fruity panomatic"] = FL_Plugin::FruityPanOMatic;
mappedPluginTypes["fruity parametric eq"] = FL_Plugin::FruityParametricEQ;
mappedPluginTypes["fruity peak controller"] = FL_Plugin::FruityPeakController;
mappedPluginTypes["fruity phase inverter"] = FL_Plugin::FruityPhaseInverter;
mappedPluginTypes["fruity phaser"] = FL_Plugin::FruityPhaser;
mappedPluginTypes["fruity reeverb"] = FL_Plugin::FruityReeverb;
mappedPluginTypes["fruity scratcher"] = FL_Plugin::FruityScratcher;
mappedPluginTypes["fruity send"] = FL_Plugin::FruitySend;
mappedPluginTypes["fruity soft clipper"] = FL_Plugin::FruitySoftClipper;
mappedPluginTypes["fruity spectroman"] = FL_Plugin::FruitySpectroman;
mappedPluginTypes["fruity stereo enhancer"] = FL_Plugin::FruityStereoEnhancer;
mappedPluginTypes["fruity x-y controller"] = FL_Plugin::FruityXYController;
FL_Project p;
if( openFile() == false )
{
return false;
}
if( readID() != makeID( 'F', 'L', 'h', 'd' ) )
{
qWarning( "FlpImport::tryImport(): not a valid FL project\n" );
return false;
}
const int header_len = read32LE();
if( header_len != 6 )
{
qWarning( "FlpImport::tryImport(): invalid file format\n" );
return false;
}
const int type = read16LE();
if( type != 0 )
{
qWarning( "FlpImport::tryImport(): type %d format is not "
"supported\n", type );
return false;
}
p.numChannels = read16LE();
if( p.numChannels < 1 || p.numChannels > 1000 )
{
qWarning( "FlpImport::tryImport(): invalid number of channels "
"(%d)\n", p.numChannels );
return false;
}
const int ppq = read16LE();
if( ppq < 0 )
{
qWarning( "FlpImport::tryImport(): invalid ppq\n" );
return false;
}
QProgressDialog progressDialog(
trackContainer::tr( "Importing FLP-file..." ),
trackContainer::tr( "Cancel" ), 0, p.numChannels );
progressDialog.setWindowTitle( trackContainer::tr( "Please wait..." ) );
progressDialog.show();
bool valid = false;
// search for FLdt chunk
while( 1 )
{
Sint32 id = readID();
const int len = read32LE();
if( file().atEnd() )
{
qWarning( "FlpImport::tryImport(): unexpected "
"end of file\n" );
return false;
}
if( len < 0 || len >= 0x10000000 )
{
qWarning( "FlpImport::tryImport(): invalid "
"chunk length %d\n", len );
return false;
}
if( id == makeID( 'F', 'L', 'd', 't' ) )
{
valid = true;
break;
}
skip( len );
}
if( valid == false )
{
return false;
}
for( int i = 0; i < p.numChannels; ++i )
{
p.channels += FL_Channel();
}
qDebug( "channels: %d\n", p.numChannels );
char * text = NULL;
int text_len = 0;
FL_Plugin::PluginTypes last_plugin_type = FL_Plugin::UnknownPlugin;
int cur_channel = -1;
const bool is_journ = engine::projectJournal()->isJournalling();
engine::projectJournal()->setJournalling( false );
while( file().atEnd() == false )
{
FLP_Events ev = static_cast<FLP_Events>( readByte() );
Uint32 data = readByte();
if( ev >= FLP_Word && ev < FLP_Text )
{
data = data | ( readByte() << 8 );
}
if( ev >= FLP_Int && ev < FLP_Text )
{
data = data | ( readByte() << 16 );
data = data | ( readByte() << 24 );
}
if( ev >= FLP_Text )
{
text_len = data & 0x7F;
Uint8 shift = 0;
while( data & 0x80 )
{
data = readByte();
text_len = text_len | ( ( data & 0x7F ) <<
( shift += 7 ) );
}
delete[] text;
text = new char[text_len+1];
if( readBlock( text, text_len ) <= 0 )
{
qWarning( "could not read string (len: %d)\n",
text_len );
}
text[text_len] = 0;
}
const unsigned char * puc = (const unsigned char*) text;
const int * pi = (const int *) text;
FL_Channel * cc = cur_channel >= 0 ?
&p.channels[cur_channel] : NULL;
switch( ev )
{
// BYTE EVENTS
case FLP_Byte:
qDebug( "undefined byte %d\n", data );
break;
case FLP_NoteOn:
qDebug( "note on: %d\n", data );
// data = pos how to handle?
break;
case FLP_Vol:
qDebug( "vol %d\n", data );
break;
case FLP_Pan:
qDebug( "pan %d\n", data );
break;
case FLP_LoopActive:
qDebug( "active loop: %d\n", data );
break;
case FLP_ShowInfo:
qDebug( "show info: %d\n", data );
break;
case FLP_Shuffle:
qDebug( "shuffle: %d\n", data );
break;
case FLP_MainVol:
p.mainVolume = data * 100 / 128;
break;
case FLP_PatLength:
qDebug( "pattern length: %d\n", data );
break;
case FLP_BlockLength:
qDebug( "block length: %d\n", data );
break;
case FLP_UseLoopPoints:
cc->sampleUseLoopPoints = true;
break;
case FLP_LoopType:
qDebug( "loop type: %d\n", data );
break;
case FLP_ChanType:
qDebug( "channel type: %d\n", data );
if( cc )
{
switch( data )
{
case 0: cc->pluginType = FL_Plugin::Sampler; break;
case 1: cc->pluginType = FL_Plugin::TS404; break;
// case 2: cc->pluginType = FL_Plugin::Fruity_3x_Osc; break;
case 3: cc->pluginType = FL_Plugin::Layer; break;
default:
break;
}
}
break;
case FLP_MixSliceNum:
cc->fxChannel = data+1;
break;
case FLP_EffectChannelMuted:
if( p.currentEffectChannel <= NumFLFxChannels )
{
p.effectChannels[p.currentEffectChannel].isMuted =
( data & 0x08 ) > 0 ? false : true;
}
break;
// WORD EVENTS
case FLP_NewChan:
cur_channel = data;
qDebug( "new channel: %d\n", data );
break;
case FLP_NewPat:
p.currentPattern = data - 1;
if( p.currentPattern > p.maxPatterns )
{
p.maxPatterns = p.currentPattern;
}
break;
case FLP_Tempo:
p.tempo = data;
break;
case FLP_CurrentPatNum:
p.activeEditPattern = data;
break;
case FLP_FX:
qDebug( "FX: %d\n", data );
break;
case FLP_Fade_Stereo:
if( data & 0x02 )
{
cc->sampleReversed = true;
}
else if( data & 0x100 )
{
cc->sampleReverseStereo = true;
}
qDebug( "fade stereo: %d\n", data );
break;
case FLP_CutOff:
qDebug( "cutoff (sample): %d\n", data );
break;
case FLP_PreAmp:
cc->sampleAmp = 100 + data * 100 / 256;
break;
case FLP_Decay:
qDebug( "decay (sample): %d\n", data );
break;
case FLP_Attack:
qDebug( "attack (sample): %d\n", data );
break;
case FLP_MainPitch:
p.mainPitch = data;
break;
case FLP_Resonance:
qDebug( "reso (sample): %d\n", data );
break;
case FLP_LoopBar:
qDebug( "loop bar: %d\n", data );
break;
case FLP_StDel:
qDebug( "stdel (delay?): %d\n", data );
break;
case FLP_FX3:
qDebug( "FX 3: %d\n", data );
break;
case FLP_ShiftDelay:
qDebug( "shift delay: %d\n", data );
break;
case FLP_Dot:
cc->dots.push_back( ( data & 0xff ) +
( p.currentPattern << 8 ) );
break;
case FLP_LayerChans:
p.channels[data].layerParent = cur_channel;
// DWORD EVENTS
case FLP_Color:
cc->color = data;
break;
case FLP_PlayListItem:
{
FL_PlayListItem i;
i.position = ( data & 0xffff ) *
DefaultTicksPerTact;
i.length = DefaultTicksPerTact;
i.pattern = ( data >> 16 ) - 1;
p.playListItems.push_back( i );
if( i.pattern > p.maxPatterns )
{
p.maxPatterns = i.pattern;
}
break;
}
case FLP_FXSine:
qDebug( "fx sine: %d\n", data );
break;
case FLP_CutCutBy:
qDebug( "cut cut by: %d\n", data );
break;
case FLP_MiddleNote:
cc->baseNote = data+9;
break;
case FLP_DelayReso:
qDebug( "delay resonance: %d\n", data );
break;
case FLP_Reverb:
qDebug( "reverb (sample): %d\n", data );
break;
case FLP_IntStretch:
qDebug( "int stretch (sample): %d\n", data );
break;
// TEXT EVENTS
case FLP_Text_ChanName:
cc->name = text;
break;
case FLP_Text_PatName:
p.patternNames[p.currentPattern] = text;
break;
case FLP_Text_CommentRTF:
{
QByteArray ba( text, text_len );
QBuffer buf( &ba );
buf.open( QBuffer::ReadOnly );
lineno = 0;
attr_clear_all();
op = html_init();
hash_init();
Word * word = word_read( &buf );
QString out;
word_print( word, out );
word_free( word );
op_free( op );
p.projectNotes = out;
outstring = "";
break;
}
case FLP_Text_Title:
p.projectTitle = text;
break;
case FLP_Text_SampleFileName:
{
QString f = "";
QString f_name = text;
/* if( f.mid( 1, 11 ) == "Instruments" )
{
f = "\\Patches\\Packs" +
f.mid( 12 );
}*/
bool foundFile = false;
f_name.replace( '\\', QDir::separator() );
if( QFileInfo( configManager::inst()->flDir() +
"/Data/" ).exists() )
{
f = configManager::inst()->flDir() +
"/Data/" + f_name;
foundFile = QFileInfo( f ).exists();
}
else
{
// FL 3 compat
f = configManager::inst()->flDir() +
"/Samples/" + f_name;
foundFile = QFileInfo( f ).exists();
}
if( ! foundFile )
{
// look in same directory as .flp file
f = m_fileBase + "/" + QFileInfo( f_name ).fileName();
printf("looking in %s for samples\n", qPrintable(f));
foundFile = QFileInfo( f ).exists();
}
cc->sampleFileName = f;
break;
}
case FLP_Text_Version:
{
qDebug( "FLP version: %s\n", text );
p.versionString = text;
QStringList l = p.versionString.split( '.' );
p.version = ( l[0].toInt() << 8 ) +
( l[1].toInt() << 4 ) +
( l[2].toInt() << 0 );
if( p.version >= 0x600 )
{
p.versionSpecificFactor = 100;
}
break;
}
case FLP_Text_PluginName:
if( mappedPluginTypes.
contains( QString( text ).toLower() ) )
{
const FL_Plugin::PluginTypes t = static_cast<FL_Plugin::PluginTypes>(
mappedPluginTypes[QString( text ).toLower()] );
if( t > FL_Plugin::EffectPlugin )
{
qDebug( "recognized new effect %s\n", text );
p.effects.push_back( FL_Effect( t ) );
}
else if( cc )
{
qDebug( "recognized new plugin %s\n", text );
cc->pluginType = t;
}
last_plugin_type = t;
}
else
{
qDebug( "unsupported plugin: %s!\n", text );
}
break;
case FLP_Text_EffectChanName:
++p.currentEffectChannel;
if( p.currentEffectChannel <= NumFLFxChannels )
{
p.effectChannels[p.currentEffectChannel].name = text;
}
break;
case FLP_Text_Delay:
qDebug( "delay data: " );
// pi[1] seems to be volume or similiar and
// needs to be divided
// by p.versionSpecificFactor
dump_mem( text, text_len );
break;
case FLP_Text_TS404Params:
if( cc && cc->pluginType == FL_Plugin::UnknownPlugin &&
cc->pluginSettings == NULL )
{
cc->pluginSettings = new char[text_len];
memcpy( cc->pluginSettings, text, text_len );
cc->pluginSettingsLength = text_len;
cc->pluginType = FL_Plugin::TS404;
}
break;
case FLP_Text_NewPlugin:
if( last_plugin_type > FL_Plugin::EffectPlugin )
{
FL_Effect * e = &p.effects.last();
e->fxChannel = puc[0];
e->fxPos = puc[4];
qDebug( "new effect: " );
}
else
{
qDebug( "new plugin: " );
}
dump_mem( text, text_len );
break;
case FLP_Text_PluginParams:
if( cc && cc->pluginSettings == NULL )
{
cc->pluginSettings = new char[text_len];
memcpy( cc->pluginSettings, text,
text_len );
cc->pluginSettingsLength = text_len;
}
qDebug( "plugin params: " );
dump_mem( text, text_len );
break;
case FLP_Text_ChanParams:
cc->arpDir = mappedArpDir[pi[10]];
cc->arpRange = pi[11];
cc->selectedArp = pi[12];
if( cc->selectedArp < 8 )
{
const int mappedArps[] = { 0, 1, 5, 6, 2, 3, 4 } ;
cc->selectedArp = mappedArps[cc->selectedArp];
}
cc->arpTime = ( ( pi[13]+1 ) * p.tempo ) /
( 4*16 ) + 1;
cc->arpGate = ( pi[14] * 100.0f ) / 48.0f;
cc->arpEnabled = pi[10] > 0;
qDebug( "channel params: " );
dump_mem( text, text_len );
break;
case FLP_Text_EnvLfoParams:
{
const float scaling = 1.0 / 65536.0f;
FL_Channel_Envelope e;
switch( cc->envelopes.size() )
{
case 1:
e.target = InstrumentSoundShaping::Volume;
break;
case 2:
e.target = InstrumentSoundShaping::Cut;
break;
case 3:
e.target = InstrumentSoundShaping::Resonance;
break;
default:
e.target = InstrumentSoundShaping::NumTargets;
break;
}
e.predelay = pi[2] * scaling;
e.attack = pi[3] * scaling;
e.hold = pi[4] * scaling;
e.decay = pi[5] * scaling;
e.sustain = 1-pi[6] / 128.0f;
e.release = pi[7] * scaling;
if( e.target == InstrumentSoundShaping::Volume )
{
e.amount = pi[1] ? 1 : 0;
}
else
{
e.amount = pi[8] / 128.0f;
}
// e.lfoAmount = pi[11] / 128.0f;
cc->envelopes.push_back( e );
qDebug( "envelope and lfo params:\n" );
dump_mem( text, text_len );
break;
}
case FLP_Text_BasicChanParams:
cc->volume = ( pi[1] / p.versionSpecificFactor ) * 100 / 128;
cc->panning = ( pi[0] / p.versionSpecificFactor ) * 200 / 128 -
PanningRight;
if( text_len > 12 )
{
cc->filterType = mappedFilter[puc[20]];
cc->filterCut = puc[12] / ( 255.0f * 2.5f );
cc->filterRes = 0.01f + puc[16] / ( 256.0f * 2 );
cc->filterEnabled = ( puc[13] == 0 );
if( puc[20] >= 6 )
{
cc->filterCut *= 0.5f;
}
}
qDebug( "basic chan params: " );
dump_mem( text, text_len );
break;
case FLP_Text_OldFilterParams:
cc->filterType = mappedFilter[puc[8]];
cc->filterCut = puc[0] / ( 255.0f * 2.5 );
cc->filterRes = 0.1f + puc[4] / ( 256.0f * 2 );
cc->filterEnabled = ( puc[1] == 0 );
if( puc[8] >= 6 )
{
cc->filterCut *= 0.5;
}
qDebug( "old filter params: " );
dump_mem( text, text_len );
break;
case FLP_Text_AutomationData:
{
const int bpae = 12;
const int imax = text_len / bpae;
qDebug( "automation data (%d items)\n", imax );
for( int i = 0; i < imax; ++i )
{
FL_Automation a;
a.pos = pi[3*i+0] /
( 4*ppq / DefaultTicksPerTact );
a.value = pi[3*i+2];
a.channel = pi[3*i+1] >> 16;
a.control = pi[3*i+1] & 0xffff;
if( a.channel >= 0 &&
a.channel < p.numChannels )
{
qDebug( "add channel %d at %d val %d control:%d\n",
a.channel, a.pos, a.value, a.control );
p.channels[a.channel].automationData += a;
}
// dump_mem( text+i*bpae, bpae );
}
break;
}
case FLP_Text_PatternNotes:
{
//dump_mem( text, text_len );
const int bpn = 20;
const int imax = ( text_len + bpn - 1 ) / bpn;
for( int i = 0; i < imax; ++i )
{
int ch = *( puc + i*bpn + 6 );
int pan = *( puc + i*bpn + 16 );
int vol = *( puc + i*bpn + 17 );
int pos = *( (int *)( puc + i*bpn ) );
int key = *( puc + i*bpn + 12 );
int len = *( (int*)( puc + i*bpn +
8 ) );
pos /= (4*ppq) / DefaultTicksPerTact;
len /= (4*ppq) / DefaultTicksPerTact;
note n( len, pos, key, vol * 100 / 128,
pan*200 / 128 - 100 );
if( ch < p.numChannels )
{
p.channels[ch].notes.push_back( qMakePair( p.currentPattern, n ) );
}
else
{
qDebug( "invalid " );
}
qDebug( "note: " );
dump_mem( text+i*bpn, bpn );
}
break;
}
case FLP_Text_ChanGroupName:
qDebug( "channel group name: %s\n", text );
break;
// case 216: pi[2] /= p.versionSpecificFactor
// case 229: pi[1] /= p.versionSpecificFactor
case 225:
{
enum FLP_EffectParams
{
EffectParamVolume = 0x1fc0
} ;
const int bpi = 12;
const int imax = text_len / bpi;
for( int i = 0; i < imax; ++i )
{
const int param = pi[i*3+1] & 0xffff;
const int ch = ( pi[i*3+1] >> 22 )
& 0x7f;
if( ch < 0 || ch > NumFLFxChannels )
{
continue;
}
const int val = pi[i*3+2];
if( param == EffectParamVolume )
{
p.effectChannels[ch].volume = ( val / p.versionSpecificFactor ) * 100 / 128;
}
else
{
qDebug( "FX-ch: %d param: %x value:%x\n", ch, param, val );
}
}
break;
}
case 233: // playlist items
{
const int bpi = 28;
const int imax = text_len / bpi;
for( int i = 0; i < imax; ++i )
{
const int pos = pi[i*bpi/sizeof(int)+0] / ( (4*ppq) / DefaultTicksPerTact );
const int len = pi[i*bpi/sizeof(int)+2] / ( (4*ppq) / DefaultTicksPerTact );
const int pat = pi[i*bpi/sizeof(int)+3] & 0xfff;
if( pat > 2146 && pat <= 2278 ) // whatever these magic numbers are for...
{
FL_PlayListItem i;
i.position = pos;
i.length = len;
i.pattern = 2278 - pat;
p.playListItems += i;
}
else
{
qDebug( "unknown playlist item: " );
dump_mem( text+i*bpi, bpi );
}
}
break;
}
default:
if( ev >= FLP_Text )
{
qDebug( "!! unhandled text (ev: %d, len: %d): ",
ev, text_len );
dump_mem( text, text_len );
}
else
{
qDebug( "!! handling of FLP-event %d not implemented yet "
"(data=%d).\n", ev, data );
}
break;
}
}
// now create a project from FL_Project data structure
engine::getSong()->clearProject();
// configure the mixer
for( int i=0; i<NumFLFxChannels; ++i )
{
engine::fxMixer()->createChannel();
}
engine::fxMixerView()->refreshDisplay();
// set global parameters
engine::getSong()->setMasterVolume( p.mainVolume );
engine::getSong()->setMasterPitch( p.mainPitch );
engine::getSong()->setTempo( p.tempo );
// set project notes
engine::getProjectNotes()->setText( p.projectNotes );
progressDialog.setMaximum( p.maxPatterns + p.channels.size() +
p.effects.size() );
int cur_progress = 0;
// create BB tracks
QList<bbTrack *> bb_tracks;
QList<InstrumentTrack *> i_tracks;
while( engine::getBBTrackContainer()->numOfBBs() <= p.maxPatterns )
{
const int cur_pat = bb_tracks.size();
bbTrack * bbt = dynamic_cast<bbTrack *>(
track::create( track::BBTrack, engine::getSong() ) );
if( p.patternNames.contains( cur_pat ) )
{
bbt->setName( p.patternNames[cur_pat] );
}
bb_tracks += bbt;
progressDialog.setValue( ++cur_progress );
qApp->processEvents();
}
// create instrument-track for each channel
for( QList<FL_Channel>::Iterator it = p.channels.begin();
it != p.channels.end(); ++it )
{
InstrumentTrack * t = dynamic_cast<InstrumentTrack *>(
track::create( track::InstrumentTrack,
engine::getBBTrackContainer() ) );
engine::getBBTrackContainer()->updateAfterTrackAdd();
i_tracks.push_back( t );
switch( it->pluginType )
{
case FL_Plugin::Fruity_3x_Osc:
it->instrumentPlugin =
t->loadInstrument( "tripleoscillator" );
break;
case FL_Plugin::Plucked:
it->instrumentPlugin =
t->loadInstrument( "vibedstrings" );
break;
case FL_Plugin::FruitKick:
it->instrumentPlugin =
t->loadInstrument( "kicker" );
break;
case FL_Plugin::TS404:
it->instrumentPlugin =
t->loadInstrument( "lb302" );
break;
case FL_Plugin::FruitySoundfontPlayer:
it->instrumentPlugin =
t->loadInstrument( "sf2player" );
break;
case FL_Plugin::Sampler:
case FL_Plugin::UnknownPlugin:
default:
it->instrumentPlugin =
t->loadInstrument( "audiofileprocessor" );
break;
}
processPluginParams( &( *it ) );
t->setName( it->name );
t->volumeModel()->setValue( it->volume );
t->panningModel()->setValue( it->panning );
t->baseNoteModel()->setValue( it->baseNote );
t->effectChannelModel()->setValue( it->fxChannel );
InstrumentSoundShaping * iss = &t->m_soundShaping;
iss->m_filterModel.setValue( it->filterType );
iss->m_filterCutModel.setValue( it->filterCut *
( iss->m_filterCutModel.maxValue() -
iss->m_filterCutModel.minValue() ) +
iss->m_filterCutModel.minValue() );
iss->m_filterResModel.setValue( it->filterRes *
( iss->m_filterResModel.maxValue() -
iss->m_filterResModel.minValue() ) +
iss->m_filterResModel.minValue() );
iss->m_filterEnabledModel.setValue( it->filterEnabled );
for( QList<FL_Channel_Envelope>::iterator jt = it->envelopes.begin();
jt != it->envelopes.end(); ++jt )
{
if( jt->target != InstrumentSoundShaping::NumTargets )
{
EnvelopeAndLfoParameters * elp =
iss->m_envLfoParameters[jt->target];
elp->m_predelayModel.setValue( jt->predelay );
elp->m_attackModel.setValue( jt->attack );
elp->m_holdModel.setValue( jt->hold );
elp->m_decayModel.setValue( jt->decay );
elp->m_sustainModel.setValue( jt->sustain );
elp->m_releaseModel.setValue( jt->release );
elp->m_amountModel.setValue( jt->amount );
elp->updateSampleVars();
}
}
Arpeggiator * arp = &t->m_arpeggiator;
arp->m_arpDirectionModel.setValue( it->arpDir );
arp->m_arpRangeModel.setValue( it->arpRange );
arp->m_arpModel.setValue( it->selectedArp );
arp->m_arpTimeModel.setValue( it->arpTime );
arp->m_arpGateModel.setValue( it->arpGate );
arp->m_arpEnabledModel.setValue( it->arpEnabled );
// process all dots
for( QList<int>::ConstIterator jt = it->dots.begin();
jt != it->dots.end(); ++jt )
{
const int pat = *jt / 256;
const int pos = *jt % 256;
pattern * p =
dynamic_cast<pattern *>( t->getTCO( pat ) );
if( p == NULL )
{
continue;
}
p->setStep( pos, true );
}
// TODO: use future layering feature
if( it->layerParent >= 0 )
{
it->notes += p.channels[it->layerParent].notes;
}
// process all notes
for( FL_Channel::noteVector::ConstIterator jt = it->notes.begin();
jt != it->notes.end(); ++jt )
{
const int pat = jt->first;
if( pat > 100 )
{
continue;
}
pattern * p = dynamic_cast<pattern *>( t->getTCO( pat ) );
if( p != NULL )
{
p->addNote( jt->second, false );
}
}
// process automation data
for( QList<FL_Automation>::ConstIterator jt =
it->automationData.begin();
jt != it->automationData.end(); ++jt )
{
AutomatableModel * m = NULL;
float value = jt->value;
bool scale = false;
switch( jt->control )
{
case FL_Automation::ControlVolume:
m = t->volumeModel();
value *= ( 100.0f / 128.0f ) / p.versionSpecificFactor;
break;
case FL_Automation::ControlPanning:
m = t->panningModel();
value = ( value / p.versionSpecificFactor ) *200/128 - PanningRight;
break;
case FL_Automation::ControlPitch:
m = t->pitchModel();
break;
case FL_Automation::ControlFXChannel:
m = t->effectChannelModel();
value = value*200/128 - PanningRight;
break;
case FL_Automation::ControlFilterCut:
scale = true;
m = &t->m_soundShaping.m_filterCutModel;
value /= ( 255 * 2.5f );
break;
case FL_Automation::ControlFilterRes:
scale = true;
m = &t->m_soundShaping.m_filterResModel;
value = 0.1f + value / ( 256.0f * 2 );
break;
case FL_Automation::ControlFilterType:
m = &t->m_soundShaping.m_filterModel;
value = mappedFilter[jt->value];
break;
default:
qDebug( "handling automation data of "
"control %d not implemented "
"yet\n", jt->control );
break;
}
if( m )
{
if( scale )
{
value = m->minValue<float>() + value *
( m->maxValue<float>() - m->minValue<float>() );
}
automationPattern * p = automationPattern::globalAutomationPattern( m );
p->putValue( jt->pos, value, false );
}
}
progressDialog.setValue( ++cur_progress );
qApp->processEvents();
}
// process all effects
EffectKeyList effKeys;
Plugin::DescriptorList pluginDescs;
Plugin::getDescriptorsOfAvailPlugins( pluginDescs );
for( Plugin::DescriptorList::ConstIterator it = pluginDescs.begin();
it != pluginDescs.end(); ++it )
{
if( it->type != Plugin::Effect )
{
continue;
}
if( it->subPluginFeatures )
{
it->subPluginFeatures->listSubPluginKeys( &( *it ), effKeys );
}
else
{
effKeys << EffectKey( &( *it ), it->name );
}
}
for( int fx_ch = 0; fx_ch <= NumFLFxChannels ; ++fx_ch )
{
FxChannel * ch = engine::fxMixer()->effectChannel( fx_ch );
if( !ch )
{
continue;
}
FL_EffectChannel * flch = &p.effectChannels[fx_ch];
if( !flch->name.isEmpty() )
{
ch->m_name = flch->name;
}
ch->m_volumeModel.setValue( flch->volume / 100.0f );
ch->m_muteModel.setValue( flch->isMuted );
}
for( QList<FL_Effect>::ConstIterator it = p.effects.begin();
it != p.effects.end(); ++it )
{
QString effName;
switch( it->pluginType )
{
case FL_Plugin::Fruity7BandEq:
effName = "C* Eq2x2";
break;
case FL_Plugin::FruityBassBoost:
effName = "BassBooster";
break;
case FL_Plugin::FruityChorus:
effName = "TAP Chorus";
break;
case FL_Plugin::FruityCompressor:
//effName = "C* Compress";
effName = "Fast Lookahead limiter";
break;
case FL_Plugin::FruityDelay:
case FL_Plugin::FruityDelay2:
// effName = "Feedback Delay Line (Maximum Delay 5s)";
break;
case FL_Plugin::FruityBloodOverdrive:
case FL_Plugin::FruityFastDist:
case FL_Plugin::FruitySoftClipper:
effName = "C* Clip";
break;
case FL_Plugin::FruityFastLP:
effName = "Low Pass Filter";
break;
case FL_Plugin::FruityPhaser:
effName = "C* PhaserI";
break;
case FL_Plugin::FruityReeverb:
effName = "C* Plate2x2";
break;
case FL_Plugin::FruitySpectroman:
effName = "Spectrum Analyzer";
break;
default:
break;
}
if( effName.isEmpty() || it->fxChannel < 0 ||
it->fxChannel > NumFLFxChannels )
{
continue;
}
EffectChain * ec = &engine::fxMixer()->
effectChannel( it->fxChannel )->m_fxChain;
qDebug( "adding %s to %d\n", effName.toUtf8().constData(),
it->fxChannel );
for( EffectKeyList::Iterator jt = effKeys.begin();
jt != effKeys.end(); ++jt )
{
if( QString( jt->desc->displayName ).contains( effName ) ||
( jt->desc->subPluginFeatures != NULL &&
jt->name.contains( effName ) ) )
{
qDebug( "instantiate %s\n", jt->desc->name );
::Effect * e = Effect::instantiate( jt->desc->name, ec, &( *jt ) );
ec->appendEffect( e );
ec->setEnabled( true );
break;
}
}
progressDialog.setValue( ++cur_progress );
qApp->processEvents();
}
// process all playlist-items
for( QList<FL_PlayListItem>::ConstIterator it = p.playListItems.begin();
it != p.playListItems.end(); ++it )
{
if( it->pattern > p.maxPatterns )
{
continue;
}
trackContentObject * tco =
bb_tracks[it->pattern]->createTCO( midiTime() );
tco->movePosition( it->position );
if( it->length != DefaultTicksPerTact )
{
tco->changeLength( it->length );
}
}
// set current pattern
if( p.activeEditPattern < engine::getBBTrackContainer()->numOfBBs() )
{
engine::getBBTrackContainer()->setCurrentBB(
p.activeEditPattern );
}
// restore journalling settings
engine::projectJournal()->setJournalling( is_journ );
return true;
}
void FlpImport::processPluginParams( FL_Channel * _ch )
{
qDebug( "plugin params for plugin %d (%d bytes): ", _ch->pluginType,
_ch->pluginSettingsLength );
dump_mem( _ch->pluginSettings, _ch->pluginSettingsLength );
switch( _ch->pluginType )
{
case FL_Plugin::Sampler: // AudioFileProcessor loaded
{
QDomDocument dd;
QDomElement de = dd.createElement(
_ch->instrumentPlugin->nodeName() );
de.setAttribute( "reversed", _ch->sampleReversed );
de.setAttribute( "amp", _ch->sampleAmp );
de.setAttribute( "looped", _ch->sampleUseLoopPoints );
de.setAttribute( "sframe", 0 );
de.setAttribute( "eframe", 1 );
de.setAttribute( "src", _ch->sampleFileName );
_ch->instrumentPlugin->restoreState( de );
break;
}
case FL_Plugin::TS404: // LB302 loaded
break;
case FL_Plugin::Fruity_3x_Osc: // TripleOscillator loaded
{
const Oscillator::WaveShapes mapped_3xOsc_Shapes[] =
{
Oscillator::SineWave,
Oscillator::TriangleWave,
Oscillator::SquareWave,
Oscillator::SawWave,
Oscillator::SquareWave, // square-sin
Oscillator::WhiteNoise,
Oscillator::UserDefinedWave
} ;
QDomDocument dd;
QDomElement de = dd.createElement(
_ch->instrumentPlugin->nodeName() );
de.setAttribute( "modalgo1", Oscillator::SignalMix );
de.setAttribute( "modalgo2", Oscillator::SignalMix );
int ws = Oscillator::UserDefinedWave;
for( int i = 0; i < 3; ++i )
{
const Sint32 * d = (const Sint32 *)
( _ch->pluginSettings + i * 28 );
QString is = QString::number( i );
de.setAttribute( "vol" + is,
QString::number( d[0] * 100 /
( 3 * 128 ) ) );
de.setAttribute( "pan" + is,
QString::number( d[1] ) );
de.setAttribute( "coarse" + is,
QString::number( d[3] ) );
de.setAttribute( "finel" + is,
QString::number( d[4] - d[6] / 2 ) );
de.setAttribute( "finer" + is,
QString::number( d[4] + d[6] / 2 ) );
de.setAttribute( "stphdetun" + is,
QString::number( d[5] ) );
const int s = mapped_3xOsc_Shapes[d[2]];
de.setAttribute( "wavetype" + is,
QString::number( s ) );
if( s != Oscillator::UserDefinedWave )
{
ws = s;
}
}
if( ws == Oscillator::UserDefinedWave )
{
de.setAttribute( "wavetype0",
Oscillator::SawWave );
}
de.setAttribute( "vol0", QString::number( 100/2 ) );
// now apply the prepared plugin-state
_ch->instrumentPlugin->restoreState( de );
break;
}
case FL_Plugin::Layer:
// nothing to do
break;
case FL_Plugin::Plucked: // Vibed loaded
// TODO: setup vibed-instrument
break;
case FL_Plugin::UnknownPlugin:
default:
qDebug( "handling of plugin params not implemented "
"for current plugin\n" );
break;
}
}
extern "C"
{
// necessary for getting instance out of shared lib
Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
{
return new FlpImport( QString::fromUtf8(
static_cast<const char *>( _data ) ) );
}
}