Files
lmms/plugins/HydrogenImport/HydrogenImport.cpp
Dalton Messmer e6776bcfe5 Remove usage of QTextCodec in Hydrogen Import plugin (#7562)
* Remove QTextCodec

QTextCodec was removed from Qt6 and is only available through the
Qt5Compat module.

QTextCodec was only used by the HydrogenImport plugin when importing old
Hydrogen files that were saved using TinyXML before it supported UTF-8.
HydrogenImport would use QTextCodec to try to get the current encoding
from the locale, and then use that as a best guess for interpreting the
XML data in the unspecified encoding it was saved in. None of this was
ever reliable, since the encoding of the computer that saved the
Hydrogen file might not be the same as the computer running LMMS and
importing that file.

There is no good solution here, so I decided to simply assume the old
Hydrogen files are UTF-8 encoded. The worst that can happen is someone's
ancient Hydrogen files containing non-ASCII text of some random encoding
becomes mojibake'd after importing into LMMS, which is something that
already could have happened.

* Clean up a little
2024-10-23 13:17:14 -04:00

359 lines
8.9 KiB
C++

#include "HydrogenImport.h"
#include <QDomDocument>
#include "LocalFileMng.h"
#include "Song.h"
#include "Engine.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
#include "Note.h"
#include "MidiClip.h"
#include "PatternStore.h"
#include "Track.h"
#include "plugin_export.h"
#define MAX_LAYERS 4
namespace lmms
{
extern "C"
{
Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor =
{
LMMS_STRINGIFY( PLUGIN_NAME ),
"Hydrogen Import",
QT_TRANSLATE_NOOP( "PluginBrowser",
"Filter for importing Hydrogen files into LMMS" ),
"frank mather",
0x0100,
Plugin::Type::ImportFilter,
nullptr,
nullptr,
nullptr,
} ;
}
QString filename;
class NoteKey
{
public:
enum class Key {
C = 0,
Cs,
D,
Ef,
E,
F,
Fs,
G,
Af,
A,
Bf,
B,
};
static int stringToNoteKey( const QString& str )
{
auto m_key = Key::C;
QString sKey = str.left( str.length() - 1 );
QString sOct = str.mid( str.length() - 1, str.length() );
if ( sKey.endsWith( "-" ) )
{
sKey.replace( "-", "" );
sOct.insert( 0, "-" );
}
int nOctave = sOct.toInt();
if ( sKey == "C" )
{
m_key = Key::C;
}
else if ( sKey == "Cs" )
{
m_key = Key::Cs;
}
else if ( sKey == "D" )
{
m_key = Key::D;
}
else if ( sKey == "Ef" )
{
m_key = Key::Ef;
}
else if ( sKey == "E" )
{
m_key = Key::E;
}
else if ( sKey == "F" )
{
m_key = Key::F;
}
else if ( sKey == "Fs" )
{
m_key = Key::Fs;
}
else if ( sKey == "G" )
{
m_key = Key::G;
}
else if ( sKey == "Af" )
{
m_key = Key::Af;
}
else if ( sKey == "A" )
{
m_key = Key::A;
}
else if ( sKey == "Bf" )
{
m_key = Key::Bf;
}
else if ( sKey == "B" ) {
m_key = Key::B;
}
// Hydrogen records MIDI notes from C-1 to B5, and exports them as a number ranging from -3 to 3
return static_cast<int>(m_key) + ((nOctave + 3) * 12);
}
};
HydrogenImport::HydrogenImport( const QString & _file ) :
ImportFilter( _file, &hydrogenimport_plugin_descriptor )
{
filename = _file;
}
Instrument * ins;
bool HydrogenImport::readSong()
{
QHash<QString, InstrumentTrack *> drum_track;
QHash<QString, int> pattern_length;
QHash<QString, int> pattern_id;
Song *s = Engine::getSong();
int song_num_tracks = s->tracks().size();
if ( QFile( filename ).exists() == false )
{
printf( "Song file not found \n" );
return false;
}
QDomDocument doc = LocalFileMng::openXmlDocument( filename );
QDomNodeList nodeList = doc.elementsByTagName( "song" );
if( nodeList.isEmpty() )
{
printf( "Error reading song: song node not found\n" );
return false;
}
QDomNode songNode = nodeList.at( 0 );
QString m_sSongVersion = LocalFileMng::readXmlString( songNode , "version", "Unknown version" );
QString sName( LocalFileMng::readXmlString( songNode, "name", "Untitled Song" ) );
QString sAuthor( LocalFileMng::readXmlString( songNode, "author", "Unknown Author" ) );
QString sNotes( LocalFileMng::readXmlString( songNode, "notes", "..." ) );
QString sLicense( LocalFileMng::readXmlString( songNode, "license", "Unknown license" ) );
QString sMode = LocalFileMng::readXmlString( songNode, "mode", "pattern" );
QDomNode instrumentListNode = songNode.firstChildElement( "instrumentList" );
if ( ( ! instrumentListNode.isNull() ) )
{
int instrumentList_count = 0;
QDomNode instrumentNode;
instrumentNode = instrumentListNode.firstChildElement( "instrument" );
while ( ! instrumentNode.isNull() )
{
instrumentList_count++;
QString sId = LocalFileMng::readXmlString( instrumentNode, "id", "" ); // instrument id
QString sDrumkit = LocalFileMng::readXmlString( instrumentNode, "drumkit", "" ); // drumkit
QString sName = LocalFileMng::readXmlString( instrumentNode, "name", "" ); // name
float fVolume = LocalFileMng::readXmlFloat( instrumentNode, "volume", 1.0 ); // volume
float fPan_L = LocalFileMng::readXmlFloat( instrumentNode, "pan_L", 0.5 ); // pan L
float fPan_R = LocalFileMng::readXmlFloat( instrumentNode, "pan_R", 0.5 ); // pan R
if ( sId.isEmpty() ) {
printf( "Empty ID for instrument. skipping \n" );
instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" );
continue;
}
QDomNode filenameNode = instrumentNode.firstChildElement( "filename" );
if ( ! filenameNode.isNull() )
{
return false;
}
else
{
unsigned nLayer = 0;
QDomNode instrumentComponentNode = instrumentNode.firstChildElement("instrumentComponent");
if (instrumentComponentNode.isNull())
{
instrumentComponentNode = instrumentNode;
}
QDomNode layerNode = instrumentComponentNode.firstChildElement( "layer" );
while ( ! layerNode.isNull() )
{
if ( nLayer >= MAX_LAYERS )
{
printf("nLayer >= MAX_LAYERS\n");
break;
}
QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" );
QString sMode = LocalFileMng::readXmlString( layerNode, "smode", "forward" );
if ( nLayer == 0 )
{
drum_track[sId] = static_cast<InstrumentTrack*>(
Track::create(Track::Type::Instrument, Engine::patternStore())
);
drum_track[sId]->volumeModel()->setValue( fVolume * 100 );
drum_track[sId]->panningModel()->setValue( ( fPan_R - fPan_L ) * 100 );
ins = drum_track[sId]->loadInstrument( "audiofileprocessor" );
ins->loadFile( sFilename );
}
nLayer++;
layerNode = ( QDomNode ) layerNode.nextSiblingElement( "layer" );
}
}
instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" );
}
if ( instrumentList_count == 0 )
{
return false;
}
}
else
{
return false;
}
QDomNode patterns = songNode.firstChildElement( "patternList" );
int pattern_count = 0;
int existing_patterns = Engine::patternStore()->numOfPatterns();
QDomNode patternNode = patterns.firstChildElement( "pattern" );
int pn = 1;
while ( !patternNode.isNull() )
{
if ( pn > 0 )
{
pattern_count++;
s->addPatternTrack();
pn = 0;
}
QString sName; // name
sName = LocalFileMng::readXmlString( patternNode, "name", sName );
QString sCategory = ""; // category
sCategory = LocalFileMng::readXmlString( patternNode, "category", sCategory ,false ,false );
int nSize = -1;
nSize = LocalFileMng::readXmlInt( patternNode, "size", nSize, false, false );
pattern_length[sName] = nSize;
QDomNode pNoteListNode = patternNode.firstChildElement( "noteList" );
if ( ! pNoteListNode.isNull() ) {
QDomNode noteNode = pNoteListNode.firstChildElement( "note" );
while ( ! noteNode.isNull() ) {
int nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 );
float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f );
float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 );
float fPan_R = LocalFileMng::readXmlFloat( noteNode, "pan_R", 0.5 );
QString sKey = LocalFileMng::readXmlString( noteNode, "key", "C0", false, false );
QString nNoteOff = LocalFileMng::readXmlString( noteNode, "note_off", "false", false, false );
QString instrId = LocalFileMng::readXmlString( noteNode, "instrument", 0,false, false );
int i = pattern_count - 1 + existing_patterns;
pattern_id[sName] = pattern_count - 1;
auto p = dynamic_cast<MidiClip*>(drum_track[instrId]->getClip(i));
Note n;
n.setPos( nPosition );
if ( (nPosition + 48) <= nSize )
{
n.setLength( 48 );
}
else
{
n.setLength( nSize - nPosition );
}
n.setVolume( fVelocity * 100 );
n.setPanning( ( fPan_R - fPan_L ) * 100 );
n.setKey( NoteKey::stringToNoteKey( sKey ) );
p->addNote( n,false );
pn = pn + 1;
noteNode = ( QDomNode ) noteNode.nextSiblingElement( "note" );
}
}
patternNode = ( QDomNode ) patternNode.nextSiblingElement( "pattern" );
}
// MidiClip sequence
QDomNode patternSequenceNode = songNode.firstChildElement( "patternSequence" );
QDomNode groupNode = patternSequenceNode.firstChildElement( "group" );
int pos = 0;
while ( !groupNode.isNull() )
{
int best_length = 0;
QDomNode patternId = groupNode.firstChildElement( "patternID" );
while ( !patternId.isNull() )
{
QString patId = patternId.firstChild().nodeValue();
patternId = ( QDomNode ) patternId.nextSiblingElement( "patternID" );
int i = pattern_id[patId]+song_num_tracks;
Track* t = s->tracks().at(i);
t->createClip(pos);
if ( pattern_length[patId] > best_length )
{
best_length = pattern_length[patId];
}
}
pos = pos + best_length;
groupNode = groupNode.nextSiblingElement( "group" );
}
if ( pattern_count == 0 )
{
return false;
}
return true;
}
bool HydrogenImport::tryImport( TrackContainer* tc )
{
if( openFile() == false )
{
return false;
}
return readSong();
}
extern "C"
{
// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data )
{
return new HydrogenImport( QString::fromUtf8(
static_cast<const char *>( _data ) ) );
}
}
} // namespace lmms