hydrogen 1.2.6
SMF.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <core/SMF/SMF.h>
24#include <core/Basics/Pattern.h>
26#include <core/Basics/Note.h>
27#include <core/Basics/Song.h>
31
32#include <QFile>
33#include <QTextStream>
34
35namespace H2Core
36{
37
38SMFHeader::SMFHeader( int nFormat, int nTracks, int nTPQN )
39 : m_nFormat( nFormat )
40 , m_nTracks( nTracks )
41 , m_nTPQN( nTPQN )
42{
43
44}
45
46
48{
49 INFOLOG( "DESTROY" );
50}
51
53 m_nTracks++;
54}
55
56QByteArray SMFHeader::getBuffer() const
57{
58 SMFBuffer buffer;
59
60 buffer.writeDWord( 1297377380 ); // MThd
61 buffer.writeDWord( 6 ); // Header length = 6
62 buffer.writeWord( m_nFormat );
63 buffer.writeWord( m_nTracks );
64 buffer.writeWord( m_nTPQN );
65
66 return buffer.m_buffer;
67}
68
69QString SMFHeader::toQString() const {
70 return QString( getBuffer().toHex( ' ' ) );
71}
72
73
74
75// :::::::::::::::
76
78 : Object()
79{
80
81}
82
83
84
86{
87 INFOLOG( "DESTROY" );
88
89 for ( unsigned i = 0; i < m_eventList.size(); i++ ) {
90 delete m_eventList[ i ];
91 }
92}
93
94
95
96QByteArray SMFTrack::getBuffer() const
97{
98 QByteArray trackData;
99
100 for ( unsigned i = 0; i < m_eventList.size(); i++ ) {
101 SMFEvent *pEv = m_eventList[ i ];
102 auto buf = pEv->getBuffer();
103
104 for ( unsigned j = 0; j < buf.size(); j++ ) {
105 trackData.push_back( buf[ j ] );
106 }
107 }
108
109
110 SMFBuffer buf;
111
112 buf.writeDWord( 1297379947 ); // MTrk
113 buf.writeDWord( trackData.size() + 4 ); // Track length
114
115 auto trackBuf = buf.getBuffer();
116
117 for ( unsigned i = 0; i < trackData.size(); i++ ) {
118 trackBuf.push_back( trackData[i] );
119 }
120
121
122 // track end
123 trackBuf.push_back( static_cast<char>(0x00) ); // delta
124 trackBuf.push_back( static_cast<char>(0xFF) );
125 trackBuf.push_back( static_cast<char>(0x2F) );
126 trackBuf.push_back( static_cast<char>(0x00) );
127
128
129
130 return trackBuf;
131}
132
133QString SMFTrack::toQString() const {
134 return QString( getBuffer().toHex( ' ' ) );
135}
136
138{
139 m_eventList.push_back( pEvent );
140}
141
142
143
144// ::::::::::::::::::::::
145
146SMF::SMF(int nFormat, int nTPQN )
147{
148
149
150 m_pHeader = new SMFHeader( nFormat, 0, nTPQN );
151}
152
153
154
156{
157 INFOLOG( "DESTROY" );
158
159 delete m_pHeader;
160
161 for ( unsigned i = 0; i < m_trackList.size(); i++ ) {
162 delete m_trackList[i];
163 }
164}
165
166
167
168void SMF::addTrack( SMFTrack *pTrack )
169{
170 m_pHeader->addTrack();
171 m_trackList.push_back( pTrack );
172}
173
174
175
176QByteArray SMF::getBuffer() const
177{
178 // header
179 auto smfBuffer = m_pHeader->getBuffer();
180
181 // tracks
182 for ( unsigned nTrack = 0; nTrack < m_trackList.size(); nTrack++ ) {
183 SMFTrack *pTrack = m_trackList[ nTrack ];
184 smfBuffer.append( pTrack->getBuffer() );
185 }
186
187 return smfBuffer;
188}
189
190QString SMF::toQString() const {
191 return QString( getBuffer().toHex( ' ' ) );
192}
193
194
195
196// :::::::::::::::::::...
197
198constexpr unsigned int TPQN = 192;
199constexpr unsigned int DRUM_CHANNEL = 9;
200constexpr unsigned int NOTE_LENGTH = 12;
201
203{
204
205}
206
207
209{
210 INFOLOG( "DESTROY" );
211}
212
213
214SMFTrack* SMFWriter::createTrack0( std::shared_ptr<Song> pSong ) {
215 SMFTrack *pTrack0 = new SMFTrack();
216 pTrack0->addEvent( new SMFCopyRightNoticeMetaEvent( pSong->getAuthor() , 0 ) );
217 pTrack0->addEvent( new SMFTrackNameMetaEvent( pSong->getName() , 0 ) );
218 pTrack0->addEvent( new SMFSetTempoMetaEvent( pSong->getBpm() , 0 ) );
219 pTrack0->addEvent( new SMFTimeSignatureMetaEvent( 4 , 4 , 24 , 8 , 0 ) );
220 return pTrack0;
221}
222
223
224void SMFWriter::save( const QString& sFilename, std::shared_ptr<Song> pSong )
225{
226 INFOLOG( QString( "Export MIDI to [%1]" ).arg( sFilename ) );
227
228 SMF* pSmf = createSMF( pSong );
229
230 AutomationPath* pAutomationPath = pSong->getVelocityAutomationPath();
231
232 // here writers must prepare to receive pattern events
233 prepareEvents( pSong, pSmf );
234
235 auto pInstrumentList = pSong->getInstrumentList();
236 // ogni pattern sara' una diversa traccia
237 int nTick = 1;
238 for ( unsigned nPatternList = 0 ;
239 nPatternList < pSong->getPatternGroupVector()->size() ;
240 nPatternList++ ) {
241 // infoLog( "[save] pattern list pos: " + toString( nPatternList ) );
242 PatternList *pPatternList =
243 ( *(pSong->getPatternGroupVector()) )[ nPatternList ];
244
245 int nStartTicks = nTick;
246 int nMaxPatternLength = 0;
247 for ( unsigned nPattern = 0 ;
248 nPattern < pPatternList->size() ;
249 nPattern++ ) {
250 Pattern *pPattern = pPatternList->get( nPattern );
251 // infoLog( " |-> pattern: " + pPattern->getName() );
252 if ( ( int )pPattern->get_length() > nMaxPatternLength ) {
253 nMaxPatternLength = pPattern->get_length();
254 }
255
256 for ( unsigned nNote = 0; nNote < pPattern->get_length(); nNote++ ) {
257 const Pattern::notes_t* notes = pPattern->get_notes();
258 FOREACH_NOTE_CST_IT_BOUND_LENGTH(notes,it,nNote,pPattern) {
259 Note *pNote = it->second;
260 if ( pNote ) {
261 float rnd = (float)rand()/(float)RAND_MAX;
262 if ( pNote->get_probability() < rnd ) {
263 continue;
264 }
265
266 float fPos = nPatternList + (float)nNote/(float)nMaxPatternLength;
267 float fVelocityAdjustment = pAutomationPath->get_value(fPos);
268 int nVelocity =
269 (int)( 127.0 * pNote->get_velocity() * fVelocityAdjustment );
270
271 auto pInstr = pNote->get_instrument();
272 int nPitch = pNote->get_midi_key();
273
274 int nChannel = pInstr->get_midi_out_channel();
275 if ( nChannel == -1 ) {
276 nChannel = DRUM_CHANNEL;
277 }
278
279 int nLength = pNote->get_length();
280 if ( nLength == -1 ) {
281 nLength = NOTE_LENGTH;
282 }
283
284 // get events for specific instrument
285 EventList* eventList = getEvents(pSong, pInstr);
286 eventList->push_back(
287 new SMFNoteOnEvent(
288 nStartTicks + nNote,
289 nChannel,
290 nPitch,
291 nVelocity
292 )
293 );
294
295 eventList->push_back(
296 new SMFNoteOffEvent(
297 nStartTicks + nNote + nLength,
298 nChannel,
299 nPitch,
300 nVelocity
301 )
302 );
303 }
304 }
305 }
306 }
307 nTick += nMaxPatternLength;
308 }
309
310 //tracks creation
311 packEvents(pSong, pSmf);
312
313 saveSMF(sFilename, pSmf);
314 delete pSmf;
315}
316
317
319{
320 // awful bubble sort..
321 for ( unsigned i = 0; i < pEvents->size(); i++ ) {
322 for ( std::vector<SMFEvent*>::iterator it = pEvents->begin() ;
323 it != ( pEvents->end() - 1 ) ;
324 it++ ) {
325 SMFEvent *pEvent = *it;
326 SMFEvent *pNextEvent = *( it + 1 );
327 if ( pNextEvent->m_nTicks < pEvent->m_nTicks ) {
328 // swap
329 *it = pNextEvent;
330 *( it +1 ) = pEvent;
331 }
332 }
333 }
334}
335
336
337void SMFWriter::saveSMF( const QString& sFilename, SMF* pSmf )
338{
339 // save the midi file
340 QFile file( sFilename );
341 if ( ! file.open( QIODevice::WriteOnly ) ) {
342 ERRORLOG( QString( "Unable to open file [%1] for writing" )
343 .arg( sFilename ) );
344 return;
345 }
346
347 QDataStream stream( &file );
348 const auto buffer = pSmf->getBuffer();
349 stream.writeRawData( buffer.constData(), buffer.size() );
350
351 file.close();
352}
353
354
355// SMF1Writer - base class for two smf1 writers
356
361
362
366
367
368SMF* SMF1Writer::createSMF( std::shared_ptr<Song> pSong ){
369 SMF* pSmf = new SMF( 1, TPQN );
370 // Standard MIDI format 1 files should have the first track being the tempo map
371 // which is a track that contains global meta events only.
372
373 SMFTrack* pTrack0 = createTrack0( pSong );
374 pSmf->addTrack( pTrack0 );
375
376 // Standard MIDI Format 1 files should have note events in tracks =>2
377 return pSmf;
378}
379
380
381// SMF1 MIDI SINGLE EXPROT
382
383
389
390
391
395
396
397
398EventList* SMF1WriterSingle::getEvents( std::shared_ptr<Song> pSong, std::shared_ptr<Instrument> pInstr )
399{
400 return &m_eventList;
401}
402
403
404
405void SMF1WriterSingle::prepareEvents( std::shared_ptr<Song> pSong, SMF* pSmf )
406{
407 m_eventList.clear();
408}
409
410
411
412void SMF1WriterSingle::packEvents( std::shared_ptr<Song> pSong, SMF* pSmf )
413{
415
416 SMFTrack *pTrack1 = new SMFTrack();
417 pSmf->addTrack( pTrack1 );
418
419 unsigned nLastTick = 1;
420 for( auto& pEvent : m_eventList ) {
421 pEvent->m_nDeltaTime = ( pEvent->m_nTicks - nLastTick ) * 4;
422 nLastTick = pEvent->m_nTicks;
423
424 // infoLog( " pos: " + toString( (*it)->m_nTicks ) + ", delta: "
425 // + toString( (*it)->m_nDeltaTime ) );
426
427 pTrack1->addEvent( pEvent );
428 }
429
430 m_eventList.clear();
431}
432
433
434
435// SMF1 MIDI MULTI EXPORT
436
442
443
447
448
449void SMF1WriterMulti::prepareEvents( std::shared_ptr<Song> pSong, SMF* pSmf )
450{
451 auto pInstrumentList = pSong->getInstrumentList();
452 m_eventLists.clear();
453 for( unsigned nInstr=0; nInstr < pInstrumentList->size(); nInstr++ ){
454 m_eventLists.push_back( new EventList() );
455 }
456}
457
458
459EventList* SMF1WriterMulti::getEvents( std::shared_ptr<Song> pSong, std::shared_ptr<Instrument> pInstr )
460{
461 int nInstr = pSong->getInstrumentList()->index(pInstr);
462 EventList* pEventList = m_eventLists.at( nInstr );
463
464 return pEventList;
465}
466
467
468void SMF1WriterMulti::packEvents( std::shared_ptr<Song> pSong, SMF* pSmf )
469{
470 auto pInstrumentList = pSong->getInstrumentList();
471 for ( unsigned nTrack = 0; nTrack < m_eventLists.size(); nTrack++ ) {
472 EventList* pEventList = m_eventLists.at( nTrack );
473 auto instrument = pInstrumentList->get( nTrack );
474
475 sortEvents( pEventList );
476
477 SMFTrack *pTrack = new SMFTrack();
478 pSmf->addTrack( pTrack );
479
480 //Set instrument name as track name
481 pTrack->addEvent( new SMFTrackNameMetaEvent( instrument->get_name() , 0 ) );
482
483 unsigned nLastTick = 1;
484 for ( std::vector<SMFEvent*>::iterator it = pEventList->begin();
485 it != pEventList->end();
486 it++ ) {
487 SMFEvent *pEvent = *it;
488 pEvent->m_nDeltaTime = ( pEvent->m_nTicks - nLastTick ) * 4;
489 nLastTick = pEvent->m_nTicks;
490
491 pTrack->addEvent( *it );
492 }
493
494 // we can safely delete vector with events now
495 delete pEventList;
496 }
497 m_eventLists.clear();
498}
499
500
501// SMF0 MIDI EXPORT
502
504 : SMFWriter(),
505 m_pTrack( nullptr ),
507{
508}
509
510
514
515
516SMF* SMF0Writer::createSMF( std::shared_ptr<Song> pSong ){
517 // MIDI files format 0 have all their events in one track
518 SMF* pSmf = new SMF( 0, TPQN );
519 m_pTrack = createTrack0( pSong );
520 pSmf->addTrack( m_pTrack );
521 return pSmf;
522}
523
524
525EventList* SMF0Writer::getEvents( std::shared_ptr<Song> pSong, std::shared_ptr<Instrument> pInstr )
526{
527 return &m_eventList;
528}
529
530
531void SMF0Writer::prepareEvents( std::shared_ptr<Song> pSong, SMF* pSmf )
532{
533 m_eventList.clear();
534}
535
536
537void SMF0Writer::packEvents( std::shared_ptr<Song> pSong, SMF* pSmf )
538{
540
541 unsigned nLastTick = 1;
542 for ( std::vector<SMFEvent*>::iterator it = m_eventList.begin();
543 it != m_eventList.end();
544 it++ ) {
545 SMFEvent *pEvent = *it;
546 pEvent->m_nDeltaTime = ( pEvent->m_nTicks - nLastTick ) * 4;
547 nLastTick = pEvent->m_nTicks;
548
549 m_pTrack->addEvent( *it );
550 }
551
552 m_eventList.clear();
553}
554
555
556};
#define INFOLOG(x)
Definition Object.h:240
#define ERRORLOG(x)
Definition Object.h:242
#define FOREACH_NOTE_CST_IT_BOUND_LENGTH(_notes, _it, _bound, _pattern)
Iterate over all notes in column _bound in an immutable way if it is contained in _pattern.
Definition Pattern.h:290
float get_value(float x) const noexcept
Get value at given location.
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:101
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:499
int get_length() const
__length accessor
Definition Note.h:559
int get_midi_key() const
return scaled key for midi output, !
Definition Note.h:681
float get_velocity() const
__velocity accessor
Definition Note.h:539
float get_probability() const
Definition Note.h:609
PatternList is a collection of patterns.
Definition PatternList.h:43
int size() const
returns the numbers of patterns
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
Definition Pattern.h:46
int get_length() const
set the denominator of the pattern
Definition Pattern.h:340
const notes_t * get_notes() const
get the virtual pattern set
Definition Pattern.h:355
std::multimap< int, Note * > notes_t
< multimap note type
Definition Pattern.h:50
virtual void prepareEvents(std::shared_ptr< Song > pSong, SMF *pSmf) override
Definition SMF.cpp:531
SMFTrack * m_pTrack
Definition SMF.h:188
virtual EventList * getEvents(std::shared_ptr< Song > pSong, std::shared_ptr< Instrument > pInstr) override
Definition SMF.cpp:525
EventList m_eventList
Definition SMF.h:189
virtual ~SMF0Writer()
Definition SMF.cpp:511
virtual SMF * createSMF(std::shared_ptr< Song > pSong) override
Definition SMF.cpp:516
virtual void packEvents(std::shared_ptr< Song > pSong, SMF *pSmf) override
Definition SMF.cpp:537
virtual void prepareEvents(std::shared_ptr< Song > pSong, SMF *pSmf) override
Definition SMF.cpp:449
virtual EventList * getEvents(std::shared_ptr< Song > pSong, std::shared_ptr< Instrument > pInstr) override
Definition SMF.cpp:459
std::vector< EventList * > m_eventLists
Definition SMF.h:169
virtual ~SMF1WriterMulti()
Definition SMF.cpp:444
virtual void packEvents(std::shared_ptr< Song > pSong, SMF *pSmf) override
Definition SMF.cpp:468
virtual void prepareEvents(std::shared_ptr< Song > pSong, SMF *pSmf) override
Definition SMF.cpp:405
virtual EventList * getEvents(std::shared_ptr< Song > pSong, std::shared_ptr< Instrument > pInstr) override
Definition SMF.cpp:398
EventList m_eventList
Definition SMF.h:152
virtual ~SMF1WriterSingle()
Definition SMF.cpp:392
virtual void packEvents(std::shared_ptr< Song > pSong, SMF *pSmf) override
Definition SMF.cpp:412
virtual SMF * createSMF(std::shared_ptr< Song > pSong) override
Definition SMF.cpp:368
virtual ~SMF1Writer()
Definition SMF.cpp:363
virtual QByteArray getBuffer() const =0
QByteArray m_buffer
Definition SMFEvent.h:48
QByteArray getBuffer()
Definition SMFEvent.h:38
void writeDWord(long nVal)
Definition SMFEvent.cpp:44
void writeWord(int nVal)
Definition SMFEvent.cpp:37
int m_nTPQN
ticks per quarter note
Definition SMF.h:55
SMFHeader(int nFormat, int nTracks, int nTPQN)
Definition SMF.cpp:38
int m_nTracks
number of tracks
Definition SMF.h:54
virtual QByteArray getBuffer() const override
Definition SMF.cpp:56
void addTrack()
Definition SMF.cpp:52
virtual QString toQString() const override
Definition SMF.cpp:69
int m_nFormat
SMF format.
Definition SMF.h:53
virtual QByteArray getBuffer() const override
Definition SMF.cpp:96
virtual QString toQString() const override
Definition SMF.cpp:133
std::vector< SMFEvent * > m_eventList
Definition SMF.h:75
void addEvent(SMFEvent *pEvent)
Definition SMF.cpp:137
void saveSMF(const QString &sFilename, SMF *pSmf)
Definition SMF.cpp:337
virtual void packEvents(std::shared_ptr< Song > pSong, SMF *pSmf)=0
virtual SMF * createSMF(std::shared_ptr< Song > pSong)=0
void sortEvents(EventList *pEventList)
Definition SMF.cpp:318
virtual EventList * getEvents(std::shared_ptr< Song > pSong, std::shared_ptr< Instrument > pInstr)=0
virtual ~SMFWriter()
Definition SMF.cpp:208
SMFTrack * createTrack0(std::shared_ptr< Song > pSong)
Definition SMF.cpp:214
virtual void prepareEvents(std::shared_ptr< Song > pSong, SMF *pSmf)=0
void save(const QString &sFilename, std::shared_ptr< Song > pSong)
Definition SMF.cpp:224
void addTrack(SMFTrack *pTrack)
Definition SMF.cpp:168
SMFHeader * m_pHeader
Definition SMF.h:95
virtual QByteArray getBuffer() const override
Definition SMF.cpp:176
std::vector< SMFTrack * > m_trackList
Definition SMF.h:93
virtual QString toQString() const override
Definition SMF.cpp:190
SMF(int nFormat, int nTPQN)
Definition SMF.cpp:146
constexpr unsigned int DRUM_CHANNEL
Definition SMF.cpp:199
constexpr unsigned int NOTE_LENGTH
Definition SMF.cpp:200
constexpr unsigned int TPQN
Definition SMF.cpp:198
std::vector< SMFEvent * > EventList
Definition SMF.h:100