flp-filter-improvements

git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@116 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2006-03-29 13:26:53 +00:00
parent 4041e69e41
commit 4b9fb916f4
6 changed files with 173 additions and 176 deletions

2
TODO
View File

@@ -1,4 +1,4 @@
- when recording MIDI-events, add switch to disable recording of velocity
- in flp-import-filter: merge play-list-items if possible
- integrated sample-browser in context-menu of sample-track/-tco
- font-size-scaling-coefficient in setup-dialog
- make note able of journalling

View File

@@ -2,8 +2,8 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.50)
AC_INIT(lmms, 0.1.4-cvs20060327, tobydox/at/users/dot/sourceforge/dot/net)
AM_INIT_AUTOMAKE(lmms, 0.1.4-cvs20060327)
AC_INIT(lmms, 0.1.4-cvs20060328, tobydox/at/users/dot/sourceforge/dot/net)
AM_INIT_AUTOMAKE(lmms, 0.1.4-cvs20060328)
AM_CONFIG_HEADER(config.h)

View File

@@ -56,6 +56,7 @@ public:
virtual ~projectNotes();
void clear( void );
void setText( const QString & _text );
virtual void FASTCALL saveSettings( QDomDocument & _doc,
QDomElement & _parent );

View File

@@ -187,6 +187,8 @@ public slots:
m_modified = TRUE;
}
void clearProject( void );
protected:
virtual void closeEvent( QCloseEvent * _ce );
@@ -232,8 +234,6 @@ private:
songEditor( const songEditor & );
virtual ~songEditor();
void clearProject( void );
inline tact currentTact( void ) const
{

View File

@@ -24,99 +24,10 @@
#include "flp_import.h"
/*
The FruityLoops
FLP
file format explained
(v 2.7.x)
(BETA)
Introduction:
Here I will try to explain the FLP file format, which is the FruityLoops loop / song format. Visit www.fruityloops.com to get more info about FruityLoops.
First you've got to understand that FruityLoops keeps growing, so does the FLP format. Fortunately it has been designed for it... While I made some mistakes during its evolution, the format still remains updatable.
Normally anything made out of this document will be able to process future loop files, although of course you will miss newly added features.
Not sure what this document could be useful for, but if you ever use it to make something, send it to me!
Also if you'd like me to update this file (it's not complete yet), feel free to mail me.
(2/09/99) Didier Dambrin (gol)
gol@fruityloops.com
How it works:
Don't expect the FLP format to be a big chunk full of ordered parameters, like most trackers file formats. It had to evolute, so I chose the 'events' way.
You should see it a bit like MIDI / AIFF files. It's just a succession of events. Once you've understand how to process the file to retrieve these events, the only thing you'll need is the list of events available!
It's better, since once you've made the piece of code to get the events, you won't bother with the format anymore. Also you will just ignore any event you don't know (yet) about.
Please note that the format *does not* respect the AIFF standard, although I tried to keep the chunks system similar.
Although the program has been coded in Pascal, I'll do my best to use C++ declarations, so everyone will understand. DWORD is 4 bytes, WORD is 2 bytes.
Retrieving the events:
I said it looks like a MIDI file, but it's not a MIDI file.
First, you'll have to get & check the HEADER chunk, to be sure it's a FLP file.
The header is similar to the format of a MIDI file header:
DWORD ChunkID 4 chars which are the letters 'FLhd' for 'FruityLoops header'
DWORD Length The length of this chunk, like in MIDI files. Should be 6 because of the 3 WORDS below...
WORD Format Set to 0 for full songs.
WORD nChannels The total number of channels (not really used).
WORD BeatDiv Pulses per quarter of the song.
Most of this chunk is not used, it's just that I tried (as a start) to respect the proper MIDI header :)
Then you'll encounter the DATA chunk, which is in fact the last chunk, the one containing all the events.
DWORD ChunkID 4 chars which are the letters 'FLdt' for 'FruityLoops data'
DWORD Length The length of this chunk WITHOUT these 2 DWORDS (that is minus 4*2 bytes), like in MIDI files.
The whole data chunk is a succession of EVENTS, which I'm going to explain...
To retrieve an event, first you read a byte (the event ID). According to this byte, the size of the event data varies:
0..63 The data after this byte is a BYTE (signed or unsigned, depending on the ID).
64..127 The data after this byte is a WORD.
128..191 The data after this byte is a DWORD.
192..255 The data after this byte is a variable-length block of data (a text for example).
That makes 64 BYTE events, 64 WORD events, 64 DWORD events & 64 TEXT events. The purpose of this split is of course to keep the file size small.
So you get the event ID & then you read the number of bytes according to this ID. Whether you process the event or not isn't important. What is important is that you can jump correctly to the next event if you skip it.
For TEXT (variable-length) events, you still have to read the size of the event, which is coded in the next byte(s) a bit like in MIDI files (but not stupidly inverted). After the size is the actual data, which you can process or skip.
To get the size of the event, you've got to read bytes until the last one, which has bit 7 off (the purpose of this compression is to reduce the file size again).
Start with a DWORD Size = 0. You're going to reconstruct the size by getting packs of 7 bits:
1. Get a byte.
2. Add the first 7 bits of this byte to Size.
3. Check bit 7 (the last bit) of this byte. If it's on, go back to 1. to process the next byte.
To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 it will occupy only 2 bytes & so on...
So globally, you open the file, check the header, point to the data chunk & retrieve / filter all the events. Easy(?)
Now let's get to the events...
*/
#include "project_notes.h"
#include "bb_editor.h"
#include "song_editor.h"
#include "bb_track.h"
#include "track_container.h"
#include "instrument_track.h"
#include "pattern.h"
@@ -280,29 +191,33 @@ bool flpImport::tryImport( trackContainer * _tc )
pattern * p = NULL;
char * text = NULL;
int text_len = 0;
int pat_cnt = 0;
int it_cnt = 0;
int ev_cnt = 0;
// read channels
/* for( int i = 0; i < num_channels; ++i )
{
pd.setValue( i );
#ifdef QT4
qApp->processEvents( QEventLoop::AllEvents, 100 );
#else
qApp->processEvents( 100 );
#endif
_tc->eng()->getSongEditor()->clearProject();
if( pd.wasCanceled() )
{
return( FALSE );
}*/
while( file().atEnd() == FALSE )
{
if( ++ev_cnt > 100 )
{
ev_cnt = 0;
#ifdef QT4
qApp->processEvents( QEventLoop::AllEvents, 100 );
#else
qApp->processEvents( 100 );
#endif
pd.setValue( it_cnt );
if( pd.wasCanceled() )
{
return( FALSE );
}
}
flpEvents ev = static_cast<flpEvents>( readByte() );
Uint32 data = readByte();
//printf("ev: %d\n", (int) ev );
if( ev >= FLP_Word && ev < FLP_Text )
{
data = data | ( readByte() << 8 );
@@ -314,16 +229,6 @@ bool flpImport::tryImport( trackContainer * _tc )
data = data | ( readByte() << 24 );
}
/*For TEXT (variable-length) events, you still have to read the size of the event, which is coded in the next byte(s) a bit like in MIDI files (but not stupidly inverted). After the size is the actual data, which you can process or skip.
To get the size of the event, you've got to read bytes until the last one, which has bit 7 off (the purpose of this compression is to reduce the file size again).
Start with a DWORD Size = 0. You're going to reconstruct the size by getting packs of 7 bits:
1. Get a byte.
2. Add the first 7 bits of this byte to Size.
3. Check bit 7 (the last bit) of this byte. If it's on, go back to 1. to process the next byte.
To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 it will occupy only 2 bytes & so on...
*/
if( ev >= FLP_Text )
{
@@ -345,7 +250,12 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
switch( ev )
{
// BYTE EVENTS
case FLP_Byte:
printf( "undefined byte %d\n", data );
break;
case FLP_NoteOn:
printf( "note on: %d\n", data );
// data = pos how to handle?
break;
@@ -365,10 +275,6 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
printf( "main-volume: %d\n", data );
break;
case FLP_MainPitch:
printf( "main-pitch: %d\n", data );
break;
case FLP_PatLength:
printf( "pattern-length: %d\n", data );
break;
@@ -377,18 +283,35 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
printf( "block length: %d\n", data );
break;
case FLP_LoopType:
printf( "loop type: %d\n", data );
break;
case FLP_ChanType:
printf( "channel type: %d\n", data );
break;
case FLP_MixSliceNum:
printf( "mix slice num: %d\n", data );
break;
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
printf( "ev: %d data: %d\n", ev, data );
break;
// WORD EVENTS
case FLP_NewChan:
printf( "new channel\n" );
++it_cnt;
m_events.clear();
pat_cnt = 0;
it = dynamic_cast<instrumentTrack *>(
track::create( track::CHANNEL_TRACK, _tc ) );
track::create( track::CHANNEL_TRACK, _tc->eng()->getBBEditor() ) );
assert( it != NULL );
it->loadInstrument( "tripleoscillator" );
it->toggledInstrumentTrackButton( FALSE );
@@ -396,10 +319,14 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
break;
case FLP_NewPat:
p = dynamic_cast<pattern *>( it->createTCO(
pat_cnt ) );
assert( p != NULL );
it->addTCO( p );
while( _tc->eng()->getBBEditor()->numOfBBs() <=
data )
{
track::create( track::BB_TRACK,
_tc->eng()->getSongEditor() );
}
p = dynamic_cast<pattern *>(
it->getTCO( data - 1 ) );
break;
case FLP_Tempo:
@@ -411,17 +338,46 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
break;
case FLP_FX:
printf( "FX-channel for cur channel: %d\n", data );
printf( "FX-channel for cur channel: %d\n",
data );
break;
case FLP_Fade_Stereo:
printf( "fade stereo: %d\n", data );
break;
case FLP_CutOff:
printf( "cutoff (for cur channel?): %d\n", data );
printf( "cutoff (for cur channel?): %d\n",
data );
break;
case FLP_PreAmp:
printf( "pre-amp (for cur channel?): %d\n",
data );
break;
case FLP_Decay:
printf( "decay (for cur channel?): %d\n",
data );
break;
case FLP_Attack:
printf( "attack (for cur channel?): %d\n",
data );
break;
case FLP_MainPitch:
printf( "main-pitch: %d\n", data );
break;
case FLP_Resonance:
printf( "reso (for cur channel?): %d\n", data );
break;
case FLP_StDel:
printf( "stdel (delay?): %d\n", data );
break;
case FLP_FX3:
printf( "FX 3: %d\n", data );
break;
@@ -431,6 +387,40 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
break;
// DWORD EVENTS
case FLP_Color:
printf( "color r:%d g:%d b:%d\n",
qRed( data ),
qGreen( data ),
qBlue( data ) );
break;
case FLP_PlayListItem:
{
unsigned int pat_num = ( data >> 16 ) - 1;
while( _tc->eng()->getBBEditor()->numOfBBs() <=
pat_num )
{
track::create( track::BB_TRACK,
_tc->eng()->getSongEditor() );
}
bbTrack * bbt = bbTrack::findBBTrack( pat_num,
_tc->eng() );
trackContentObject * tco = bbt->addTCO(
bbt->createTCO( 0 ) );
tco->movePosition( midiTime( ( data & 0xffff ) *
64 ) );
break;
}
case FLP_FXSine:
printf( "fx sine: %d\n", data );
break;
case FLP_CutCutBy:
printf( "cut cut by: %d\n", data );
break;
case FLP_DelayReso:
printf( "delay resonance: %d\n", data );
break;
@@ -439,6 +429,28 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
printf( "reverb: %d\n", data );
break;
case FLP_IntStretch:
printf( "int stretch: %d\n", data );
break;
// TEXT EVENTS
case FLP_Text_ChanName:
it->setName( text );
break;
case FLP_Text_CommentRTF:
_tc->eng()->getProjectNotes()->setText( text );
break;
case FLP_Text_Title:
printf( "project title: %s\n", text );
break;
case FLP_Text_SampleFileName:
printf( "sample for current channel: %s\n",
text );
break;
case FLP_Version:
printf( "FL Version: %s\n", text );
break;
@@ -446,11 +458,15 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
case FLP_Text_PluginName:
if( QString( text ) == "3x Osc" )
{
it->loadInstrument( "tripleoscillator" );
printf( "loading TripleOscillator for "
"3x Osc\n" );
it->loadInstrument(
"tripleoscillator" );
}
else
{
printf( "unsupported plugin: %s\n", text );
printf( "unsupported plugin: %s\n",
text );
}
break;
@@ -468,46 +484,27 @@ To resume, if Size < 128 then it will occupy only 1 byte, else if Size < 16384 i
dump_mem( text, text_len );
break;
case FLP_ChanParams:
printf( "plugin params:\n" );
dump_mem( text, text_len );
break;
default:
if( ev >= FLP_Text )
{
printf( "unhandled text (%d): %s\n", ev,
text );
printf( "!! unhandled text (ev: %d, "
"len: %d):\n",
ev, text_len );
dump_mem( text, text_len );
}
else
{
printf( "!! handling of FLP-event %d not "
"implemented yet.\n", ev );
printf( "!! handling of FLP-event %d "
"not implemented yet.\n", ev );
}
break;
}
/*
// now create new channel-track for reading track
instrumentTrack * ct = dynamic_cast<instrumentTrack *>(
track::create(
track::CHANNEL_TRACK,
_tc ) );
#ifdef LMMS_DEBUG
assert( ct != NULL );
#endif
// TODO: setup program, channel etc.
ct->loadInstrument( "tripleoscillator" );
ct->toggledInstrumentTrackButton( FALSE );
// now create pattern to store notes in
pattern * p = dynamic_cast<pattern *>( ct->createTCO( 0 ) );
#ifdef LMMS_DEBUG
assert( p != NULL );
#endif
ct->addTCO( p );
// init keys
int keys[NOTES_PER_OCTAVE * OCTAVES][2];
for( int j = 0; j < NOTES_PER_OCTAVE * OCTAVES; ++j )
{
keys[j][0] = -1;
}
// now process every event
for( eventVector::const_iterator it = m_events.begin();
it != m_events.end(); ++it )
@@ -742,17 +739,8 @@ bool FASTCALL flpImport::readTrack( int _track_end )
#endif
/*
void flpImport::error( void )
{
printf( "flpImport::readTrack(): invalid MIDI data (offset %#x)\n",
(unsigned int) file().pos() );
}
*/
extern "C"
{

View File

@@ -152,6 +152,14 @@ void projectNotes::clear( void )
void projectNotes::setText( const QString & _text )
{
m_edit->setHtml( _text );
}
void projectNotes::setupActions()
{
QToolBar * tb = new QToolBar( tr( "Edit Actions" ), this );