Update midi_processing to latest version, fixing some severe MIDI file handling issues, including Standard MIDI file SysEx and SysEx continuation handling

CQTexperiment
Christopher Snowhill 2020-04-14 02:10:52 -07:00
parent 798cc4ce43
commit f5c7c4d49a
15 changed files with 2393 additions and 2105 deletions

File diff suppressed because it is too large Load Diff

View File

@ -13,36 +13,36 @@
struct midi_event struct midi_event
{ {
enum enum
{ {
max_static_data_count = 16 max_static_data_count = 16
}; };
enum event_type enum event_type
{ {
note_off = 0, note_off = 0,
note_on, note_on,
polyphonic_aftertouch, polyphonic_aftertouch,
control_change, control_change,
program_change, program_change,
channel_aftertouch, channel_aftertouch,
pitch_wheel, pitch_wheel,
extended extended
}; };
unsigned long m_timestamp; unsigned long m_timestamp;
event_type m_type; event_type m_type;
unsigned m_channel; unsigned m_channel;
unsigned long m_data_count; unsigned long m_data_count;
uint8_t m_data[max_static_data_count]; uint8_t m_data[max_static_data_count];
std::vector<uint8_t> m_ext_data; std::vector<uint8_t> m_ext_data;
midi_event() : m_timestamp(0), m_type(note_off), m_channel(0), m_data_count(0) { } midi_event() : m_timestamp(0), m_type(note_off), m_channel(0), m_data_count(0) { }
midi_event( const midi_event & p_in ); midi_event( const midi_event & p_in );
midi_event( unsigned long p_timestamp, event_type p_type, unsigned p_channel, const uint8_t * p_data, std::size_t p_data_count ); midi_event( unsigned long p_timestamp, event_type p_type, unsigned p_channel, const uint8_t * p_data, std::size_t p_data_count );
unsigned long get_data_count() const; unsigned long get_data_count() const;
void copy_data( uint8_t * p_out, unsigned long p_offset, unsigned long p_count ) const; void copy_data( uint8_t * p_out, unsigned long p_offset, unsigned long p_count ) const;
}; };
@ -51,23 +51,24 @@ class midi_track
std::vector<midi_event> m_events; std::vector<midi_event> m_events;
public: public:
midi_track() { } midi_track() { }
midi_track(const midi_track & p_in); midi_track(const midi_track & p_in);
void add_event( const midi_event & p_event ); void add_event( const midi_event & p_event );
std::size_t get_count() const; std::size_t get_count() const;
const midi_event & operator [] ( std::size_t p_index ) const; const midi_event & operator [] ( std::size_t p_index ) const;
midi_event & operator [] ( std::size_t p_index );
void remove_event( unsigned long index ); void remove_event( unsigned long index );
}; };
struct tempo_entry struct tempo_entry
{ {
unsigned long m_timestamp; unsigned long m_timestamp;
unsigned m_tempo; unsigned m_tempo;
tempo_entry() : m_timestamp(0), m_tempo(0) { } tempo_entry() : m_timestamp(0), m_tempo(0) { }
tempo_entry(unsigned long p_timestamp, unsigned p_tempo); tempo_entry(unsigned long p_timestamp, unsigned p_tempo);
}; };
class tempo_map class tempo_map
@ -75,11 +76,12 @@ class tempo_map
std::vector<tempo_entry> m_entries; std::vector<tempo_entry> m_entries;
public: public:
void add_tempo( unsigned p_tempo, unsigned long p_timestamp ); void add_tempo( unsigned p_tempo, unsigned long p_timestamp );
unsigned long timestamp_to_ms( unsigned long p_timestamp, unsigned p_dtx ) const; unsigned long timestamp_to_ms( unsigned long p_timestamp, unsigned p_dtx ) const;
std::size_t get_count() const; std::size_t get_count() const;
const tempo_entry & operator [] ( std::size_t p_index ) const; const tempo_entry & operator [] ( std::size_t p_index ) const;
tempo_entry & operator [] ( std::size_t p_index );
}; };
struct system_exclusive_entry struct system_exclusive_entry
@ -87,8 +89,8 @@ struct system_exclusive_entry
std::size_t m_port; std::size_t m_port;
std::size_t m_offset; std::size_t m_offset;
std::size_t m_length; std::size_t m_length;
system_exclusive_entry() : m_port(0), m_offset(0), m_length(0) { } system_exclusive_entry() : m_port(0), m_offset(0), m_length(0) { }
system_exclusive_entry(const system_exclusive_entry & p_in); system_exclusive_entry(const system_exclusive_entry & p_in);
system_exclusive_entry(std::size_t p_port, std::size_t p_offset, std::size_t p_length); system_exclusive_entry(std::size_t p_port, std::size_t p_offset, std::size_t p_length);
}; };
@ -104,42 +106,42 @@ public:
struct midi_stream_event struct midi_stream_event
{ {
unsigned long m_timestamp; unsigned long m_timestamp;
uint32_t m_event; uint32_t m_event;
midi_stream_event() : m_timestamp(0), m_event(0) { } midi_stream_event() : m_timestamp(0), m_event(0) { }
midi_stream_event(unsigned long p_timestamp, uint32_t p_event); midi_stream_event(unsigned long p_timestamp, uint32_t p_event);
}; };
struct midi_meta_data_item struct midi_meta_data_item
{ {
unsigned long m_timestamp; unsigned long m_timestamp;
std::string m_name; std::string m_name;
std::string m_value; std::string m_value;
midi_meta_data_item() : m_timestamp(0) { } midi_meta_data_item() : m_timestamp(0) { }
midi_meta_data_item(const midi_meta_data_item & p_in); midi_meta_data_item(const midi_meta_data_item & p_in);
midi_meta_data_item(unsigned long p_timestamp, const char * p_name, const char * p_value); midi_meta_data_item(unsigned long p_timestamp, const char * p_name, const char * p_value);
}; };
class midi_meta_data class midi_meta_data
{ {
std::vector<midi_meta_data_item> m_data; std::vector<midi_meta_data_item> m_data;
std::vector<uint8_t> m_bitmap; std::vector<uint8_t> m_bitmap;
public: public:
midi_meta_data() { } midi_meta_data() { }
void add_item( const midi_meta_data_item & p_item ); void add_item( const midi_meta_data_item & p_item );
void append( const midi_meta_data & p_data ); void append( const midi_meta_data & p_data );
bool get_item( const char * p_name, midi_meta_data_item & p_out ) const; bool get_item( const char * p_name, midi_meta_data_item & p_out ) const;
bool get_bitmap( std::vector<uint8_t> & p_out ); bool get_bitmap( std::vector<uint8_t> & p_out );
void assign_bitmap( std::vector<uint8_t>::const_iterator const& begin, std::vector<uint8_t>::const_iterator const& end ); void assign_bitmap( std::vector<uint8_t>::const_iterator const& begin, std::vector<uint8_t>::const_iterator const& end );
std::size_t get_count() const; std::size_t get_count() const;
const midi_meta_data_item & operator [] ( std::size_t p_index ) const; const midi_meta_data_item & operator [] ( std::size_t p_index ) const;
@ -148,16 +150,16 @@ public:
class midi_container class midi_container
{ {
public: public:
enum enum
{ {
clean_flag_emidi = 1 << 0, clean_flag_emidi = 1 << 0,
clean_flag_instruments = 1 << 1, clean_flag_instruments = 1 << 1,
clean_flag_banks = 1 << 2, clean_flag_banks = 1 << 2,
}; };
private: private:
unsigned m_form; unsigned m_form;
unsigned m_dtx; unsigned m_dtx;
std::vector<uint64_t> m_channel_mask; std::vector<uint64_t> m_channel_mask;
std::vector<tempo_map> m_tempo_map; std::vector<tempo_map> m_tempo_map;
std::vector<midi_track> m_tracks; std::vector<midi_track> m_tracks;
@ -166,7 +168,7 @@ private:
std::vector< std::vector< std::string > > m_device_names; std::vector< std::vector< std::string > > m_device_names;
midi_meta_data m_extra_meta_data; midi_meta_data m_extra_meta_data;
std::vector<unsigned long> m_timestamp_end; std::vector<unsigned long> m_timestamp_end;
@ -207,9 +209,9 @@ private:
public: public:
midi_container() { m_device_names.resize( 16 ); } midi_container() { m_device_names.resize( 16 ); }
void initialize( unsigned p_form, unsigned p_dtx ); void initialize( unsigned p_form, unsigned p_dtx );
void add_track( const midi_track & p_track ); void add_track( const midi_track & p_track );
void add_track_event( std::size_t p_track_index, const midi_event & p_event ); void add_track_event( std::size_t p_track_index, const midi_event & p_event );
@ -219,7 +221,7 @@ public:
void merge_tracks( const midi_container & p_source ); void merge_tracks( const midi_container & p_source );
void set_track_count( unsigned count ); void set_track_count( unsigned count );
void set_extra_meta_data( const midi_meta_data & p_data ); void set_extra_meta_data( const midi_meta_data & p_data );
/* /*
* Blah. * Blah.
* Hack 0: Remove channel 16 * Hack 0: Remove channel 16
@ -233,6 +235,17 @@ public:
void promote_to_type1(); void promote_to_type1();
void trim_start();
private:
void trim_range_of_tracks(unsigned long start, unsigned long end);
void trim_tempo_map(unsigned long p_index, unsigned long base_timestamp);
public:
typedef std::string(*split_callback)(uint8_t bank_msb, uint8_t bank_lsb, uint8_t instrument);
void split_by_instrument_changes(split_callback cb = NULL);
unsigned long get_subsong_count() const; unsigned long get_subsong_count() const;
unsigned long get_subsong( unsigned long p_index ) const; unsigned long get_subsong( unsigned long p_index ) const;
@ -245,9 +258,9 @@ public:
unsigned long get_timestamp_loop_start(unsigned long subsong, bool ms = false) const; unsigned long get_timestamp_loop_start(unsigned long subsong, bool ms = false) const;
unsigned long get_timestamp_loop_end(unsigned long subsong, bool ms = false) const; unsigned long get_timestamp_loop_end(unsigned long subsong, bool ms = false) const;
void get_meta_data( unsigned long subsong, midi_meta_data & p_out ); void get_meta_data( unsigned long subsong, midi_meta_data & p_out );
void scan_for_loops( bool p_xmi_loops, bool p_marker_loops, bool p_rpgmaker_loops ); void scan_for_loops( bool p_xmi_loops, bool p_marker_loops, bool p_rpgmaker_loops, bool p_touhou_loops );
static void encode_delta( std::vector<uint8_t> & p_out, unsigned long delta ); static void encode_delta( std::vector<uint8_t> & p_out, unsigned long delta );
}; };

View File

@ -51,14 +51,14 @@ class midi_processor
static bool process_lds( std::vector<uint8_t> const& p_file, midi_container & p_out ); static bool process_lds( std::vector<uint8_t> const& p_file, midi_container & p_out );
static bool process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out ); static bool process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out );
static bool process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out ); static bool process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out );
static bool process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count ); static bool process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
static bool process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count ); static bool process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
static bool process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count ); static bool process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
public: public:
static bool process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count ); static bool process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count );
static bool process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out ); static bool process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out );
static bool process_syx_file( std::vector<uint8_t> const& p_file, midi_container & p_out ); static bool process_syx_file( std::vector<uint8_t> const& p_file, midi_container & p_out );

View File

@ -4,47 +4,47 @@ bool midi_processor::is_gmf( std::vector<uint8_t> const& p_file )
{ {
if ( p_file.size() < 32 ) return false; if ( p_file.size() < 32 ) return false;
if ( p_file[ 0 ] != 'G' || p_file[ 1 ] != 'M' || p_file[ 2 ] != 'F' || p_file[ 3 ] != 1 ) return false; if ( p_file[ 0 ] != 'G' || p_file[ 1 ] != 'M' || p_file[ 2 ] != 'F' || p_file[ 3 ] != 1 ) return false;
return true; return true;
} }
bool midi_processor::process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out ) bool midi_processor::process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out )
{ {
uint8_t buffer[10]; uint8_t buffer[10];
p_out.initialize( 0, 0xC0 ); p_out.initialize( 0, 0xC0 );
uint16_t tempo = ( p_file[ 4 ] << 8 ) | p_file[ 5 ]; uint16_t tempo = ( p_file[ 4 ] << 8 ) | p_file[ 5 ];
uint32_t tempo_scaled = tempo * 100000; uint32_t tempo_scaled = tempo * 100000;
midi_track track; midi_track track;
buffer[0] = 0xFF; buffer[0] = 0xFF;
buffer[1] = 0x51; buffer[1] = 0x51;
buffer[2] = tempo_scaled >> 16; buffer[2] = tempo_scaled >> 16;
buffer[3] = tempo_scaled >> 8; buffer[3] = tempo_scaled >> 8;
buffer[4] = tempo_scaled; buffer[4] = tempo_scaled;
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 5 ) ); track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 5 ) );
buffer[0] = 0xF0; buffer[0] = 0xF0;
buffer[1] = 0x41; buffer[1] = 0x41;
buffer[2] = 0x10; buffer[2] = 0x10;
buffer[3] = 0x16; buffer[3] = 0x16;
buffer[4] = 0x12; buffer[4] = 0x12;
buffer[5] = 0x7F; buffer[5] = 0x7F;
buffer[6] = 0x00; buffer[6] = 0x00;
buffer[7] = 0x00; buffer[7] = 0x00;
buffer[8] = 0x01; buffer[8] = 0x01;
buffer[9] = 0xF7; buffer[9] = 0xF7;
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 10 ) ); track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 10 ) );
buffer[0] = 0xFF; buffer[0] = 0xFF;
buffer[1] = 0x2F; buffer[1] = 0x2F;
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 2 ) ); track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 2 ) );
p_out.add_track( track ); p_out.add_track( track );
std::vector<uint8_t>::const_iterator it = p_file.begin() + 7; std::vector<uint8_t>::const_iterator it = p_file.begin() + 7;

View File

@ -6,54 +6,54 @@ const uint8_t midi_processor::loop_end[9] = {0xFF, 0x06, 'l', 'o', 'o', 'p',
int midi_processor::decode_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end ) int midi_processor::decode_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
{ {
int delta = 0; int delta = 0;
unsigned char byte; unsigned char byte;
do do
{ {
if ( it == end ) return 0; if ( it == end ) return 0;
byte = *it++; byte = *it++;
delta = ( delta << 7 ) + ( byte & 0x7F ); delta = ( delta << 7 ) + ( byte & 0x7F );
} }
while ( byte & 0x80 ); while ( byte & 0x80 );
return delta; return delta;
} }
bool midi_processor::process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out ) bool midi_processor::process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out )
{ {
if ( is_standard_midi( p_file ) ) if ( is_standard_midi( p_file ) )
{ {
return process_standard_midi( p_file, p_out ); return process_standard_midi( p_file, p_out );
} }
else if ( is_riff_midi( p_file ) ) else if ( is_riff_midi( p_file ) )
{ {
return process_riff_midi( p_file, p_out ); return process_riff_midi( p_file, p_out );
} }
else if ( is_hmp( p_file ) ) else if ( is_hmp( p_file ) )
{ {
return process_hmp( p_file, p_out ); return process_hmp( p_file, p_out );
} }
else if ( is_hmi( p_file ) ) else if ( is_hmi( p_file ) )
{ {
return process_hmi( p_file, p_out ); return process_hmi( p_file, p_out );
} }
else if ( is_xmi( p_file ) ) else if ( is_xmi( p_file ) )
{ {
return process_xmi( p_file, p_out ); return process_xmi( p_file, p_out );
} }
else if ( is_mus( p_file ) ) else if ( is_mus( p_file ) )
{ {
return process_mus( p_file, p_out ); return process_mus( p_file, p_out );
} }
else if ( is_mids( p_file ) ) else if ( is_mids( p_file ) )
{ {
return process_mids( p_file, p_out ); return process_mids( p_file, p_out );
} }
else if ( is_lds( p_file, p_extension ) ) else if ( is_lds( p_file, p_extension ) )
{ {
return process_lds( p_file, p_out ); return process_lds( p_file, p_out );
} }
else if ( is_gmf( p_file ) ) else if ( is_gmf( p_file ) )
{ {
return process_gmf( p_file, p_out ); return process_gmf( p_file, p_out );
} }
else return false; else return false;
@ -71,7 +71,7 @@ bool midi_processor::process_syx_file( std::vector<uint8_t> const& p_file, midi_
bool midi_processor::process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count ) bool midi_processor::process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count )
{ {
track_count = 0; track_count = 0;
if ( is_standard_midi( p_file ) ) if ( is_standard_midi( p_file ) )
{ {
return process_standard_midi_count( p_file, track_count ); return process_standard_midi_count( p_file, track_count );

View File

@ -18,43 +18,43 @@ bool midi_processor::process_hmi( std::vector<uint8_t> const& p_file, midi_conta
uint32_t track_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); uint32_t track_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
uint32_t track_table_offset = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 ); uint32_t track_table_offset = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
if ( track_table_offset >= p_file.size() || track_table_offset + track_count * 4 > p_file.size() ) if ( track_table_offset >= p_file.size() || track_table_offset + track_count * 4 > p_file.size() )
return false; return false;
it = p_file.begin() + track_table_offset; it = p_file.begin() + track_table_offset;
std::vector<uint32_t> track_offsets; std::vector<uint32_t> track_offsets;
track_offsets.resize( track_count ); track_offsets.resize( track_count );
for ( unsigned i = 0; i < track_count; ++i ) for ( unsigned i = 0; i < track_count; ++i )
{ {
track_offsets[ i ] = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); track_offsets[ i ] = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
} }
p_out.initialize( 1, 0xC0 ); p_out.initialize( 1, 0xC0 );
{ {
midi_track track; midi_track track;
track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) );
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track ); p_out.add_track( track );
} }
for ( unsigned i = 0; i < track_count; ++i ) for ( unsigned i = 0; i < track_count; ++i )
{ {
unsigned track_offset = track_offsets[ i ]; unsigned track_offset = track_offsets[ i ];
unsigned long track_length; unsigned long track_length;
if ( i + 1 < track_count ) if ( i + 1 < track_count )
{ {
track_length = track_offsets[ i + 1 ] - track_offset; track_length = track_offsets[ i + 1 ] - track_offset;
} }
else else
{ {
track_length = p_file.size() - track_offset; track_length = p_file.size() - track_offset;
} }
if ( track_offset >= p_file.size() || track_offset + track_length > p_file.size() ) if ( track_offset >= p_file.size() || track_offset + track_length > p_file.size() )
return false; return false;
std::vector<uint8_t>::const_iterator track_body = p_file.begin() + track_offset; std::vector<uint8_t>::const_iterator track_body = p_file.begin() + track_offset;
std::vector<uint8_t>::const_iterator track_end = track_body + track_length; std::vector<uint8_t>::const_iterator track_end = track_body + track_length;
@ -65,31 +65,31 @@ bool midi_processor::process_hmi( std::vector<uint8_t> const& p_file, midi_conta
track_body[ 8 ] != 'T' || track_body[ 9 ] != 'R' || track_body[ 10 ] != 'A' || track_body[ 11 ] != 'C' || track_body[ 8 ] != 'T' || track_body[ 9 ] != 'R' || track_body[ 10 ] != 'A' || track_body[ 11 ] != 'C' ||
track_body[ 12 ] != 'K' ) return false; track_body[ 12 ] != 'K' ) return false;
midi_track track; midi_track track;
unsigned current_timestamp = 0; unsigned current_timestamp = 0;
unsigned char last_event_code = 0xFF; unsigned char last_event_code = 0xFF;
unsigned last_event_timestamp = 0; unsigned last_event_timestamp = 0;
if ( track_length < 0x4B + 4 ) return false; if ( track_length < 0x4B + 4 ) return false;
uint32_t meta_offset = track_body[ 0x4B ] | ( track_body[ 0x4C ] << 8 ) | ( track_body[ 0x4D ] << 16 ) | ( track_body[ 0x4E ] << 24 ); uint32_t meta_offset = track_body[ 0x4B ] | ( track_body[ 0x4C ] << 8 ) | ( track_body[ 0x4D ] << 16 ) | ( track_body[ 0x4E ] << 24 );
if ( meta_offset && meta_offset + 1 < track_length ) if ( meta_offset && meta_offset + 1 < track_length )
{ {
buffer.resize( 2 ); buffer.resize( 2 );
std::copy( track_body + meta_offset, track_body + meta_offset + 2, buffer.begin() ); std::copy( track_body + meta_offset, track_body + meta_offset + 2, buffer.begin() );
unsigned meta_size = buffer[ 1 ]; unsigned meta_size = buffer[ 1 ];
if ( meta_offset + 2 + meta_size > track_length ) return false; if ( meta_offset + 2 + meta_size > track_length ) return false;
buffer.resize( meta_size + 2 ); buffer.resize( meta_size + 2 );
std::copy( track_body + meta_offset + 2, track_body + meta_offset + 2 + meta_size, buffer.begin() + 2 ); std::copy( track_body + meta_offset + 2, track_body + meta_offset + 2 + meta_size, buffer.begin() + 2 );
while ( meta_size > 0 && buffer[ meta_size + 1 ] == ' ' ) --meta_size; while ( meta_size > 0 && buffer[ meta_size + 1 ] == ' ' ) --meta_size;
if ( meta_size > 0 ) if ( meta_size > 0 )
{ {
buffer[ 0 ] = 0xFF; buffer[ 0 ] = 0xFF;
buffer[ 1 ] = 0x01; buffer[ 1 ] = 0x01;
track.add_event( midi_event( 0, midi_event::extended, 0, &buffer[0], meta_size + 2 ) ); track.add_event( midi_event( 0, midi_event::extended, 0, &buffer[0], meta_size + 2 ) );
} }
} }
if ( track_length < 0x57 + 4 ) return false; if ( track_length < 0x57 + 4 ) return false;
@ -100,129 +100,129 @@ bool midi_processor::process_hmi( std::vector<uint8_t> const& p_file, midi_conta
buffer.resize( 3 ); buffer.resize( 3 );
while ( it != track_end ) while ( it != track_end )
{ {
int delta = decode_delta( it, track_end ); int delta = decode_delta( it, track_end );
if ( delta > 0xFFFF || delta < 0 ) if ( delta > 0xFFFF || delta < 0 )
{ {
current_timestamp = last_event_timestamp; current_timestamp = last_event_timestamp;
/*console::formatter() << "[foo_midi] Large HMI delta detected, shunting.";*/ /*console::formatter() << "[foo_midi] Large HMI delta detected, shunting.";*/
} }
else else
{ {
current_timestamp += delta; current_timestamp += delta;
if ( current_timestamp > last_event_timestamp ) if ( current_timestamp > last_event_timestamp )
{ {
last_event_timestamp = current_timestamp; last_event_timestamp = current_timestamp;
} }
} }
if ( it == track_end ) return false; if ( it == track_end ) return false;
buffer[ 0 ] = *it++; buffer[ 0 ] = *it++;
if ( buffer[ 0 ] == 0xFF ) if ( buffer[ 0 ] == 0xFF )
{ {
last_event_code = 0xFF; last_event_code = 0xFF;
if ( it == track_end ) return false; if ( it == track_end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
int meta_count = decode_delta( it, track_end ); int meta_count = decode_delta( it, track_end );
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI meta message" );*/ if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI meta message" );*/
if ( track_end - it < meta_count ) return false; if ( track_end - it < meta_count ) return false;
buffer.resize( meta_count + 2 ); buffer.resize( meta_count + 2 );
std::copy( it, it + meta_count, buffer.begin() + 2 ); std::copy( it, it + meta_count, buffer.begin() + 2 );
it += meta_count; it += meta_count;
if ( buffer[ 1 ] == 0x2F && last_event_timestamp > current_timestamp ) if ( buffer[ 1 ] == 0x2F && last_event_timestamp > current_timestamp )
{ {
current_timestamp = last_event_timestamp; current_timestamp = last_event_timestamp;
} }
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
if ( buffer[ 1 ] == 0x2F ) break; if ( buffer[ 1 ] == 0x2F ) break;
} }
else if ( buffer[ 0 ] == 0xF0 ) else if ( buffer[ 0 ] == 0xF0 )
{ {
last_event_code = 0xFF; last_event_code = 0xFF;
int system_exclusive_count = decode_delta( it, track_end ); int system_exclusive_count = decode_delta( it, track_end );
if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI System Exclusive message" );*/ if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI System Exclusive message" );*/
if ( track_end - it < system_exclusive_count ) return false; if ( track_end - it < system_exclusive_count ) return false;
buffer.resize( system_exclusive_count + 1 ); buffer.resize( system_exclusive_count + 1 );
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 ); std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
it += system_exclusive_count; it += system_exclusive_count;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
} }
else if ( buffer[ 0 ] == 0xFE ) else if ( buffer[ 0 ] == 0xFE )
{ {
last_event_code = 0xFF; last_event_code = 0xFF;
if ( it == track_end ) return false; if ( it == track_end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
if ( buffer[ 1 ] == 0x10 ) if ( buffer[ 1 ] == 0x10 )
{ {
if ( track_end - it < 3 ) return false; if ( track_end - it < 3 ) return false;
it += 2; it += 2;
buffer[ 2 ] = *it++; buffer[ 2 ] = *it++;
if ( track_end - it < buffer[ 2 ] + 4 ) return false; if ( track_end - it < buffer[ 2 ] + 4 ) return false;
it += buffer[ 2 ] + 4; it += buffer[ 2 ] + 4;
} }
else if ( buffer[ 1 ] == 0x12 ) else if ( buffer[ 1 ] == 0x12 )
{ {
if ( track_end - it < 2 ) return false; if ( track_end - it < 2 ) return false;
it += 2; it += 2;
} }
else if ( buffer[ 1 ] == 0x13 ) else if ( buffer[ 1 ] == 0x13 )
{ {
if ( track_end - it < 10 ) return false; if ( track_end - it < 10 ) return false;
it += 10; it += 10;
} }
else if ( buffer[ 1 ] == 0x14 ) else if ( buffer[ 1 ] == 0x14 )
{ {
if ( track_end - it < 2 ) return false; if ( track_end - it < 2 ) return false;
it += 2; it += 2;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_start, _countof( loop_start ) ) ); p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_start, _countof( loop_start ) ) );
} }
else if ( buffer[ 1 ] == 0x15 ) else if ( buffer[ 1 ] == 0x15 )
{ {
if ( track_end - it < 6 ) return false; if ( track_end - it < 6 ) return false;
it += 6; it += 6;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_end, _countof( loop_end ) ) ); p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_end, _countof( loop_end ) ) );
} }
else return false; /*throw exception_io_data( "Unexpected HMI meta event" );*/ else return false; /*throw exception_io_data( "Unexpected HMI meta event" );*/
} }
else if ( buffer[ 0 ] <= 0xEF ) else if ( buffer[ 0 ] <= 0xEF )
{ {
unsigned bytes_read = 1; unsigned bytes_read = 1;
if ( buffer[ 0 ] >= 0x80 ) if ( buffer[ 0 ] >= 0x80 )
{ {
if ( it == track_end ) return false; if ( it == track_end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
last_event_code = buffer[ 0 ]; last_event_code = buffer[ 0 ];
} }
else else
{ {
if ( last_event_code == 0xFF ) return false; /*throw exception_io_data( "HMI used shortened event after Meta or SysEx message" );*/ if ( last_event_code == 0xFF ) return false; /*throw exception_io_data( "HMI used shortened event after Meta or SysEx message" );*/
buffer[ 1 ] = buffer[ 0 ]; buffer[ 1 ] = buffer[ 0 ];
buffer[ 0 ] = last_event_code; buffer[ 0 ] = last_event_code;
} }
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 ); midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
unsigned channel = buffer[ 0 ] & 0x0F; unsigned channel = buffer[ 0 ] & 0x0F;
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch ) if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
{ {
if ( it == track_end ) return false; if ( it == track_end ) return false;
buffer[ 2 ] = *it++; buffer[ 2 ] = *it++;
bytes_read = 2; bytes_read = 2;
} }
track.add_event( midi_event( current_timestamp, type, channel, &buffer[ 1 ], bytes_read ) ); track.add_event( midi_event( current_timestamp, type, channel, &buffer[ 1 ], bytes_read ) );
if ( type == midi_event::note_on ) if ( type == midi_event::note_on )
{ {
buffer[ 2 ] = 0x00; buffer[ 2 ] = 0x00;
int note_length = decode_delta( it, track_end ); int note_length = decode_delta( it, track_end );
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid HMI note message" );*/ if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid HMI note message" );*/
unsigned note_end_timestamp = current_timestamp + note_length; unsigned note_end_timestamp = current_timestamp + note_length;
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp; if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
track.add_event( midi_event( note_end_timestamp, midi_event::note_on, channel, &buffer[1], bytes_read ) ); track.add_event( midi_event( note_end_timestamp, midi_event::note_on, channel, &buffer[1], bytes_read ) );
} }
} }
else return false; /*throw exception_io_data( "Unexpected HMI status code" );*/ else return false; /*throw exception_io_data( "Unexpected HMI status code" );*/
} }
p_out.add_track( track ); p_out.add_track( track );
} }
return true; return true;
} }

View File

@ -13,18 +13,18 @@ bool midi_processor::is_hmp( std::vector<uint8_t> const& p_file )
unsigned midi_processor::decode_hmp_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end ) unsigned midi_processor::decode_hmp_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
{ {
unsigned delta = 0; unsigned delta = 0;
unsigned shift = 0; unsigned shift = 0;
unsigned char byte; unsigned char byte;
do do
{ {
if ( it == end ) return 0; if ( it == end ) return 0;
byte = *it++; byte = *it++;
delta = delta + ( ( byte & 0x7F ) << shift ); delta = delta + ( ( byte & 0x7F ) << shift );
shift += 7; shift += 7;
} }
while ( !( byte & 0x80 ) ); while ( !( byte & 0x80 ) );
return delta; return delta;
} }
bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_container & p_out ) bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_container & p_out )
@ -34,87 +34,89 @@ bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_conta
uint8_t track_count_8; uint8_t track_count_8;
uint16_t dtx = 0xC0; uint16_t dtx = 0xC0;
uint32_t offset = is_funky ? 0x1A : 0x30; uint32_t offset = is_funky ? 0x1A : 0x30;
if ( offset >= p_file.size() ) if ( offset >= p_file.size() )
return false; return false;
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset; std::vector<uint8_t>::const_iterator it = p_file.begin() + offset;
std::vector<uint8_t>::const_iterator end = p_file.end(); std::vector<uint8_t>::const_iterator end = p_file.end();
track_count_8 = *it; track_count_8 = *it;
if ( is_funky ) if ( is_funky )
{ {
if ( p_file.size() <= 0x4D ) if ( p_file.size() <= 0x4D )
return false; return false;
dtx = ( p_file[ 0x4C ] << 16 ) | p_file[ 0x4D ]; dtx = ( p_file[ 0x4C ] << 16 ) | p_file[ 0x4D ];
} if ( !dtx ) // dtx == 0, will cause division by zero on tempo calculations
return false;
}
p_out.initialize( 1, dtx ); p_out.initialize( 1, dtx );
{ {
midi_track track; midi_track track;
track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) );
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track ); p_out.add_track( track );
} }
uint8_t buffer[ 4 ]; uint8_t buffer[ 4 ];
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 0 ] = *it++; buffer[ 0 ] = *it++;
while ( it != end ) while ( it != end )
{ {
if ( buffer[ 0 ] != 0xFF ) if ( buffer[ 0 ] != 0xFF )
{ {
buffer[ 0 ] = *it++; buffer[ 0 ] = *it++;
continue; continue;
} }
if ( it == end ) break; if ( it == end ) break;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
if ( buffer[ 1 ] != 0x2F ) if ( buffer[ 1 ] != 0x2F )
{ {
buffer[ 0 ] = buffer[ 1 ]; buffer[ 0 ] = buffer[ 1 ];
continue; continue;
} }
break; break;
} }
offset = is_funky ? 3 : 5; offset = is_funky ? 3 : 5;
if ( (unsigned long)(end - it) < offset ) return false; if ( (unsigned long)(end - it) < offset ) return false;
it += offset; it += offset;
unsigned track_count = track_count_8; unsigned track_count = track_count_8;
for ( unsigned i = 1; i < track_count; ++i ) for ( unsigned i = 1; i < track_count; ++i )
{ {
uint16_t track_size_16; uint16_t track_size_16;
uint32_t track_size_32; uint32_t track_size_32;
if ( is_funky ) if ( is_funky )
{ {
if ( end - it < 4 ) break; if ( end - it < 4 ) break;
track_size_16 = it[ 0 ] | ( it[ 1 ] << 8 ); track_size_16 = it[ 0 ] | ( it[ 1 ] << 8 );
it += 2; it += 2;
track_size_32 = track_size_16 - 4; track_size_32 = track_size_16 - 4;
if ( (unsigned long)(end - it) < track_size_32 + 2 ) break; if ( (unsigned long)(end - it) < track_size_32 + 2 ) break;
it += 2; it += 2;
} }
else else
{ {
if ( end - it < 8 ) break; if ( end - it < 8 ) break;
track_size_32 = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); track_size_32 = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
track_size_32 -= 12; track_size_32 -= 12;
if ( (unsigned long)(end - it) < track_size_32 + 8 ) break; if ( (unsigned long)(end - it) < track_size_32 + 8 ) break;
it += 4; it += 4;
} }
midi_track track; midi_track track;
unsigned current_timestamp = 0; unsigned current_timestamp = 0;
std::vector<uint8_t> _buffer; std::vector<uint8_t> _buffer;
_buffer.resize( 3 ); _buffer.resize( 3 );
@ -122,47 +124,47 @@ bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_conta
std::vector<uint8_t>::const_iterator track_end = it + track_size_32; std::vector<uint8_t>::const_iterator track_end = it + track_size_32;
while ( it != track_end ) while ( it != track_end )
{ {
unsigned delta = decode_hmp_delta( it, track_end ); unsigned delta = decode_hmp_delta( it, track_end );
current_timestamp += delta; current_timestamp += delta;
if ( it == track_end ) return false; if ( it == track_end ) return false;
_buffer[ 0 ] = *it++; _buffer[ 0 ] = *it++;
if ( _buffer[ 0 ] == 0xFF ) if ( _buffer[ 0 ] == 0xFF )
{ {
if ( it == track_end ) return false; if ( it == track_end ) return false;
_buffer[ 1 ] = *it++; _buffer[ 1 ] = *it++;
int meta_count = decode_delta( it, track_end ); int meta_count = decode_delta( it, track_end );
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMP meta message" );*/ if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMP meta message" );*/
if ( track_end - it < meta_count ) return false; if ( track_end - it < meta_count ) return false;
_buffer.resize( meta_count + 2 ); _buffer.resize( meta_count + 2 );
std::copy( it, it + meta_count, _buffer.begin() + 2 ); std::copy( it, it + meta_count, _buffer.begin() + 2 );
it += meta_count; it += meta_count;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &_buffer[0], meta_count + 2 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &_buffer[0], meta_count + 2 ) );
if ( _buffer[ 1 ] == 0x2F ) break; if ( _buffer[ 1 ] == 0x2F ) break;
} }
else if ( _buffer[ 0 ] >= 0x80 && _buffer[ 0 ] <= 0xEF ) else if ( _buffer[ 0 ] >= 0x80 && _buffer[ 0 ] <= 0xEF )
{ {
unsigned bytes_read = 2; unsigned bytes_read = 2;
switch ( _buffer[ 0 ] & 0xF0 ) switch ( _buffer[ 0 ] & 0xF0 )
{ {
case 0xC0: case 0xC0:
case 0xD0: case 0xD0:
bytes_read = 1; bytes_read = 1;
} }
if ( (unsigned long)(track_end - it) < bytes_read ) return false; if ( (unsigned long)(track_end - it) < bytes_read ) return false;
std::copy( it, it + bytes_read, _buffer.begin() + 1 ); std::copy( it, it + bytes_read, _buffer.begin() + 1 );
it += bytes_read; it += bytes_read;
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( ( _buffer[ 0 ] >> 4 ) - 8 ), _buffer[ 0 ] & 0x0F, &_buffer[1], bytes_read ) ); track.add_event( midi_event( current_timestamp, (midi_event::event_type)( ( _buffer[ 0 ] >> 4 ) - 8 ), _buffer[ 0 ] & 0x0F, &_buffer[1], bytes_read ) );
} }
else return false; /*throw exception_io_data( "Unexpected status code in HMP track" );*/ else return false; /*throw exception_io_data( "Unexpected status code in HMP track" );*/
} }
offset = is_funky ? 0 : 4; offset = is_funky ? 0 : 4;
if ( end - it < (signed long)offset ) return false; if ( end - it < (signed long)offset ) return false;
it = track_end + offset; it = track_end + offset;
p_out.add_track( track ); p_out.add_track( track );
} }
return true; return true;
} }

View File

@ -13,122 +13,124 @@ bool midi_processor::is_mids( std::vector<uint8_t> const& p_file )
bool midi_processor::process_mids( std::vector<uint8_t> const& p_file, midi_container & p_out ) bool midi_processor::process_mids( std::vector<uint8_t> const& p_file, midi_container & p_out )
{ {
if ( p_file.size() < 20 ) return false; if ( p_file.size() < 20 ) return false;
std::vector<uint8_t>::const_iterator it = p_file.begin() + 16; std::vector<uint8_t>::const_iterator it = p_file.begin() + 16;
std::vector<uint8_t>::const_iterator end = p_file.end(); std::vector<uint8_t>::const_iterator end = p_file.end();
uint32_t fmt_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); uint32_t fmt_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
if ( (unsigned long)(end - it) < fmt_size ) return false; if ( (unsigned long)(end - it) < fmt_size ) return false;
uint32_t time_format = 1; uint32_t time_format = 1;
/*uint32_t max_buffer = 0;*/ /*uint32_t max_buffer = 0;*/
uint32_t flags = 0; uint32_t flags = 0;
if ( fmt_size >= 4 ) if ( fmt_size >= 4 )
{ {
time_format = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); time_format = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
fmt_size -= 4; fmt_size -= 4;
} if ( !time_format ) // dtx == 0, will cause division by zero on tempo calculations
if ( fmt_size >= 4 ) return false;
{ }
if ( fmt_size >= 4 )
{
/*max_buffer = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );*/ /*max_buffer = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );*/
it += 4; it += 4;
fmt_size -= 4; fmt_size -= 4;
} }
if ( fmt_size >= 4 ) if ( fmt_size >= 4 )
{ {
flags = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); flags = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
fmt_size -= 4; fmt_size -= 4;
} }
it += fmt_size; it += fmt_size;
if ( it == end ) return false; if ( it == end ) return false;
if ( fmt_size & 1 ) ++it; if ( fmt_size & 1 ) ++it;
p_out.initialize( 0, time_format ); p_out.initialize( 0, time_format );
if ( end - it < 4 ) return false; if ( end - it < 4 ) return false;
if ( it[ 0 ] != 'd' || it[ 1 ] != 'a' || it[ 2 ] != 't' || it[ 3 ] != 'a' ) return false; /*throw exception_io_data( "MIDS missing RIFF data chunk" );*/ if ( it[ 0 ] != 'd' || it[ 1 ] != 'a' || it[ 2 ] != 't' || it[ 3 ] != 'a' ) return false; /*throw exception_io_data( "MIDS missing RIFF data chunk" );*/
it += 4; it += 4;
{ {
midi_track track; midi_track track;
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track ); p_out.add_track( track );
} }
if ( end - it < 4 ) return false; if ( end - it < 4 ) return false;
uint32_t data_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); uint32_t data_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
std::vector<uint8_t>::const_iterator body_end = it + data_size; std::vector<uint8_t>::const_iterator body_end = it + data_size;
if ( body_end - it < 4 ) return false; if ( body_end - it < 4 ) return false;
uint32_t segment_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); uint32_t segment_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
bool is_eight_byte = !!(flags & 1); bool is_eight_byte = !!(flags & 1);
midi_track track; midi_track track;
unsigned current_timestamp = 0; unsigned current_timestamp = 0;
for ( unsigned i = 0; i < segment_count; ++i ) for ( unsigned i = 0; i < segment_count; ++i )
{ {
if ( end - it < 12 ) return false; if ( end - it < 12 ) return false;
it += 4; it += 4;
uint32_t segment_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); uint32_t segment_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
std::vector<uint8_t>::const_iterator segment_end = it + segment_size; std::vector<uint8_t>::const_iterator segment_end = it + segment_size;
while ( it != segment_end && it != body_end ) while ( it != segment_end && it != body_end )
{ {
if ( segment_end - it < 4 ) return false; if ( segment_end - it < 4 ) return false;
uint32_t delta = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); uint32_t delta = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
uint32_t event; uint32_t event;
current_timestamp += delta; current_timestamp += delta;
if ( !is_eight_byte ) if ( !is_eight_byte )
{ {
if ( segment_end - it < 4 ) return false; if ( segment_end - it < 4 ) return false;
it += 4; it += 4;
} }
if ( segment_end - it < 4 ) return false; if ( segment_end - it < 4 ) return false;
event = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 ); event = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4; it += 4;
if ( event >> 24 == 0x01 ) if ( event >> 24 == 0x01 )
{ {
uint8_t buffer[ 5 ] = { 0xFF, 0x51 }; uint8_t buffer[ 5 ] = { 0xFF, 0x51 };
buffer[ 2 ] = (uint8_t)( event >> 16 ); buffer[ 2 ] = (uint8_t)( event >> 16 );
buffer[ 3 ] = (uint8_t)( event >> 8 ); buffer[ 3 ] = (uint8_t)( event >> 8 );
buffer[ 4 ] = (uint8_t)event; buffer[ 4 ] = (uint8_t)event;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, buffer, sizeof( buffer ) ) ); p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, buffer, sizeof( buffer ) ) );
} }
else if ( !( event >> 24 ) ) else if ( !( event >> 24 ) )
{ {
unsigned event_code = ( event & 0xF0 ) >> 4; unsigned event_code = ( event & 0xF0 ) >> 4;
if ( event_code >= 0x8 && event_code <= 0xE ) if ( event_code >= 0x8 && event_code <= 0xE )
{ {
unsigned bytes_to_write = 1; unsigned bytes_to_write = 1;
uint8_t buffer[2]; uint8_t buffer[2];
buffer[ 0 ] = (uint8_t)( event >> 8 ); buffer[ 0 ] = (uint8_t)( event >> 8 );
if ( event_code != 0xC && event_code != 0xD ) if ( event_code != 0xC && event_code != 0xD )
{ {
buffer[ 1 ] = (uint8_t)( event >> 16 ); buffer[ 1 ] = (uint8_t)( event >> 16 );
bytes_to_write = 2; bytes_to_write = 2;
} }
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( event_code - 8 ), event & 0x0F, buffer, bytes_to_write ) ); track.add_event( midi_event( current_timestamp, (midi_event::event_type)( event_code - 8 ), event & 0x0F, buffer, bytes_to_write ) );
} }
} }
} }
} }
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track ); p_out.add_track( track );
return true; return true;
} }

View File

@ -20,132 +20,132 @@ bool midi_processor::process_mus( std::vector<uint8_t> const& p_file, midi_conta
uint16_t length = p_file[ 4 ] | ( p_file[ 5 ] << 8 ); uint16_t length = p_file[ 4 ] | ( p_file[ 5 ] << 8 );
uint16_t offset = p_file[ 6 ] | ( p_file[ 7 ] << 8 ); uint16_t offset = p_file[ 6 ] | ( p_file[ 7 ] << 8 );
p_out.initialize( 0, 0x59 ); p_out.initialize( 0, 0x59 );
{ {
midi_track track; midi_track track;
track.add_event( midi_event( 0, midi_event::extended, 0, mus_default_tempo, _countof( mus_default_tempo ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, mus_default_tempo, _countof( mus_default_tempo ) ) );
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track ); p_out.add_track( track );
} }
midi_track track; midi_track track;
unsigned current_timestamp = 0; unsigned current_timestamp = 0;
uint8_t velocity_levels[ 16 ] = { 0 }; uint8_t velocity_levels[ 16 ] = { 0 };
if ( (size_t)offset >= p_file.size() || (size_t)(offset + length) > p_file.size() ) if ( (size_t)offset >= p_file.size() || (size_t)(offset + length) > p_file.size() )
return false; return false;
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset, end = p_file.begin() + offset + length; std::vector<uint8_t>::const_iterator it = p_file.begin() + offset, end = p_file.begin() + offset + length;
uint8_t buffer[ 4 ]; uint8_t buffer[ 4 ];
while ( it != end ) while ( it != end )
{ {
buffer[ 0 ] = *it++; buffer[ 0 ] = *it++;
if ( buffer[ 0 ] == 0x60 ) break; if ( buffer[ 0 ] == 0x60 ) break;
midi_event::event_type type; midi_event::event_type type;
unsigned bytes_to_write; unsigned bytes_to_write;
unsigned channel = buffer[ 0 ] & 0x0F; unsigned channel = buffer[ 0 ] & 0x0F;
if ( channel == 0x0F ) channel = 9; if ( channel == 0x0F ) channel = 9;
else if ( channel >= 9 ) ++channel; else if ( channel >= 9 ) ++channel;
switch ( buffer[ 0 ] & 0x70 ) switch ( buffer[ 0 ] & 0x70 )
{ {
case 0x00: case 0x00:
type = midi_event::note_on; type = midi_event::note_on;
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
buffer[ 2 ] = 0; buffer[ 2 ] = 0;
bytes_to_write = 2; bytes_to_write = 2;
break; break;
case 0x10: case 0x10:
type = midi_event::note_on; type = midi_event::note_on;
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
if ( buffer[ 1 ] & 0x80 ) if ( buffer[ 1 ] & 0x80 )
{ {
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 2 ] = *it++; buffer[ 2 ] = *it++;
velocity_levels[ channel ] = buffer[ 2 ]; velocity_levels[ channel ] = buffer[ 2 ];
buffer[ 1 ] &= 0x7F; buffer[ 1 ] &= 0x7F;
} }
else else
{ {
buffer[ 2 ] = velocity_levels[ channel ]; buffer[ 2 ] = velocity_levels[ channel ];
} }
bytes_to_write = 2; bytes_to_write = 2;
break; break;
case 0x20: case 0x20:
type = midi_event::pitch_wheel; type = midi_event::pitch_wheel;
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
buffer[ 2 ] = buffer[ 1 ] >> 1; buffer[ 2 ] = buffer[ 1 ] >> 1;
buffer[ 1 ] <<= 7; buffer[ 1 ] <<= 7;
bytes_to_write = 2; bytes_to_write = 2;
break; break;
case 0x30: case 0x30:
type = midi_event::control_change; type = midi_event::control_change;
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
if ( buffer[ 1 ] >= 10 && buffer[ 1 ] <= 14 ) if ( buffer[ 1 ] >= 10 && buffer[ 1 ] <= 14 )
{ {
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ]; buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
buffer[ 2 ] = 1; buffer[ 2 ] = 1;
bytes_to_write = 2; bytes_to_write = 2;
} }
else return false; /*throw exception_io_data( "Unhandled MUS system event" );*/ else return false; /*throw exception_io_data( "Unhandled MUS system event" );*/
break; break;
case 0x40: case 0x40:
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
if ( buffer[ 1 ] ) if ( buffer[ 1 ] )
{ {
if ( buffer[ 1 ] < 10 ) if ( buffer[ 1 ] < 10 )
{ {
type = midi_event::control_change; type = midi_event::control_change;
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ]; buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 2 ] = *it++; buffer[ 2 ] = *it++;
bytes_to_write = 2; bytes_to_write = 2;
} }
else return false; /*throw exception_io_data( "Invalid MUS controller change event" );*/ else return false; /*throw exception_io_data( "Invalid MUS controller change event" );*/
} }
else else
{ {
type = midi_event::program_change; type = midi_event::program_change;
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
bytes_to_write = 1; bytes_to_write = 1;
} }
break; break;
default: default:
return false; /*throw exception_io_data( "Invalid MUS status code" );*/ return false; /*throw exception_io_data( "Invalid MUS status code" );*/
} }
track.add_event( midi_event( current_timestamp, type, channel, buffer + 1, bytes_to_write ) ); track.add_event( midi_event( current_timestamp, type, channel, buffer + 1, bytes_to_write ) );
if ( buffer[ 0 ] & 0x80 ) if ( buffer[ 0 ] & 0x80 )
{ {
int delta = decode_delta( it, end ); int delta = decode_delta( it, end );
if ( delta < 0 ) return false; /*throw exception_io_data( "Invalid MUS delta" );*/ if ( delta < 0 ) return false; /*throw exception_io_data( "Invalid MUS delta" );*/
current_timestamp += delta; current_timestamp += delta;
} }
} }
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track ); p_out.add_track( track );
return true; return true;
} }

View File

@ -2,15 +2,42 @@
#include <string.h> #include <string.h>
static inline bool it_equal( std::vector<uint8_t>::const_iterator it1, const char *it2, size_t length )
{
for ( size_t i = 0; i < length; i++ )
{
if ( it1[ i ] != it2[ i ] )
return false;
}
return true;
}
static inline uint32_t toInt32LE( const uint8_t *in )
{
return static_cast<uint32_t>( in[ 0 ] ) |
static_cast<uint32_t>( in[ 1 ] << 8 ) |
static_cast<uint32_t>( in[ 2 ] << 16 ) |
static_cast<uint32_t>( in[ 3 ] << 24 );
}
static inline uint32_t toInt32LE( std::vector<uint8_t>::const_iterator in )
{
return static_cast<uint32_t>( in[ 0 ] ) |
static_cast<uint32_t>( in[ 1 ] << 8 ) |
static_cast<uint32_t>( in[ 2 ] << 16 ) |
static_cast<uint32_t>( in[ 3 ] << 24 );
}
bool midi_processor::is_riff_midi( std::vector<uint8_t> const& p_file ) bool midi_processor::is_riff_midi( std::vector<uint8_t> const& p_file )
{ {
if ( p_file.size() < 20 ) return false; if ( p_file.size() < 20 ) return false;
if ( p_file[ 0 ] != 'R' || p_file[ 1 ] != 'I' || p_file[ 2 ] != 'F' || p_file[ 3 ] != 'F' ) return false; if ( memcmp( &p_file[ 0 ], "RIFF", 4 ) != 0 ) return false;
uint32_t riff_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 ); uint32_t riff_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 );
if ( riff_size < 12 || ( p_file.size() < riff_size + 8 ) ) return false; if ( riff_size < 12 || ( p_file.size() < riff_size + 8 ) ) return false;
if ( p_file[ 8 ] != 'R' || p_file[ 9 ] != 'M' || p_file[ 10 ] != 'I' || p_file[ 11 ] != 'D' || if ( memcmp( &p_file[ 8 ], "RMID", 4 ) != 0 ||
p_file[ 12 ] != 'd' || p_file[ 13 ] != 'a' || p_file[ 14 ] != 't' || p_file[ 15 ] != 'a' ) return false; memcmp( &p_file[ 12 ], "data", 4 ) != 0 ) return false;
uint32_t data_size = p_file[ 16 ] | ( p_file[ 17 ] << 8 ) | ( p_file[ 18 ] << 16 ) | ( p_file[ 19 ] << 24 ); uint32_t data_size = toInt32LE(&p_file[ 16 ]);
if ( data_size < 18 || p_file.size() < data_size + 20 || riff_size < data_size + 12 ) return false; if ( data_size < 18 || p_file.size() < data_size + 20 || riff_size < data_size + 12 ) return false;
std::vector<uint8_t> test; std::vector<uint8_t> test;
test.assign( p_file.begin() + 20, p_file.begin() + 20 + 18 ); test.assign( p_file.begin() + 20, p_file.begin() + 20 + 18 );
@ -20,21 +47,21 @@ bool midi_processor::is_riff_midi( std::vector<uint8_t> const& p_file )
bool midi_processor::process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count ) bool midi_processor::process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
{ {
track_count = 0; track_count = 0;
uint32_t file_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 ); uint32_t file_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 );
std::vector<uint8_t>::const_iterator it = p_file.begin() + 12; std::vector<uint8_t>::const_iterator it = p_file.begin() + 12;
std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size; std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size;
std::vector<uint8_t> extra_buffer; std::vector<uint8_t> extra_buffer;
while ( it != body_end ) while ( it != body_end )
{ {
if ( body_end - it < 8 ) return false; if ( body_end - it < 8 ) return false;
uint32_t chunk_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 ); uint32_t chunk_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
if ( (unsigned long)(body_end - it) < chunk_size ) return false; if ( (unsigned long)(body_end - it) < chunk_size ) return false;
if ( it[ 0 ] == 'd' && it[ 1 ] == 'a' && it[ 2 ] == 't' && it[ 3 ] == 'a' ) if ( it_equal(it, "data", 4) )
{ {
std::vector<uint8_t> midi_file; std::vector<uint8_t> midi_file;
midi_file.assign( it + 8, it + 8 + chunk_size ); midi_file.assign( it + 8, it + 8 + chunk_size );
@ -46,32 +73,32 @@ bool midi_processor::process_riff_midi_count( std::vector<uint8_t> const& p_file
if ( chunk_size & 1 && it != body_end ) ++it; if ( chunk_size & 1 && it != body_end ) ++it;
} }
} }
return false; return false;
} }
static const char * riff_tag_mappings[][2] = static const char * riff_tag_mappings[][2] =
{ {
{ "IALB", "album" }, { "IALB", "album" },
{ "IARL", "archival_location" }, { "IARL", "archival_location" },
{ "IART", "artist" }, { "IART", "artist" },
{ "ITRK", "tracknumber" }, { "ITRK", "tracknumber" },
{ "ICMS", "commissioned" }, { "ICMS", "commissioned" },
{ "ICMP", "composer" }, { "ICMP", "composer" },
{ "ICMT", "comment" }, { "ICMT", "comment" },
{ "ICOP", "copyright" }, { "ICOP", "copyright" },
{ "ICRD", "creation_date" }, { "ICRD", "creation_date" },
{ "IENG", "engineer" }, { "IENG", "engineer" },
{ "IGNR", "genre" }, { "IGNR", "genre" },
{ "IKEY", "keywords" }, { "IKEY", "keywords" },
{ "IMED", "medium" }, { "IMED", "medium" },
{ "INAM", "title" }, { "INAM", "title" },
{ "IPRD", "product" }, { "IPRD", "product" },
{ "ISBJ", "subject" }, { "ISBJ", "subject" },
{ "ISFT", "software" }, { "ISFT", "software" },
{ "ISRC", "source" }, { "ISRC", "source" },
{ "ISRF", "source_form" }, { "ISRF", "source_form" },
{ "ITCH", "technician" } { "ITCH", "technician" }
}; };
bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi_container & p_out ) bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi_container & p_out )
@ -82,77 +109,79 @@ bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi
std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size; std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size;
bool found_data = false; bool found_data = false;
bool found_info = false; bool found_info = false;
midi_meta_data meta_data; midi_meta_data meta_data;
std::vector<uint8_t> extra_buffer; std::vector<uint8_t> extra_buffer;
while ( it != body_end ) while ( it != body_end )
{ {
if ( body_end - it < 8 ) return false; if ( body_end - it < 8 ) return false;
uint32_t chunk_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 ); uint32_t chunk_size = toInt32LE( it + 4 );
if ( (unsigned long)(body_end - it) < chunk_size ) return false; if ( (unsigned long)(body_end - it) < chunk_size ) return false;
if ( it[ 0 ] == 'd' && it[ 1 ] == 'a' && it[ 2 ] == 't' && it[ 3 ] == 'a' ) if ( it_equal( it, "data", 4 ) )
{ {
if ( !found_data ) if ( !found_data )
{ {
std::vector<uint8_t> midi_file; std::vector<uint8_t> midi_file;
midi_file.assign( it + 8, it + 8 + chunk_size ); midi_file.assign( it + 8, it + 8 + chunk_size );
if ( !process_standard_midi( midi_file, p_out ) ) return false; if ( !process_standard_midi( midi_file, p_out ) ) return false;
found_data = true; found_data = true;
} }
else return false; /*throw exception_io_data( "Multiple RIFF data chunks found" );*/ else return false; /*throw exception_io_data( "Multiple RIFF data chunks found" );*/
it += 8 + chunk_size; it += 8 + chunk_size;
if ( chunk_size & 1 && it != body_end ) ++it; if ( chunk_size & 1 && it != body_end ) ++it;
} }
else if ( it[ 0 ] == 'D' && it[ 1 ] == 'I' && it[ 2 ] == 'S' && it[ 3 ] == 'P' ) else if ( it_equal( it, "DISP", 4 ) )
{ {
uint32_t type = it[ 8 ] | ( it[ 9 ] << 8 ) | ( it[ 10 ] << 16 ) | ( it[ 11 ] << 24 ); uint32_t type = toInt32LE( it + 8 );
if ( type == 1 ) if ( type == 1 )
{ {
extra_buffer.resize( chunk_size - 4 ); extra_buffer.resize( chunk_size - 4 + 1 );
std::copy( it + 12, it + 8 + chunk_size, extra_buffer.begin() ); std::copy( it + 12, it + 8 + chunk_size, extra_buffer.begin() );
extra_buffer[ chunk_size - 4 ] = '\0';
meta_data.add_item( midi_meta_data_item( 0, "display_name", (const char *) &extra_buffer[0] ) ); meta_data.add_item( midi_meta_data_item( 0, "display_name", (const char *) &extra_buffer[0] ) );
} }
it += 8 + chunk_size; it += 8 + chunk_size;
if ( chunk_size & 1 && it != body_end ) ++it; if ( chunk_size & 1 && it != body_end ) ++it;
} }
else if ( it[ 0 ] == 'L' && it[ 1 ] == 'I' && it[ 2 ] == 'S' && it[ 3 ] == 'T' ) else if ( it_equal( it, "LIST", 4 ) )
{ {
std::vector<uint8_t>::const_iterator chunk_end = it + 8 + chunk_size; std::vector<uint8_t>::const_iterator chunk_end = it + 8 + chunk_size;
if ( it[ 8 ] == 'I' && it[ 9 ] == 'N' && it[ 10 ] == 'F' && it[ 11 ] == 'O' ) if ( it_equal( it + 8, "INFO", 4 ) )
{ {
if ( !found_info ) if ( !found_info )
{ {
if ( chunk_end - it < 12 ) return false; if ( chunk_end - it < 12 ) return false;
it += 12; it += 12;
while ( it != chunk_end ) while ( it != chunk_end )
{ {
if ( chunk_end - it < 4 ) return false; if ( chunk_end - it < 4 ) return false;
uint32_t field_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 ); uint32_t field_size = toInt32LE( it + 4 );
if ( (unsigned long)(chunk_end - it) < 8 + field_size ) return false; if ( (unsigned long)(chunk_end - it) < 8 + field_size ) return false;
std::string field; std::string field;
field.assign( it, it + 4 ); field.assign( it, it + 4 );
for ( unsigned i = 0; i < _countof(riff_tag_mappings); ++i ) for ( unsigned i = 0; i < _countof(riff_tag_mappings); ++i )
{ {
if ( !memcmp( &it[0], riff_tag_mappings[ i ][ 0 ], 4 ) ) if ( !memcmp( &it[0], riff_tag_mappings[ i ][ 0 ], 4 ) )
{ {
field = riff_tag_mappings[ i ][ 1 ]; field = riff_tag_mappings[ i ][ 1 ];
break; break;
} }
} }
extra_buffer.resize( field_size ); extra_buffer.resize( field_size + 1 );
std::copy( it + 8, it + 8 + field_size, extra_buffer.begin() ); std::copy( it + 8, it + 8 + field_size, extra_buffer.begin() );
extra_buffer[ field_size ] = '\0';
it += 8 + field_size; it += 8 + field_size;
meta_data.add_item( midi_meta_data_item( 0, field.c_str(), ( const char * ) &extra_buffer[0] ) ); meta_data.add_item( midi_meta_data_item( 0, field.c_str(), ( const char * ) &extra_buffer[0] ) );
if ( field_size & 1 && it != chunk_end ) ++it; if ( field_size & 1 && it != chunk_end ) ++it;
} }
found_info = true; found_info = true;
} }
else return false; /*throw exception_io_data( "Multiple RIFF LIST INFO chunks found" );*/ else return false; /*throw exception_io_data( "Multiple RIFF LIST INFO chunks found" );*/
} }
else return false; /* unknown LIST chunk */ else return false; /* unknown LIST chunk */
it = chunk_end; it = chunk_end;
if ( chunk_size & 1 && it != body_end ) ++it; if ( chunk_size & 1 && it != body_end ) ++it;
@ -163,10 +192,10 @@ bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi
if ( chunk_size & 1 && it != body_end ) ++it; if ( chunk_size & 1 && it != body_end ) ++it;
} }
if ( found_data && found_info ) break; if ( found_data && found_info ) break;
} }
p_out.set_extra_meta_data( meta_data ); p_out.set_extra_meta_data( meta_data );
return true; return true;
} }

View File

@ -5,6 +5,8 @@ bool midi_processor::is_standard_midi( std::vector<uint8_t> const& p_file )
if ( p_file.size() < 18 ) return false; if ( p_file.size() < 18 ) return false;
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd') return false; if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd') return false;
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false;
if ( p_file[ 10 ] == 0 && p_file[ 11 ] == 0 ) return false; // no tracks
if ( p_file[ 12 ] == 0 && p_file[ 13 ] == 0 ) return false; // dtx == 0, will cause division by zero on tempo calculations
if ( p_file[ 14 ] != 'M' || p_file[ 15 ] != 'T' || p_file[ 16 ] != 'r' || p_file[ 17 ] != 'k' ) return false; if ( p_file[ 14 ] != 'M' || p_file[ 15 ] != 'T' || p_file[ 16 ] != 'r' || p_file[ 17 ] != 'k' ) return false;
return true; return true;
} }
@ -12,29 +14,29 @@ bool midi_processor::is_standard_midi( std::vector<uint8_t> const& p_file )
bool midi_processor::process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count ) bool midi_processor::process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
{ {
track_count = 0; track_count = 0;
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd' ) return false; if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd' ) return false;
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/ if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8; std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
uint16_t form = ( it[0] << 8 ) | it[1]; uint16_t form = ( it[0] << 8 ) | it[1];
if ( form > 2 ) return false; if ( form > 2 ) return false;
uint16_t track_count_16 = ( it[2] << 8 ) | it[3]; uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
if ( form == 2 ) track_count = track_count_16; if ( form == 2 ) track_count = track_count_16;
else track_count = 1; else track_count = 1;
return true; return true;
} }
bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, midi_container & p_out, bool needs_end_marker ) bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, midi_container & p_out, bool needs_end_marker )
{ {
midi_track track; midi_track track;
unsigned current_timestamp = 0; unsigned current_timestamp = 0;
unsigned char last_event_code = 0xFF; unsigned char last_event_code = 0xFF;
unsigned last_sysex_length = 0; unsigned last_sysex_length = 0;
unsigned last_sysex_timestamp = 0; unsigned last_sysex_timestamp = 0;
@ -67,15 +69,15 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
{ {
if ( last_sysex_length ) if ( last_sysex_length )
{ {
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length + 1 ) ); track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
last_sysex_length = 0; last_sysex_length = 0;
} }
last_event_code = event_code; last_event_code = event_code;
if ( !needs_end_marker && ( event_code & 0xF0 ) == 0xE0 ) continue; if ( !needs_end_marker && ( event_code & 0xF0 ) == 0xE0 ) continue;
if ( data_bytes_read < 1 ) if ( data_bytes_read < 1 )
{ {
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 0 ] = *it++; buffer[ 0 ] = *it++;
++data_bytes_read; ++data_bytes_read;
} }
@ -85,7 +87,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
case 0xD0: case 0xD0:
break; break;
default: default:
if ( it == end ) return false; if ( it == end ) return false;
buffer[ data_bytes_read ] = *it++; buffer[ data_bytes_read ] = *it++;
++data_bytes_read; ++data_bytes_read;
} }
@ -95,13 +97,13 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
{ {
if ( last_sysex_length ) if ( last_sysex_length )
{ {
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length + 1 ) ); track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
last_sysex_length = 0; last_sysex_length = 0;
} }
int data_count = decode_delta( it, end ); int data_count = decode_delta( it, end );
if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid System Exclusive message" );*/ if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid System Exclusive message" );*/
if ( end - it < data_count ) return false; if ( end - it < data_count ) return false;
buffer.resize( data_count + 1 ); buffer.resize( data_count + 1 );
buffer[ 0 ] = 0xF0; buffer[ 0 ] = 0xF0;
std::copy( it, it + data_count, buffer.begin() + 1 ); std::copy( it, it + data_count, buffer.begin() + 1 );
@ -115,7 +117,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
int data_count = decode_delta( it, end ); int data_count = decode_delta( it, end );
if ( data_count < 0 ) return false; if ( data_count < 0 ) return false;
if ( end - it < data_count ) return false; if ( end - it < data_count ) return false;
buffer.resize( last_sysex_length + data_count + 1 ); buffer.resize( last_sysex_length + data_count );
std::copy( it, it + data_count, buffer.begin() + last_sysex_length ); std::copy( it, it + data_count, buffer.begin() + last_sysex_length );
it += data_count; it += data_count;
last_sysex_length += data_count; last_sysex_length += data_count;
@ -124,7 +126,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
{ {
if ( last_sysex_length ) if ( last_sysex_length )
{ {
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length + 1 ) ); track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
last_sysex_length = 0; last_sysex_length = 0;
} }
@ -132,7 +134,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
unsigned char meta_type = *it++; unsigned char meta_type = *it++;
int data_count = decode_delta( it, end ); int data_count = decode_delta( it, end );
if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid meta message" );*/ if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid meta message" );*/
if ( end - it < data_count ) return false; if ( end - it < data_count ) return false;
buffer.resize( data_count + 2 ); buffer.resize( data_count + 2 );
buffer[ 0 ] = 0xFF; buffer[ 0 ] = 0xFF;
buffer[ 1 ] = meta_type; buffer[ 1 ] = meta_type;
@ -148,21 +150,21 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
} }
else if ( event_code >= 0xF8 && event_code <= 0xFE ) else if ( event_code >= 0xF8 && event_code <= 0xFE )
{ {
/* Sequencer specific events, single byte */ /* Sequencer specific events, single byte */
buffer[ 0 ] = event_code; buffer[ 0 ] = event_code;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 1 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 1 ) );
} }
else return false; /*throw exception_io_data("Unhandled MIDI status code");*/ else return false; /*throw exception_io_data("Unhandled MIDI status code");*/
} }
if ( !needs_end_marker ) if ( !needs_end_marker )
{ {
buffer[ 0 ] = 0xFF; buffer[ 0 ] = 0xFF;
buffer[ 1 ] = 0x2F; buffer[ 1 ] = 0x2F;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 2 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 2 ) );
} }
p_out.add_track( track ); p_out.add_track( track );
return true; return true;
} }
@ -173,7 +175,7 @@ bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file,
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/ if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8; std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
std::vector<uint8_t>::const_iterator end = p_file.end(); std::vector<uint8_t>::const_iterator end = p_file.end();
uint16_t form = ( it[0] << 8 ) | it[1]; uint16_t form = ( it[0] << 8 ) | it[1];
if ( form > 2 ) return false; if ( form > 2 ) return false;
@ -181,19 +183,22 @@ bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file,
uint16_t track_count_16 = ( it[2] << 8 ) | it[3]; uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
uint16_t dtx = ( it[4] << 8 ) | it[5]; uint16_t dtx = ( it[4] << 8 ) | it[5];
if ( !track_count_16 || !dtx )
return false;
it += 6; it += 6;
std::size_t track_count = track_count_16; std::size_t track_count = track_count_16;
p_out.initialize( form, dtx ); p_out.initialize( form, dtx );
for ( std::size_t i = 0; i < track_count; ++i ) for ( std::size_t i = 0; i < track_count; ++i )
{ {
if ( end - it < 8 ) return false; if ( end - it < 8 ) return false;
if ( it[0] != 'M' || it[1] != 'T' || it[2] != 'r' || it[3] != 'k' ) return false; if ( it[0] != 'M' || it[1] != 'T' || it[2] != 'r' || it[3] != 'k' ) return false;
uint32_t track_size = ( it[4] << 24 ) | ( it[5] << 16 ) | ( it[6] << 8 ) | it[7]; uint32_t track_size = ( it[4] << 24 ) | ( it[5] << 16 ) | ( it[6] << 8 ) | it[7];
if ( (unsigned long)(end - it) < track_size ) return false; if ( (unsigned long)(end - it) < track_size ) return false;
it += 8; it += 8;
@ -201,12 +206,12 @@ bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file,
if ( !process_standard_midi_track( it, it + track_size, p_out, true ) ) return false; if ( !process_standard_midi_track( it, it + track_size, p_out, true ) ) return false;
track_data_offset += track_size; track_data_offset += track_size;
if ( it - p_file.begin() != track_data_offset ) if ( it - p_file.begin() != track_data_offset )
{ {
it = p_file.begin() + track_data_offset; it = p_file.begin() + track_data_offset;
} }
} }
return true; return true;
} }

View File

@ -1,35 +1,35 @@
#include "midi_processor.h" #include "midi_processor.h"
bool midi_processor::is_syx( std::vector<uint8_t> const& p_file ) bool midi_processor::is_syx( std::vector<uint8_t> const& p_file )
{ {
if ( p_file.size() < 2 ) return false; if ( p_file.size() < 2 ) return false;
if ( p_file[ 0 ] != 0xF0 || p_file[ p_file.size() - 1 ] != 0xF7 ) return false; if ( p_file[ 0 ] != 0xF0 || p_file[ p_file.size() - 1 ] != 0xF7 ) return false;
return true; return true;
} }
bool midi_processor::process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out ) bool midi_processor::process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out )
{ {
const size_t size = p_file.size(); const size_t size = p_file.size();
size_t ptr = 0; size_t ptr = 0;
p_out.initialize( 0, 1 ); p_out.initialize( 0, 1 );
midi_track track; midi_track track;
while ( ptr < size ) while ( ptr < size )
{ {
size_t msg_length = 1; size_t msg_length = 1;
if ( p_file[ptr] != 0xF0 ) return false; if ( p_file[ptr] != 0xF0 ) return false;
while ( p_file[ptr + msg_length++] != 0xF7 ); while ( p_file[ptr + msg_length++] != 0xF7 );
track.add_event( midi_event( 0, midi_event::extended, 0, &p_file[ptr], msg_length ) ); track.add_event( midi_event( 0, midi_event::extended, 0, &p_file[ptr], msg_length ) );
ptr += msg_length; ptr += msg_length;
} }
p_out.add_track( track ); p_out.add_track( track );
return true; return true;
} }

View File

@ -15,21 +15,21 @@ const uint8_t midi_processor::xmi_default_tempo[5] = {0xFF, 0x51, 0x07, 0xA1, 0x
unsigned midi_processor::decode_xmi_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end ) unsigned midi_processor::decode_xmi_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
{ {
unsigned delta = 0; unsigned delta = 0;
if ( it == end ) return 0; if ( it == end ) return 0;
uint8_t byte = *it++; uint8_t byte = *it++;
if ( !( byte & 0x80 ) ) if ( !( byte & 0x80 ) )
{ {
do do
{ {
delta += byte; delta += byte;
if ( it == end ) break; if ( it == end ) break;
byte = *it++; byte = *it++;
} }
while ( !( byte & 0x80 ) && it != end ); while ( !( byte & 0x80 ) && it != end );
} }
--it; --it;
return delta; return delta;
} }
struct iff_chunk struct iff_chunk
@ -39,46 +39,46 @@ struct iff_chunk
std::vector<uint8_t> m_data; std::vector<uint8_t> m_data;
std::vector<iff_chunk> m_sub_chunks; std::vector<iff_chunk> m_sub_chunks;
iff_chunk() iff_chunk()
{ {
memset( m_id, 0, sizeof( m_id ) ); memset( m_id, 0, sizeof( m_id ) );
memset( m_type, 0, sizeof( m_type ) ); memset( m_type, 0, sizeof( m_type ) );
} }
iff_chunk( const iff_chunk & p_in ) iff_chunk( const iff_chunk & p_in )
{ {
memcpy( m_id, p_in.m_id, sizeof( m_id ) ); memcpy( m_id, p_in.m_id, sizeof( m_id ) );
memcpy( m_type, p_in.m_type, sizeof( m_type ) ); memcpy( m_type, p_in.m_type, sizeof( m_type ) );
m_data = p_in.m_data; m_data = p_in.m_data;
m_sub_chunks = p_in.m_sub_chunks; m_sub_chunks = p_in.m_sub_chunks;
} }
const iff_chunk & find_sub_chunk( const char * p_id, unsigned index = 0 ) const const iff_chunk & find_sub_chunk( const char * p_id, unsigned index = 0 ) const
{ {
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
{
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
{
if ( index ) --index;
if ( !index ) return m_sub_chunks[ i ];
}
}
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
return *this;
}
unsigned get_chunk_count( const char * p_id ) const
{
unsigned chunk_count = 0;
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i ) for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
{ {
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) ) if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
{ {
++chunk_count; if ( index ) --index;
} if ( !index ) return m_sub_chunks[ i ];
} }
return chunk_count; }
} /*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
return *this;
}
unsigned get_chunk_count( const char * p_id ) const
{
unsigned chunk_count = 0;
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
{
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
{
++chunk_count;
}
}
return chunk_count;
}
}; };
struct iff_stream struct iff_stream
@ -87,69 +87,69 @@ struct iff_stream
iff_chunk fail; iff_chunk fail;
const iff_chunk & find_chunk( const char * p_id ) const const iff_chunk & find_chunk( const char * p_id ) const
{ {
for ( std::size_t i = 0; i < m_chunks.size(); ++i ) for ( std::size_t i = 0; i < m_chunks.size(); ++i )
{ {
if ( !memcmp( p_id, m_chunks[ i ].m_id, 4 ) ) if ( !memcmp( p_id, m_chunks[ i ].m_id, 4 ) )
{ {
return m_chunks[ i ]; return m_chunks[ i ];
} }
} }
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/ /*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
return fail; return fail;
} }
}; };
static bool read_iff_chunk( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, iff_chunk & p_out, bool first_chunk ) static bool read_iff_chunk( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, iff_chunk & p_out, bool first_chunk )
{ {
if ( end - it < 8 ) return false; if ( end - it < 8 ) return false;
std::copy( it, it + 4, p_out.m_id ); std::copy( it, it + 4, p_out.m_id );
it += 4; it += 4;
uint32_t chunk_size = ( it[ 0 ] << 24 ) | ( it[ 1 ] << 16 ) | ( it[ 2 ] << 8 ) | it[ 3 ]; uint32_t chunk_size = ( it[ 0 ] << 24 ) | ( it[ 1 ] << 16 ) | ( it[ 2 ] << 8 ) | it[ 3 ];
if ( (unsigned long)(end - it) < chunk_size ) return false; if ( (unsigned long)(end - it) < chunk_size ) return false;
it += 4; it += 4;
bool is_cat_chunk = !memcmp( p_out.m_id, "CAT ", 4 ); bool is_cat_chunk = !memcmp( p_out.m_id, "CAT ", 4 );
bool is_form_chunk = !memcmp( p_out.m_id, "FORM", 4 ); bool is_form_chunk = !memcmp( p_out.m_id, "FORM", 4 );
std::size_t chunk_size_limit = end - it; std::size_t chunk_size_limit = end - it;
if ( chunk_size > chunk_size_limit ) chunk_size = (uint32_t) chunk_size_limit; if ( chunk_size > chunk_size_limit ) chunk_size = (uint32_t) chunk_size_limit;
if ( ( first_chunk && is_form_chunk ) || ( !first_chunk && is_cat_chunk ) ) if ( ( first_chunk && is_form_chunk ) || ( !first_chunk && is_cat_chunk ) )
{ {
if ( end - it < 4 ) return false; if ( end - it < 4 ) return false;
std::vector<uint8_t>::const_iterator chunk_end = it + chunk_size; std::vector<uint8_t>::const_iterator chunk_end = it + chunk_size;
std::copy( it, it + 4, p_out.m_type ); std::copy( it, it + 4, p_out.m_type );
it += 4; it += 4;
while ( it < chunk_end ) while ( it < chunk_end )
{ {
iff_chunk chunk; iff_chunk chunk;
if ( !read_iff_chunk( it, chunk_end, chunk, is_cat_chunk ) ) return false; if ( !read_iff_chunk( it, chunk_end, chunk, is_cat_chunk ) ) return false;
p_out.m_sub_chunks.push_back( chunk ); p_out.m_sub_chunks.push_back( chunk );
} }
it = chunk_end; it = chunk_end;
if ( chunk_size & 1 && it != end ) ++it; if ( chunk_size & 1 && it != end ) ++it;
} }
else if ( !is_form_chunk && !is_cat_chunk ) else if ( !is_form_chunk && !is_cat_chunk )
{ {
p_out.m_data.assign( it, it + chunk_size ); p_out.m_data.assign( it, it + chunk_size );
it += chunk_size; it += chunk_size;
if ( chunk_size & 1 && it != end ) ++it; if ( chunk_size & 1 && it != end ) ++it;
} }
else else
{ {
/*if ( first_chunk ) throw exception_io_data( pfc::string_formatter() << "Found " << pfc::string8( (const char *)p_out.m_id, 4 ) << " chunk instead of FORM" ); /*if ( first_chunk ) throw exception_io_data( pfc::string_formatter() << "Found " << pfc::string8( (const char *)p_out.m_id, 4 ) << " chunk instead of FORM" );
else throw exception_io_data( "Found multiple FORM chunks" );*/ else throw exception_io_data( "Found multiple FORM chunks" );*/
return false; return false;
} }
return true; return true;
} }
static bool read_iff_stream( std::vector<uint8_t> const& p_file, iff_stream & p_out ) static bool read_iff_stream( std::vector<uint8_t> const& p_file, iff_stream & p_out )
{ {
std::vector<uint8_t>::const_iterator it = p_file.begin(), end = p_file.end(); std::vector<uint8_t>::const_iterator it = p_file.begin(), end = p_file.end();
bool first_chunk = true; bool first_chunk = true;
while ( it != end ) while ( it != end )
{ {
iff_chunk chunk; iff_chunk chunk;
if ( read_iff_chunk( it, end, chunk, first_chunk ) ) if ( read_iff_chunk( it, end, chunk, first_chunk ) )
{ {
p_out.m_chunks.push_back( chunk ); p_out.m_chunks.push_back( chunk );
@ -159,27 +159,27 @@ static bool read_iff_stream( std::vector<uint8_t> const& p_file, iff_stream & p_
return false; return false;
else else
break; break;
} }
return true; return true;
} }
bool midi_processor::process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count ) bool midi_processor::process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
{ {
track_count = 0; track_count = 0;
iff_stream xmi_file; iff_stream xmi_file;
if ( !read_iff_stream( p_file, xmi_file ) ) return false; if ( !read_iff_stream( p_file, xmi_file ) ) return false;
const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" ); const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" );
if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/ if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/
const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " ); const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " );
if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/ if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/
unsigned _track_count = cat_chunk.get_chunk_count( "FORM" ); unsigned _track_count = cat_chunk.get_chunk_count( "FORM" );
track_count = _track_count; track_count = _track_count;
return true; return true;
} }
@ -188,32 +188,32 @@ bool midi_processor::process_xmi( std::vector<uint8_t> const& p_file, midi_conta
iff_stream xmi_file; iff_stream xmi_file;
if ( !read_iff_stream( p_file, xmi_file ) ) return false; if ( !read_iff_stream( p_file, xmi_file ) ) return false;
const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" ); const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" );
if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/ if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/
const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " ); const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " );
if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/ if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/
unsigned track_count = cat_chunk.get_chunk_count( "FORM" ); unsigned track_count = cat_chunk.get_chunk_count( "FORM" );
p_out.initialize( track_count > 1 ? 2 : 0, 60 ); p_out.initialize( track_count > 1 ? 2 : 0, 60 );
for ( unsigned i = 0; i < track_count; ++i ) for ( unsigned i = 0; i < track_count; ++i )
{ {
const iff_chunk & xmid_form_chunk = cat_chunk.find_sub_chunk( "FORM", i ); const iff_chunk & xmid_form_chunk = cat_chunk.find_sub_chunk( "FORM", i );
if ( memcmp( xmid_form_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI nested FORM chunk not XMID type" );*/ if ( memcmp( xmid_form_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI nested FORM chunk not XMID type" );*/
const iff_chunk & event_chunk = xmid_form_chunk.find_sub_chunk( "EVNT" ); const iff_chunk & event_chunk = xmid_form_chunk.find_sub_chunk( "EVNT" );
if ( memcmp( event_chunk.m_id, "EVNT", 4 ) ) return false; /* EVNT chunk not found */ if ( memcmp( event_chunk.m_id, "EVNT", 4 ) ) return false; /* EVNT chunk not found */
std::vector<uint8_t> const& event_body = event_chunk.m_data; std::vector<uint8_t> const& event_body = event_chunk.m_data;
midi_track track; midi_track track;
bool initial_tempo = false; bool initial_tempo = false;
unsigned current_timestamp = 0; unsigned current_timestamp = 0;
unsigned last_event_timestamp = 0; unsigned last_event_timestamp = 0;
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
buffer.resize( 3 ); buffer.resize( 3 );
@ -221,94 +221,94 @@ bool midi_processor::process_xmi( std::vector<uint8_t> const& p_file, midi_conta
std::vector<uint8_t>::const_iterator it = event_body.begin(), end = event_body.end(); std::vector<uint8_t>::const_iterator it = event_body.begin(), end = event_body.end();
while ( it != end ) while ( it != end )
{ {
unsigned delta = decode_xmi_delta( it, end ); unsigned delta = decode_xmi_delta( it, end );
current_timestamp += delta; current_timestamp += delta;
if ( current_timestamp > last_event_timestamp ) if ( current_timestamp > last_event_timestamp )
{ {
last_event_timestamp = current_timestamp; last_event_timestamp = current_timestamp;
} }
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 0 ] = *it++; buffer[ 0 ] = *it++;
if ( buffer[ 0 ] == 0xFF ) if ( buffer[ 0 ] == 0xFF )
{ {
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
long meta_count; long meta_count;
if ( buffer[ 1 ] == 0x2F ) if ( buffer[ 1 ] == 0x2F )
{ {
meta_count = 0; meta_count = 0;
} }
else else
{ {
meta_count = decode_delta( it, end ); meta_count = decode_delta( it, end );
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI meta message" );*/ if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI meta message" );*/
if ( end - it < meta_count ) return false; if ( end - it < meta_count ) return false;
buffer.resize( meta_count + 2 ); buffer.resize( meta_count + 2 );
std::copy( it, it + meta_count, buffer.begin() + 2 ); std::copy( it, it + meta_count, buffer.begin() + 2 );
it += meta_count; it += meta_count;
} }
if ( buffer[ 1 ] == 0x2F && current_timestamp < last_event_timestamp ) if ( buffer[ 1 ] == 0x2F && current_timestamp < last_event_timestamp )
{ {
current_timestamp = last_event_timestamp; current_timestamp = last_event_timestamp;
} }
if ( buffer[ 1 ] == 0x51 && meta_count == 3 ) if ( buffer[ 1 ] == 0x51 && meta_count == 3 )
{ {
unsigned tempo = buffer[ 2 ] * 0x10000 + buffer[ 3 ] * 0x100 + buffer[ 4 ]; unsigned tempo = buffer[ 2 ] * 0x10000 + buffer[ 3 ] * 0x100 + buffer[ 4 ];
unsigned ppqn = ( tempo * 3 ) / 25000; unsigned ppqn = ( tempo * 3 ) / 25000;
tempo = tempo * 60 / ppqn; tempo = tempo * 60 / ppqn;
buffer[ 2 ] = tempo / 0x10000; buffer[ 2 ] = tempo / 0x10000;
buffer[ 3 ] = tempo / 0x100; buffer[ 3 ] = tempo / 0x100;
buffer[ 4 ] = tempo; buffer[ 4 ] = tempo;
if ( current_timestamp == 0 ) initial_tempo = true; if ( current_timestamp == 0 ) initial_tempo = true;
} }
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
if ( buffer[ 1 ] == 0x2F ) break; if ( buffer[ 1 ] == 0x2F ) break;
} }
else if ( buffer[ 0 ] == 0xF0 ) else if ( buffer[ 0 ] == 0xF0 )
{ {
long system_exclusive_count = decode_delta( it, end ); long system_exclusive_count = decode_delta( it, end );
if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI System Exclusive message" );*/ if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI System Exclusive message" );*/
if ( end - it < system_exclusive_count ) return false; if ( end - it < system_exclusive_count ) return false;
buffer.resize( system_exclusive_count + 1 ); buffer.resize( system_exclusive_count + 1 );
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 ); std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
it += system_exclusive_count; it += system_exclusive_count;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) ); track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
} }
else if ( buffer[ 0 ] >= 0x80 && buffer[ 0 ] <= 0xEF ) else if ( buffer[ 0 ] >= 0x80 && buffer[ 0 ] <= 0xEF )
{ {
unsigned bytes_read = 1; unsigned bytes_read = 1;
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 1 ] = *it++; buffer[ 1 ] = *it++;
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 ); midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
unsigned channel = buffer[ 0 ] & 0x0F; unsigned channel = buffer[ 0 ] & 0x0F;
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch ) if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
{ {
if ( it == end ) return false; if ( it == end ) return false;
buffer[ 2 ] = *it++; buffer[ 2 ] = *it++;
bytes_read = 2; bytes_read = 2;
} }
track.add_event( midi_event( current_timestamp, type, channel, &buffer[1], bytes_read ) ); track.add_event( midi_event( current_timestamp, type, channel, &buffer[1], bytes_read ) );
if ( type == midi_event::note_on ) if ( type == midi_event::note_on )
{ {
buffer[ 2 ] = 0x00; buffer[ 2 ] = 0x00;
int note_length = decode_delta( it, end ); int note_length = decode_delta( it, end );
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid XMI note message" );*/ if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid XMI note message" );*/
unsigned note_end_timestamp = current_timestamp + note_length; unsigned note_end_timestamp = current_timestamp + note_length;
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp; if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
track.add_event( midi_event( note_end_timestamp, type, channel, &buffer[1], bytes_read ) ); track.add_event( midi_event( note_end_timestamp, type, channel, &buffer[1], bytes_read ) );
} }
} }
else return false; /*throw exception_io_data( "Unexpected XMI status code" );*/ else return false; /*throw exception_io_data( "Unexpected XMI status code" );*/
} }
if ( !initial_tempo ) if ( !initial_tempo )
track.add_event( midi_event( 0, midi_event::extended, 0, xmi_default_tempo, _countof( xmi_default_tempo ) ) ); track.add_event( midi_event( 0, midi_event::extended, 0, xmi_default_tempo, _countof( xmi_default_tempo ) ) );
p_out.add_track( track ); p_out.add_track( track );
} }
return true; return true;
} }

View File

@ -62,7 +62,7 @@ static OSType getOSType(const char * in_)
track_num = [[[s url] fragment] intValue]; //What if theres no fragment? Assuming we get 0. track_num = [[[s url] fragment] intValue]; //What if theres no fragment? Assuming we get 0.
midi_file.scan_for_loops( true, true, true ); midi_file.scan_for_loops( true, true, true, true );
framesLength = midi_file.get_timestamp_end( track_num, true ); framesLength = midi_file.get_timestamp_end( track_num, true );