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