1 #include <QDomDocument>
2 #include <QDir>
3 #include <QApplication>
4 #include <QMessageBox>
5 #include <QProgressDialog>
6 #include <QTextStream>
7 #include <stdlib.h>
8
9 #include "LocalFileMng.h"
10 #include "HydrogenImport.h"
11 #include "Song.h"
12 #include "Engine.h"
13 #include "Instrument.h"
14 #include "InstrumentTrack.h"
15 #include "Note.h"
16 #include "Pattern.h"
17 #include "Track.h"
18 #include "BBTrack.h"
19 #include "BBTrackContainer.h"
20 #include "Instrument.h"
21
22 #define MAX_LAYERS 4
23 extern "C"
24 {
25
26 Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor =
27 {
28 STRINGIFY( PLUGIN_NAME ),
29 "Hydrogen Import",
30 QT_TRANSLATE_NOOP( "pluginBrowser",
31 "Filter for importing Hydrogen files into LMMS" ),
32 "frank mather",
33 0x0100,
34 Plugin::ImportFilter,
35 NULL,
36 NULL,
37 NULL
38 } ;
39
40 }
41
42 QString filename;
43 class NoteKey
44 {
45 public:
46 enum Key {
47 C = 0,
48 Cs,
49 D,
50 Ef,
51 E,
52 F,
53 Fs,
54 G,
55 Af,
56 A,
57 Bf,
58 B,
59 };
60
stringToNoteKey(const QString & str)61 static int stringToNoteKey( const QString& str )
62 {
63 int m_key = NoteKey::C;
64
65
66 QString sKey = str.left( str.length() - 1 );
67 QString sOct = str.mid( str.length() - 1, str.length() );
68
69 if ( sKey.endsWith( "-" ) )
70 {
71 sKey.replace( "-", "" );
72 sOct.insert( 0, "-" );
73 }
74 int nOctave = sOct.toInt();
75
76 if ( sKey == "C" )
77 {
78 m_key = NoteKey::C;
79 }
80 else if ( sKey == "Cs" )
81 {
82 m_key = NoteKey::Cs;
83 }
84 else if ( sKey == "D" )
85 {
86 m_key = NoteKey::D;
87 }
88 else if ( sKey == "Ef" )
89 {
90 m_key = NoteKey::Ef;
91 }
92 else if ( sKey == "E" )
93 {
94 m_key = NoteKey::E;
95 }
96 else if ( sKey == "F" )
97 {
98 m_key = NoteKey::F;
99 }
100 else if ( sKey == "Fs" )
101 {
102 m_key = NoteKey::Fs;
103 }
104 else if ( sKey == "G" )
105 {
106 m_key = NoteKey::G;
107 }
108 else if ( sKey == "Af" )
109 {
110 m_key = NoteKey::Af;
111 }
112 else if ( sKey == "A" )
113 {
114 m_key = NoteKey::A;
115 }
116 else if ( sKey == "Bf" )
117 {
118 m_key = NoteKey::Bf;
119 }
120 else if ( sKey == "B" ) {
121 m_key = NoteKey::B;
122 }
123 return m_key + (nOctave*12)+57;
124 }
125
126 };
HydrogenImport(const QString & _file)127 HydrogenImport::HydrogenImport( const QString & _file ) :
128 ImportFilter( _file, &hydrogenimport_plugin_descriptor )
129 {
130 filename = _file;
131 }
132
133
134
135
~HydrogenImport()136 HydrogenImport::~HydrogenImport()
137 {
138 }
139 Instrument * ins;
readSong()140 bool HydrogenImport::readSong()
141 {
142 QHash<QString, InstrumentTrack *> drum_track;
143 QHash<QString, int> pattern_length;
144 QHash<QString, int> pattern_id;
145
146 Song *s = Engine::getSong();
147 int song_num_tracks = s->tracks().size();
148 if ( QFile( filename ).exists() == false )
149 {
150 printf( "Song file not found \n" );
151 return false;
152 }
153 QDomDocument doc = LocalFileMng::openXmlDocument( filename );
154 QDomNodeList nodeList = doc.elementsByTagName( "song" );
155
156 if( nodeList.isEmpty() )
157 {
158 printf( "Error reading song: song node not found\n" );
159 return false;
160 }
161 QDomNode songNode = nodeList.at( 0 );
162
163 QString m_sSongVersion = LocalFileMng::readXmlString( songNode , "version", "Unknown version" );
164
165
166 QString sName( LocalFileMng::readXmlString( songNode, "name", "Untitled Song" ) );
167 QString sAuthor( LocalFileMng::readXmlString( songNode, "author", "Unknown Author" ) );
168 QString sNotes( LocalFileMng::readXmlString( songNode, "notes", "..." ) );
169 QString sLicense( LocalFileMng::readXmlString( songNode, "license", "Unknown license" ) );
170 QString sMode = LocalFileMng::readXmlString( songNode, "mode", "pattern" );
171
172 QDomNode instrumentListNode = songNode.firstChildElement( "instrumentList" );
173 if ( ( ! instrumentListNode.isNull() ) )
174 {
175
176 int instrumentList_count = 0;
177 QDomNode instrumentNode;
178 instrumentNode = instrumentListNode.firstChildElement( "instrument" );
179 while ( ! instrumentNode.isNull() )
180 {
181 instrumentList_count++;
182 QString sId = LocalFileMng::readXmlString( instrumentNode, "id", "" ); // instrument id
183 QString sDrumkit = LocalFileMng::readXmlString( instrumentNode, "drumkit", "" ); // drumkit
184 QString sName = LocalFileMng::readXmlString( instrumentNode, "name", "" ); // name
185 float fVolume = LocalFileMng::readXmlFloat( instrumentNode, "volume", 1.0 ); // volume
186 float fPan_L = LocalFileMng::readXmlFloat( instrumentNode, "pan_L", 0.5 ); // pan L
187 float fPan_R = LocalFileMng::readXmlFloat( instrumentNode, "pan_R", 0.5 ); // pan R
188
189 if ( sId.isEmpty() ) {
190 printf( "Empty ID for instrument. skipping \n" );
191 instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" );
192 continue;
193 }
194 QDomNode filenameNode = instrumentNode.firstChildElement( "filename" );
195
196 if ( ! filenameNode.isNull() )
197 {
198 return false;
199 }
200 else
201 {
202 unsigned nLayer = 0;
203 QDomNode instrumentComponentNode = instrumentNode.firstChildElement("instrumentComponent");
204 if (instrumentComponentNode.isNull())
205 {
206 instrumentComponentNode = instrumentNode;
207 }
208
209 QDomNode layerNode = instrumentComponentNode.firstChildElement( "layer" );
210 while ( ! layerNode.isNull() )
211 {
212 if ( nLayer >= MAX_LAYERS )
213 {
214 printf("nLayer >= MAX_LAYERS\n");
215 break;
216 }
217 QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" );
218 QString sMode = LocalFileMng::readXmlString( layerNode, "smode", "forward" );
219
220 if ( nLayer == 0 )
221 {
222 drum_track[sId] = ( InstrumentTrack * ) Track::create( Track::InstrumentTrack,Engine::getBBTrackContainer() );
223 drum_track[sId]->volumeModel()->setValue( fVolume * 100 );
224 drum_track[sId]->panningModel()->setValue( ( fPan_R - fPan_L ) * 100 );
225 ins = drum_track[sId]->loadInstrument( "audiofileprocessor" );
226 ins->loadFile( sFilename );
227 }
228 nLayer++;
229 layerNode = ( QDomNode ) layerNode.nextSiblingElement( "layer" );
230 }
231 }
232
233 instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" );
234 }
235 if ( instrumentList_count == 0 )
236 {
237 return false;
238 }
239 }
240 else
241 {
242 return false;
243 }
244 QDomNode patterns = songNode.firstChildElement( "patternList" );
245 int pattern_count = 0;
246 int nbb = Engine::getBBTrackContainer()->numOfBBs();
247 QDomNode patternNode = patterns.firstChildElement( "pattern" );
248 int pn = 1;
249 while ( !patternNode.isNull() )
250 {
251 if ( pn > 0 )
252 {
253 pattern_count++;
254 s->addBBTrack();
255 pn = 0;
256 }
257 QString sName; // name
258 sName = LocalFileMng::readXmlString( patternNode, "name", sName );
259
260 QString sCategory = ""; // category
261 sCategory = LocalFileMng::readXmlString( patternNode, "category", sCategory ,false ,false );
262 int nSize = -1;
263 nSize = LocalFileMng::readXmlInt( patternNode, "size", nSize, false, false );
264 pattern_length[sName] = nSize;
265 QDomNode pNoteListNode = patternNode.firstChildElement( "noteList" );
266 if ( ! pNoteListNode.isNull() ) {
267 QDomNode noteNode = pNoteListNode.firstChildElement( "note" );
268 while ( ! noteNode.isNull() ) {
269 int nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 );
270 float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f );
271 float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 );
272 float fPan_R = LocalFileMng::readXmlFloat( noteNode, "pan_R", 0.5 );
273 QString sKey = LocalFileMng::readXmlString( noteNode, "key", "C0", false, false );
274 QString nNoteOff = LocalFileMng::readXmlString( noteNode, "note_off", "false", false, false );
275
276 QString instrId = LocalFileMng::readXmlString( noteNode, "instrument", 0,false, false );
277 int i = pattern_count - 1 + nbb;
278 pattern_id[sName] = pattern_count - 1;
279 Pattern*p = dynamic_cast<Pattern*>( drum_track[instrId]->getTCO( i ) );
280 Note n;
281 n.setPos( nPosition );
282 if ( (nPosition + 48) <= nSize )
283 {
284 n.setLength( 48 );
285 }
286 else
287 {
288 n.setLength( nSize - nPosition );
289 }
290 n.setVolume( fVelocity * 100 );
291 n.setPanning( ( fPan_R - fPan_L ) * 100 );
292 n.setKey( NoteKey::stringToNoteKey( sKey ) );
293 p->addNote( n,false );
294 pn = pn + 1;
295 noteNode = ( QDomNode ) noteNode.nextSiblingElement( "note" );
296 }
297 }
298 patternNode = ( QDomNode ) patternNode.nextSiblingElement( "pattern" );
299 }
300 // Pattern sequence
301 QDomNode patternSequenceNode = songNode.firstChildElement( "patternSequence" );
302 QDomNode groupNode = patternSequenceNode.firstChildElement( "group" );
303 int pos = 0;
304 while ( !groupNode.isNull() )
305 {
306 int best_length = 0;
307 QDomNode patternId = groupNode.firstChildElement( "patternID" );
308 while ( !patternId.isNull() )
309 {
310 QString patId = patternId.firstChild().nodeValue();
311 patternId = ( QDomNode ) patternId.nextSiblingElement( "patternID" );
312
313 int i = pattern_id[patId]+song_num_tracks;
314 Track *t = ( BBTrack * ) s->tracks().at( i );
315 TrackContentObject *tco = t->createTCO( pos );
316 tco->movePosition( pos );
317
318
319 if ( pattern_length[patId] > best_length )
320 {
321 best_length = pattern_length[patId];
322 }
323 }
324 pos = pos + best_length;
325 groupNode = groupNode.nextSiblingElement( "group" );
326 }
327
328 if ( pattern_count == 0 )
329 {
330 return false;
331 }
332 return true;
333 }
tryImport(TrackContainer * tc)334 bool HydrogenImport::tryImport( TrackContainer* tc )
335 {
336 if( openFile() == false )
337 {
338 return false;
339 }
340 return readSong();
341 }
342
343
344
345 extern "C"
346 {
347
348 // necessary for getting instance out of shared lib
lmms_plugin_main(Model *,void * _data)349 Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
350 {
351 return new HydrogenImport( QString::fromUtf8(
352 static_cast<const char *>( _data ) ) );
353 }
354
355
356 }
357
358