示例#1
0
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;
}
示例#2
0
文件: Flp.cpp 项目: EQ4/PyDaw
Flp::Flp(std::string filename) :
    m_good(false),
    m_debug(false)
{
    m_file.open(filename.c_str(),std::fstream::in|std::fstream::binary);
    if (! m_file.good()) {
        m_errMsg = "Error opening the file.";
        return;
    }

    // check for the magic "FLhd" at the beginning
    int realMagic = makeId('F', 'L', 'h', 'd');
    int magic = read32LE();

    if (realMagic != magic) {
        m_errMsg = "Doesn't look like an .flp file.";
        return;
    }

    // header should be 6 bytes long
    const int header_len = read32LE();
    if (header_len != 6) {
        m_errMsg = "File format is too different from what we know"
            " (header should be 6 bytes long, but is " +
            Utils::intToString(header_len) + ").";
        return;
    }

    // some type thing
    const int type = read16LE();
    if (type != 0) {
        m_errMsg = "type " + Utils::intToString(type) + " is not supported.";
        return;
    }

    // number of channels
    m_project.numChannels = read16LE();
    if (m_project.numChannels < 1 || m_project.numChannels > 1000) {
        m_errMsg = "invalid number of channels: " +
            Utils::intToString(m_project.numChannels) + ".";
        return;
    }

    // ppq
    const int ppq = read16LE();
    if (ppq < 0) {
        m_errMsg = "invalid ppq: " + Utils::intToString(ppq) + ".";
        return;
    }

    // search for FLdt chunk
    bool valid = false;
    while (true) {
        int id = read32LE();
        const int len = read32LE();
        if (m_file.eof()) {
            m_errMsg = "Unexpected end of file.";
            return;
        }
        // sanity check
        if (len < 0 || len >= 0x10000000) {
            m_errMsg = "Invalid chunk length: " + Utils::intToString(len) + ".";
            return;
        }
        // check for FLdt
        if (id == makeId('F', 'L', 'd', 't')) {
            // TODO: read meta-information

            valid = true;
            break;
        }
        skip(len);
    }
    if (! valid) {
        m_errMsg = "Could not find FLdt chunk.";
        return;
    }
    
    // headers checked out ok. now read the events.
    for (int i=0; i<m_project.numChannels; ++i) {
        m_project.channels.push_back(FL_Channel());
    }

    char * text = NULL;
    int text_len = 0;
    int cur_channel = -1;
    while (! m_file.eof()) {
        FLP_Events ev = static_cast<FLP_Events>(readByte());
        int data = readByte();

        // see FLP_Format for the juicy details of this disgusting file format.
        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;
            unsigned char shift = 0;
            while (data & 0x80) {
                data = readByte();
                text_len = text_len | ((data & 0x7F) << (shift+=7));
            }
            delete[] text;
            text = new char[text_len+1];
            m_file.read(text, text_len);
            assert(m_file.good());
            text[text_len] = 0; // null byte of string
        }
        // TODO: name these variables better
        // puc = pointer to an unsigned char
        const unsigned char * puc = (const unsigned char*) text;
        // pi = pointer to an int
        const int * pi = (const int *) text;

        FL_Channel * cc = cur_channel >= 0 ?
            &m_project.channels[cur_channel] : NULL;

        switch (ev) {
        // BYTE EVENTS
        case FLP_Byte:
            if (m_debug)
                std::cerr << "undefined byte " << data << std::endl;
            break;
        case FLP_NoteOn:
            if (m_debug)
                std::cerr << "note on: " << data << std::endl;
            // data = pos   how to handle?
            break;

        case FLP_Vol:
            if (m_debug)
                std::cerr << "vol " << data << std::endl;
            break;

        case FLP_Pan:
            if (m_debug)
                std::cerr << "pan " << data << std::endl;
            break;

        case FLP_LoopActive:
            if (m_debug)
                std::cerr << "active loop: " << data << std::endl;
            break;

        case FLP_ShowInfo:
            if (m_debug)
                std::cerr << "show info: " << data << std::endl;
            break;

        case FLP_Shuffle:
            if (m_debug)
                std::cerr << "shuffle: " << data << std::endl;
            break;

        case FLP_MainVol:
            m_project.mainVolume = data;
            break;

        case FLP_PatLength:
            if (m_debug)
                std::cerr << "pattern length: " << data << std::endl;
            break;

        case FLP_BlockLength:
            if (m_debug)
                std::cerr << "block length: " << data << std::endl;
            break;

        case FLP_UseLoopPoints:
            cc->sampleUseLoopPoints = true;
            break;

        case FLP_LoopType:
            if (m_debug)
                std::cerr << "loop type: " << data << std::endl;
            break;

        case FLP_ChanType:
            if (m_debug)
                std::cerr << "channel type: " << data << std::endl;
            if (cc) {
                switch (data) {
                    case 0: cc->generatorName = "Sampler"; break;
                    case 1: cc->generatorName = "TS 404"; break;
                    case 2: cc->generatorName = "3x Osc"; break;
                    case 3: cc->generatorName = "Layer"; break;
                    default: break;
                }
            }
            break;

        case FLP_MixSliceNum:
            cc->fxChannel = data+1;
            break;

        case FLP_EffectChannelMuted:
            if( m_project.currentEffectChannel <= c_NumFLFxChannels ) {
                m_project.effectChannels[m_project.currentEffectChannel]
                    .isMuted = ( data & 0x08 ) > 0 ? false : true;
            }
            break;

            // WORD EVENTS
        case FLP_NewChan:
            cur_channel = data;
            break;

        case FLP_NewPat:
            m_project.currentPattern = data - 1;
            if( m_project.currentPattern > m_project.maxPatterns )
                m_project.maxPatterns = m_project.currentPattern;
            break;

        case FLP_Tempo:
            m_project.tempo = data;
            break;

        case FLP_CurrentPatNum:
            m_project.activeEditPattern = data;
            break;

        case FLP_FX:
            if (m_debug)
                std::cerr << "FX: " << data << std::endl;
            break;

        case FLP_Fade_Stereo:
            if( data & 0x02 )
            {
                cc->sampleReversed = true;
            }
            else if( data & 0x100 )
            {
                cc->sampleReverseStereo = true;
            }
            break;

        case FLP_CutOff:
            if (m_debug)
                std::cerr << "cutoff (sample): " << data << std::endl;
            break;

        case FLP_PreAmp:
            cc->sampleAmp = data;
            break;

        case FLP_Decay:
            if (m_debug)
                std::cerr << "decay (sample): " << data << std::endl;
            break;

        case FLP_Attack:
            if (m_debug)
                std::cerr << "attack (sample): " << data << std::endl;
            break;

        case FLP_MainPitch:
            m_project.mainPitch = data;
            break;

        case FLP_Resonance:
            if (m_debug)
                std::cerr << "resonance (sample): " << data << std::endl;
            break;

        case FLP_LoopBar:
            if (m_debug)
                std::cerr << "loop bar: " << data << std::endl;
            break;

        case FLP_StDel:
            if (m_debug)
                std::cerr << "stdel (delay?): " << data << std::endl;
            break;

        case FLP_FX3:
            if (m_debug)
                std::cerr << "FX 3: " << data << std::endl;
            break;

        case FLP_ShiftDelay:
            if (m_debug)
                std::cerr << "shift delay: " << data << std::endl;
            break;

        case FLP_Dot:
            cc->dots.push_back( ( data & 0xff ) +
                    ( m_project.currentPattern << 8 ) );
            break;

        case FLP_LayerChans:
            m_project.channels[data].layerParent = cur_channel;
            cc->generatorName = "Layer";

            // DWORD EVENTS
        case FLP_Color:
            // TODO: double check that this works
            cc->color.r = (data & 0xFF000000) >> 24;
            cc->color.g = (data & 0x00FF0000) >> 16;
            cc->color.b = (data & 0x0000FF00) >> 8;
            break;

        case FLP_PlayListItem:
            {
                FL_PlayListItem i;
                i.position = (data & 0xffff) * 192;
                i.length = 192;
                i.pattern = (data >> 16) - 1;
                m_project.playListItems.push_back(i);
                if (i.pattern > m_project.maxPatterns)
                    m_project.maxPatterns = i.pattern;
                break;
            }

        case FLP_FXSine:
            if (m_debug)
                std::cerr << "fx sine: " << data << std::endl;
            break;

        case FLP_CutCutBy:
            if (m_debug)
                std::cerr << "cut cut by: " << data << std::endl;
            break;

        case FLP_MiddleNote:
            cc->baseNote = data+9;
            break;

        case FLP_DelayReso:
            if (m_debug)
                std::cerr << "delay resonance: " << data << std::endl;
            break;

        case FLP_Reverb:
            if (m_debug)
                std::cerr << "reverb (sample): " << data << std::endl;
            break;

        case FLP_IntStretch:
            if (m_debug)
                std::cerr << "int stretch (sample): " << data << std::endl;
            break;

            // TEXT EVENTS
        case FLP_Text_ChanName:
            cc->name = text;
            break;

        case FLP_Text_PatName:
            m_project.patternNames[m_project.currentPattern] = text;
            break;

        case FLP_Text_CommentRTF:
            if (m_debug)
                std::cerr << "TODO: RTF text comment." << std::endl;
            /* TODO: support RTF comments
            {
                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:
            m_project.projectTitle = text;
            break;

        case FLP_Text_SampleFileName:
            cc->sampleFileName = text;
            cc->generatorName = "Sampler";
            m_sampleSet.insert(cc->sampleFileName);
            break;

        case FLP_Text_Version:
            {
                if (m_debug)
                    std::cerr << "FLP version: " << text << std::endl;
                m_project.versionString = text;
                // divide the version string into numbers
                std::vector<std::string> numbers;
                Utils::split(m_project.versionString, numbers, ".");
                m_project.version = (Utils::stringToInt(numbers[0]) << 8) +
                    (Utils::stringToInt(numbers[1]) << 4 ) +
                    (Utils::stringToInt(numbers[2]) << 0 );
                if( m_project.version >= 0x600 )
                    m_project.versionSpecificFactor = 100;
                break;
            }

        case FLP_Text_PluginName:
            {
                std::string pluginName = text;
                // we add all plugins to effects list and then
                // remove the ones that aren't effects later.
                m_effectPlugins.insert(pluginName);
                cc->generatorName = pluginName;
                if (m_debug)
                    std::cerr << "plugin: " << pluginName << std::endl;
                break;
            }

        case FLP_Text_EffectChanName:
            ++m_project.currentEffectChannel;
            if( m_project.currentEffectChannel <= c_NumFLFxChannels ) {
                m_project.effectChannels[m_project.currentEffectChannel]
                    .name = text;
            }
            break;

        case FLP_Text_Delay:
            if (m_debug)
                std::cerr << "delay data: " << text << std::endl;
            // pi[1] seems to be volume or similiar and
            // needs to be divided
            // by m_project.versionSpecificFactor
            break;

        case FLP_Text_TS404Params:
            if( cc && cc->pluginSettings == NULL ) {
                cc->pluginSettings = new char[text_len];
                std::memcpy( cc->pluginSettings, text, text_len );
                cc->pluginSettingsLength = text_len;
                cc->generatorName = "TS 404";
            }
            break;

        case FLP_Text_NewPlugin:
            // TODO: if it's an effect plugin make a new effect
            if (m_debug) {
                std::cerr << "new plugin: " << std::endl;
                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;
            }
            break;

        case FLP_Text_ChanParams:
            cc->arpDir = 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 ) * m_project.tempo ) / ( 4*16 ) + 1;
            cc->arpGate = ( pi[14] * 100.0f ) / 48.0f;
            cc->arpEnabled = pi[10] > 0;
            break;

        case FLP_Text_EnvLfoParams:
            {
                const float scaling = 1.0 / 65536.0f;
                FL_Channel_Envelope e;

                switch (cc->envelopes.size()) {
                    case 1:
                        e.target = Volume;
                        break;
                    case 2:
                        e.target = Cut;
                        break;
                    case 3:
                        e.target = Resonance;
                        break;
                    default:
                        e.target = 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 == Volume)
                    e.amount = pi[1] ? 1 : 0;
                else
                    e.amount = pi[8] / 128.0f;
                cc->envelopes.push_back(e);
                break;
            }

        case FLP_Text_BasicChanParams:
            cc->volume = pi[1] / m_project.versionSpecificFactor;
            cc->panning = pi[0] / m_project.versionSpecificFactor;
            if (text_len > 12) {
                cc->filterType = puc[20];
                cc->filterCut = puc[12];
                cc->filterRes = puc[16];
                cc->filterEnabled = (puc[13] == 0);
                if( puc[20] >= 6 )
                    cc->filterCut *= 0.5f;
            }
            break;

        case FLP_Text_OldFilterParams:
            cc->filterType = puc[8];
            cc->filterCut = puc[0];
            cc->filterRes = puc[4];
            cc->filterEnabled = ( puc[1] == 0 );
            if( puc[8] >= 6 )
                cc->filterCut *= 0.5;
            break;

        case FLP_Text_AutomationData:
            {
                const int bpae = 12;
                const int imax = text_len / bpae;
                for (int i = 0; i < imax; ++i) {
                    FL_Automation a;
                    a.pos = pi[3*i+0] / (4*ppq / 192);
                    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 < m_project.numChannels) {
                        m_project.channels[a.channel].automationData
                            .push_back(a);
                    }
                }
                break;
            }

        case FLP_Text_PatternNotes:
            {
                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) / 192;
                    len /= (4*ppq) / 192;
                    note n( len, pos, key, vol, pan);
                    if( ch < m_project.numChannels ) {
                        m_project.channels[ch].notes.push_back(
                            std::make_pair(m_project.currentPattern, n));
                    } else {
                        if (m_debug)
                            std::cerr << "Invalid ch: " << ch << std::endl;
                    }
                }
                break;
            }

        case FLP_Text_ChanGroupName:
            if (m_debug)
                std::cerr << "channel group name: " << text << std::endl;
            break;

        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 > c_NumFLFxChannels )
                        continue;
                    const int val = pi[i*3+2];
                    if( param == EffectParamVolume ) {
                        m_project.effectChannels[ch].volume =
                            (val / m_project.versionSpecificFactor);
                    } else {
                        if (m_debug) {
                            std::cerr << "FX-ch: " << ch << "  param: "
                                << param << "  value: " << val << std::endl;
                        }
                    }
                }
                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) / 192);
                    const int len = pi[i*bpi/sizeof(int)+2] /
                        ((4*ppq) / 192);
                    const int pat = pi[i*bpi/sizeof(int)+3] & 0xfff;
                    // whatever these magic numbers are for...
                    if( pat > 2146 && pat <= 2278 ) {
                        FL_PlayListItem i;
                        i.position = pos;
                        i.length = len;
                        i.pattern = 2278 - pat;
                        m_project.playListItems.push_back(i);
                    } else {
                        if (m_debug) {
                            std::cerr << "unknown playlist item: " << text
                                        << std::endl;
                        }
                    }
                }
                break;
            }
        default:
            if( ev >= FLP_Text ) {
                if (m_debug) {
                    std::cerr << "unhandled text (ev: " << ev << ", len: "
                        << text_len << "): " << text << std::endl;
                }
            } else {
                if (m_debug) {
                    std::cerr << "handling of FLP-event " << ev
                        << " not implemented yet (data=" << data << ")" 
                        << std::endl;
                }
            }
            break;
        }
    }
    // for each fruity wrapper, extract the plugin name.
    for (unsigned int i=0; i<m_project.channels.size(); ++i)
        tryFruityWrapper(&m_project.channels[i]);
    for (unsigned int i=0; i<m_project.effects.size(); ++i)
        tryFruityWrapper(&m_project.effects[i]);

    // create list of sample dependencies
    m_sampleStrings.clear();
    std::set<std::string>::iterator it;
    for (it=m_sampleSet.begin(); it != m_sampleSet.end(); ++it)
        m_sampleStrings.push_back(*it);

    // effects are the ones that aren't channels.
    m_channelPlugins.clear();
    m_effectStrings.clear();
    for (unsigned int i=0; i<m_project.channels.size(); ++i)
        m_channelPlugins.insert(m_project.channels[i].generatorName);
    for (it=m_effectPlugins.begin(); it != m_effectPlugins.end(); ++it) {
        if (m_channelPlugins.count(*it) == 0)
            m_effectStrings.push_back(*it);
    }
    

    m_good = true;
}