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
{
enum
{
max_static_data_count = 16
};
enum
{
max_static_data_count = 16
};
enum event_type
{
note_off = 0,
note_on,
polyphonic_aftertouch,
control_change,
program_change,
channel_aftertouch,
pitch_wheel,
extended
};
enum event_type
{
note_off = 0,
note_on,
polyphonic_aftertouch,
control_change,
program_change,
channel_aftertouch,
pitch_wheel,
extended
};
unsigned long m_timestamp;
unsigned long m_timestamp;
event_type m_type;
unsigned m_channel;
unsigned long m_data_count;
event_type m_type;
unsigned m_channel;
unsigned long m_data_count;
uint8_t m_data[max_static_data_count];
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( const midi_event & p_in );
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( 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;
};
@ -51,23 +51,24 @@ class midi_track
std::vector<midi_event> m_events;
public:
midi_track() { }
midi_track(const midi_track & p_in);
midi_track() { }
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;
const midi_event & operator [] ( std::size_t p_index ) const;
midi_event & operator [] ( std::size_t p_index );
void remove_event( unsigned long index );
};
struct tempo_entry
{
unsigned long m_timestamp;
unsigned m_tempo;
unsigned long m_timestamp;
unsigned m_tempo;
tempo_entry() : m_timestamp(0), m_tempo(0) { }
tempo_entry(unsigned long p_timestamp, unsigned p_tempo);
tempo_entry() : m_timestamp(0), m_tempo(0) { }
tempo_entry(unsigned long p_timestamp, unsigned p_tempo);
};
class tempo_map
@ -75,11 +76,12 @@ class tempo_map
std::vector<tempo_entry> m_entries;
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;
std::size_t get_count() const;
const tempo_entry & operator [] ( std::size_t p_index ) const;
tempo_entry & operator [] ( std::size_t p_index );
};
struct system_exclusive_entry
@ -87,8 +89,8 @@ struct system_exclusive_entry
std::size_t m_port;
std::size_t m_offset;
std::size_t m_length;
system_exclusive_entry() : m_port(0), m_offset(0), m_length(0) { }
system_exclusive_entry(const system_exclusive_entry & p_in);
system_exclusive_entry() : m_port(0), m_offset(0), m_length(0) { }
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);
};
@ -104,42 +106,42 @@ public:
struct midi_stream_event
{
unsigned long m_timestamp;
uint32_t m_event;
unsigned long m_timestamp;
uint32_t m_event;
midi_stream_event() : m_timestamp(0), m_event(0) { }
midi_stream_event(unsigned long p_timestamp, uint32_t p_event);
midi_stream_event() : m_timestamp(0), m_event(0) { }
midi_stream_event(unsigned long p_timestamp, uint32_t p_event);
};
struct midi_meta_data_item
{
unsigned long m_timestamp;
unsigned long m_timestamp;
std::string m_name;
std::string m_value;
midi_meta_data_item() : m_timestamp(0) { }
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() : m_timestamp(0) { }
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);
};
class midi_meta_data
{
std::vector<midi_meta_data_item> m_data;
std::vector<uint8_t> m_bitmap;
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 );
bool get_item( const char * p_name, midi_meta_data_item & p_out ) const;
void append( const midi_meta_data & p_data );
bool get_item( const char * p_name, midi_meta_data_item & p_out ) const;
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 );
std::size_t get_count() const;
const midi_meta_data_item & operator [] ( std::size_t p_index ) const;
@ -148,16 +150,16 @@ public:
class midi_container
{
public:
enum
{
clean_flag_emidi = 1 << 0,
clean_flag_instruments = 1 << 1,
clean_flag_banks = 1 << 2,
};
enum
{
clean_flag_emidi = 1 << 0,
clean_flag_instruments = 1 << 1,
clean_flag_banks = 1 << 2,
};
private:
unsigned m_form;
unsigned m_dtx;
unsigned m_form;
unsigned m_dtx;
std::vector<uint64_t> m_channel_mask;
std::vector<tempo_map> m_tempo_map;
std::vector<midi_track> m_tracks;
@ -166,7 +168,7 @@ private:
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;
@ -207,9 +209,9 @@ private:
public:
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 );
@ -219,7 +221,7 @@ public:
void merge_tracks( const midi_container & p_source );
void set_track_count( unsigned count );
void set_extra_meta_data( const midi_meta_data & p_data );
/*
* Blah.
* Hack 0: Remove channel 16
@ -233,6 +235,17 @@ public:
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( 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_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 );
};

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_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_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_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
public:
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_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[ 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 )
{
uint8_t buffer[10];
p_out.initialize( 0, 0xC0 );
p_out.initialize( 0, 0xC0 );
uint16_t tempo = ( p_file[ 4 ] << 8 ) | p_file[ 5 ];
uint32_t tempo_scaled = tempo * 100000;
midi_track track;
midi_track track;
buffer[0] = 0xFF;
buffer[1] = 0x51;
buffer[2] = tempo_scaled >> 16;
buffer[3] = tempo_scaled >> 8;
buffer[4] = tempo_scaled;
buffer[0] = 0xFF;
buffer[1] = 0x51;
buffer[2] = tempo_scaled >> 16;
buffer[3] = tempo_scaled >> 8;
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[1] = 0x41;
buffer[2] = 0x10;
buffer[3] = 0x16;
buffer[4] = 0x12;
buffer[5] = 0x7F;
buffer[6] = 0x00;
buffer[7] = 0x00;
buffer[8] = 0x01;
buffer[9] = 0xF7;
buffer[0] = 0xF0;
buffer[1] = 0x41;
buffer[2] = 0x10;
buffer[3] = 0x16;
buffer[4] = 0x12;
buffer[5] = 0x7F;
buffer[6] = 0x00;
buffer[7] = 0x00;
buffer[8] = 0x01;
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[1] = 0x2F;
buffer[0] = 0xFF;
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;

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 delta = 0;
unsigned char byte;
do
{
if ( it == end ) return 0;
int delta = 0;
unsigned char byte;
do
{
if ( it == end ) return 0;
byte = *it++;
delta = ( delta << 7 ) + ( byte & 0x7F );
}
while ( byte & 0x80 );
return delta;
delta = ( delta << 7 ) + ( byte & 0x7F );
}
while ( byte & 0x80 );
return delta;
}
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 ) )
{
{
return process_standard_midi( p_file, p_out );
}
}
else if ( is_riff_midi( p_file ) )
{
{
return process_riff_midi( p_file, p_out );
}
}
else if ( is_hmp( p_file ) )
{
{
return process_hmp( p_file, p_out );
}
else if ( is_hmi( p_file ) )
{
{
return process_hmi( p_file, p_out );
}
else if ( is_xmi( p_file ) )
{
{
return process_xmi( p_file, p_out );
}
else if ( is_mus( p_file ) )
{
{
return process_mus( p_file, p_out );
}
else if ( is_mids( p_file ) )
{
{
return process_mids( p_file, p_out );
}
else if ( is_lds( p_file, p_extension ) )
{
{
return process_lds( p_file, p_out );
}
else if ( is_gmf( p_file ) )
{
{
return process_gmf( p_file, p_out );
}
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 )
{
track_count = 0;
if ( is_standard_midi( p_file ) )
{
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_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() )
return false;
if ( track_table_offset >= p_file.size() || track_table_offset + track_count * 4 > p_file.size() )
return false;
it = p_file.begin() + track_table_offset;
std::vector<uint32_t> track_offsets;
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 );
it += 4;
}
}
p_out.initialize( 1, 0xC0 );
p_out.initialize( 1, 0xC0 );
{
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, end_of_track, _countof( end_of_track ) ) );
p_out.add_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, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track );
}
for ( unsigned i = 0; i < track_count; ++i )
{
unsigned track_offset = track_offsets[ i ];
unsigned long track_length;
if ( i + 1 < track_count )
{
track_length = track_offsets[ i + 1 ] - track_offset;
}
else
{
for ( unsigned i = 0; i < track_count; ++i )
{
unsigned track_offset = track_offsets[ i ];
unsigned long track_length;
if ( i + 1 < track_count )
{
track_length = track_offsets[ i + 1 ] - track_offset;
}
else
{
track_length = p_file.size() - track_offset;
}
if ( track_offset >= p_file.size() || track_offset + track_length > p_file.size() )
return false;
}
if ( track_offset >= p_file.size() || track_offset + track_length > p_file.size() )
return false;
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;
@ -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[ 12 ] != 'K' ) return false;
midi_track track;
unsigned current_timestamp = 0;
unsigned char last_event_code = 0xFF;
midi_track track;
unsigned current_timestamp = 0;
unsigned char last_event_code = 0xFF;
unsigned last_event_timestamp = 0;
unsigned last_event_timestamp = 0;
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 );
if ( meta_offset && meta_offset + 1 < track_length )
{
{
buffer.resize( 2 );
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;
buffer.resize( meta_size + 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 )
{
buffer[ 0 ] = 0xFF;
buffer[ 1 ] = 0x01;
track.add_event( midi_event( 0, midi_event::extended, 0, &buffer[0], meta_size + 2 ) );
}
}
}
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 );
while ( it != track_end )
{
{
int delta = decode_delta( it, track_end );
if ( delta > 0xFFFF || delta < 0 )
{
current_timestamp = last_event_timestamp;
if ( delta > 0xFFFF || delta < 0 )
{
current_timestamp = last_event_timestamp;
/*console::formatter() << "[foo_midi] Large HMI delta detected, shunting.";*/
}
else
{
current_timestamp += delta;
if ( current_timestamp > last_event_timestamp )
{
last_event_timestamp = current_timestamp;
}
}
}
else
{
current_timestamp += delta;
if ( current_timestamp > last_event_timestamp )
{
last_event_timestamp = current_timestamp;
}
}
if ( it == track_end ) return false;
if ( it == track_end ) return false;
buffer[ 0 ] = *it++;
if ( buffer[ 0 ] == 0xFF )
{
last_event_code = 0xFF;
if ( it == track_end ) return false;
if ( buffer[ 0 ] == 0xFF )
{
last_event_code = 0xFF;
if ( it == track_end ) return false;
buffer[ 1 ] = *it++;
int meta_count = decode_delta( it, track_end );
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 );
std::copy( it, it + meta_count, buffer.begin() + 2 );
it += meta_count;
if ( buffer[ 1 ] == 0x2F && last_event_timestamp > current_timestamp )
{
current_timestamp = last_event_timestamp;
}
if ( buffer[ 1 ] == 0x2F && last_event_timestamp > current_timestamp )
{
current_timestamp = last_event_timestamp;
}
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
if ( buffer[ 1 ] == 0x2F ) break;
}
else if ( buffer[ 0 ] == 0xF0 )
{
last_event_code = 0xFF;
if ( buffer[ 1 ] == 0x2F ) break;
}
else if ( buffer[ 0 ] == 0xF0 )
{
last_event_code = 0xFF;
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 ( track_end - it < system_exclusive_count ) return false;
if ( track_end - it < system_exclusive_count ) return false;
buffer.resize( system_exclusive_count + 1 );
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
it += system_exclusive_count;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
}
else if ( buffer[ 0 ] == 0xFE )
{
last_event_code = 0xFF;
if ( it == track_end ) return false;
}
else if ( buffer[ 0 ] == 0xFE )
{
last_event_code = 0xFF;
if ( it == track_end ) return false;
buffer[ 1 ] = *it++;
if ( buffer[ 1 ] == 0x10 )
{
if ( track_end - it < 3 ) return false;
if ( buffer[ 1 ] == 0x10 )
{
if ( track_end - it < 3 ) return false;
it += 2;
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;
}
else if ( buffer[ 1 ] == 0x12 )
{
if ( track_end - it < 2 ) return false;
}
else if ( buffer[ 1 ] == 0x12 )
{
if ( track_end - it < 2 ) return false;
it += 2;
}
else if ( buffer[ 1 ] == 0x13 )
{
if ( track_end - it < 10 ) return false;
}
else if ( buffer[ 1 ] == 0x13 )
{
if ( track_end - it < 10 ) return false;
it += 10;
}
else if ( buffer[ 1 ] == 0x14 )
{
if ( track_end - it < 2 ) return false;
}
else if ( buffer[ 1 ] == 0x14 )
{
if ( track_end - it < 2 ) return false;
it += 2;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_start, _countof( loop_start ) ) );
}
else if ( buffer[ 1 ] == 0x15 )
{
if ( track_end - it < 6 ) return false;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_start, _countof( loop_start ) ) );
}
else if ( buffer[ 1 ] == 0x15 )
{
if ( track_end - it < 6 ) return false;
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 if ( buffer[ 0 ] <= 0xEF )
{
unsigned bytes_read = 1;
if ( buffer[ 0 ] >= 0x80 )
{
if ( it == track_end ) return false;
}
else if ( buffer[ 0 ] <= 0xEF )
{
unsigned bytes_read = 1;
if ( buffer[ 0 ] >= 0x80 )
{
if ( it == track_end ) return false;
buffer[ 1 ] = *it++;
last_event_code = buffer[ 0 ];
}
else
{
last_event_code = buffer[ 0 ];
}
else
{
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[ 0 ] = last_event_code;
}
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
unsigned channel = buffer[ 0 ] & 0x0F;
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
{
if ( it == track_end ) return false;
buffer[ 1 ] = buffer[ 0 ];
buffer[ 0 ] = last_event_code;
}
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
unsigned channel = buffer[ 0 ] & 0x0F;
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
{
if ( it == track_end ) return false;
buffer[ 2 ] = *it++;
bytes_read = 2;
}
bytes_read = 2;
}
track.add_event( midi_event( current_timestamp, type, channel, &buffer[ 1 ], bytes_read ) );
if ( type == midi_event::note_on )
{
buffer[ 2 ] = 0x00;
if ( type == midi_event::note_on )
{
buffer[ 2 ] = 0x00;
int note_length = decode_delta( it, track_end );
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid HMI note message" );*/
unsigned note_end_timestamp = current_timestamp + note_length;
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
unsigned note_end_timestamp = current_timestamp + note_length;
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 ) );
}
}
}
}
else return false; /*throw exception_io_data( "Unexpected HMI status code" );*/
}
}
p_out.add_track( track );
}
p_out.add_track( track );
}
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 delta = 0;
unsigned shift = 0;
unsigned char byte;
do
unsigned delta = 0;
unsigned shift = 0;
unsigned char byte;
do
{
if ( it == end ) return 0;
if ( it == end ) return 0;
byte = *it++;
delta = delta + ( ( byte & 0x7F ) << shift );
shift += 7;
}
while ( !( byte & 0x80 ) );
return delta;
delta = delta + ( ( byte & 0x7F ) << shift );
shift += 7;
}
while ( !( byte & 0x80 ) );
return delta;
}
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;
uint16_t dtx = 0xC0;
uint32_t offset = is_funky ? 0x1A : 0x30;
uint32_t offset = is_funky ? 0x1A : 0x30;
if ( offset >= p_file.size() )
return false;
if ( offset >= p_file.size() )
return false;
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;
if ( is_funky )
if ( is_funky )
{
if ( p_file.size() <= 0x4D )
return false;
if ( p_file.size() <= 0x4D )
return false;
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;
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 ) ) );
p_out.add_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, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track );
}
uint8_t buffer[ 4 ];
if ( it == end ) return false;
if ( it == end ) return false;
buffer[ 0 ] = *it++;
while ( it != end )
{
if ( buffer[ 0 ] != 0xFF )
{
{
if ( buffer[ 0 ] != 0xFF )
{
buffer[ 0 ] = *it++;
continue;
}
if ( it == end ) break;
continue;
}
if ( it == end ) break;
buffer[ 1 ] = *it++;
if ( buffer[ 1 ] != 0x2F )
{
buffer[ 0 ] = buffer[ 1 ];
continue;
}
break;
}
if ( buffer[ 1 ] != 0x2F )
{
buffer[ 0 ] = buffer[ 1 ];
continue;
}
break;
}
offset = is_funky ? 3 : 5;
if ( (unsigned long)(end - it) < offset ) return false;
offset = is_funky ? 3 : 5;
if ( (unsigned long)(end - it) < offset ) return false;
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;
uint32_t track_size_32;
if ( is_funky )
{
if ( end - it < 4 ) break;
if ( is_funky )
{
if ( end - it < 4 ) break;
track_size_16 = it[ 0 ] | ( it[ 1 ] << 8 );
it += 2;
track_size_32 = track_size_16 - 4;
if ( (unsigned long)(end - it) < track_size_32 + 2 ) break;
it += 2;
}
else
{
if ( end - it < 8 ) break;
}
else
{
if ( end - it < 8 ) break;
track_size_32 = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4;
track_size_32 -= 12;
track_size_32 -= 12;
if ( (unsigned long)(end - it) < track_size_32 + 8 ) break;
it += 4;
}
}
midi_track track;
midi_track track;
unsigned current_timestamp = 0;
unsigned current_timestamp = 0;
std::vector<uint8_t> _buffer;
_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;
while ( it != track_end )
{
{
unsigned delta = decode_hmp_delta( it, track_end );
current_timestamp += delta;
if ( it == track_end ) return false;
current_timestamp += delta;
if ( it == track_end ) return false;
_buffer[ 0 ] = *it++;
if ( _buffer[ 0 ] == 0xFF )
{
if ( it == track_end ) return false;
if ( _buffer[ 0 ] == 0xFF )
{
if ( it == track_end ) return false;
_buffer[ 1 ] = *it++;
int meta_count = decode_delta( it, track_end );
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 );
std::copy( it, it + meta_count, _buffer.begin() + 2 );
it += meta_count;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &_buffer[0], meta_count + 2 ) );
if ( _buffer[ 1 ] == 0x2F ) break;
}
else if ( _buffer[ 0 ] >= 0x80 && _buffer[ 0 ] <= 0xEF )
{
unsigned bytes_read = 2;
switch ( _buffer[ 0 ] & 0xF0 )
{
case 0xC0:
case 0xD0:
bytes_read = 1;
if ( _buffer[ 1 ] == 0x2F ) break;
}
else if ( _buffer[ 0 ] >= 0x80 && _buffer[ 0 ] <= 0xEF )
{
unsigned bytes_read = 2;
switch ( _buffer[ 0 ] & 0xF0 )
{
case 0xC0:
case 0xD0:
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 );
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 ) );
}
}
else return false; /*throw exception_io_data( "Unexpected status code in HMP track" );*/
}
}
offset = is_funky ? 0 : 4;
if ( end - it < (signed long)offset ) return false;
offset = is_funky ? 0 : 4;
if ( end - it < (signed long)offset ) return false;
it = track_end + offset;
p_out.add_track( track );
}
p_out.add_track( track );
}
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 )
{
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 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 );
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 max_buffer = 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 );
it += 4;
fmt_size -= 4;
}
if ( fmt_size >= 4 )
{
if ( !time_format ) // dtx == 0, will cause division by zero on tempo calculations
return false;
}
if ( fmt_size >= 4 )
{
/*max_buffer = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );*/
it += 4;
fmt_size -= 4;
}
if ( fmt_size >= 4 )
{
fmt_size -= 4;
}
if ( fmt_size >= 4 )
{
flags = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4;
fmt_size -= 4;
}
fmt_size -= 4;
}
it += fmt_size;
if ( it == end ) return false;
if ( it == end ) return false;
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" );*/
it += 4;
{
midi_track track;
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
p_out.add_track( track );
}
{
midi_track track;
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_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 );
it += 4;
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 );
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 )
{
if ( end - it < 12 ) return false;
for ( unsigned i = 0; i < segment_count; ++i )
{
if ( end - it < 12 ) return false;
it += 4;
uint32_t segment_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4;
std::vector<uint8_t>::const_iterator segment_end = it + segment_size;
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 );
it += 4;
uint32_t event;
current_timestamp += delta;
current_timestamp += delta;
if ( !is_eight_byte )
{
if ( segment_end - it < 4 ) return false;
it += 4;
}
if ( segment_end - it < 4 ) return false;
{
if ( segment_end - it < 4 ) return false;
it += 4;
}
if ( segment_end - it < 4 ) return false;
event = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
it += 4;
if ( event >> 24 == 0x01 )
{
if ( event >> 24 == 0x01 )
{
uint8_t buffer[ 5 ] = { 0xFF, 0x51 };
buffer[ 2 ] = (uint8_t)( event >> 16 );
buffer[ 3 ] = (uint8_t)( event >> 8 );
buffer[ 4 ] = (uint8_t)event;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, buffer, sizeof( buffer ) ) );
}
else if ( !( event >> 24 ) )
{
unsigned event_code = ( event & 0xF0 ) >> 4;
if ( event_code >= 0x8 && event_code <= 0xE )
{
unsigned bytes_to_write = 1;
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, buffer, sizeof( buffer ) ) );
}
else if ( !( event >> 24 ) )
{
unsigned event_code = ( event & 0xF0 ) >> 4;
if ( event_code >= 0x8 && event_code <= 0xE )
{
unsigned bytes_to_write = 1;
uint8_t buffer[2];
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 );
bytes_to_write = 2;
}
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( event_code - 8 ), event & 0x0F, buffer, bytes_to_write ) );
}
}
}
}
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::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;
}

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 offset = p_file[ 6 ] | ( p_file[ 7 ] << 8 );
p_out.initialize( 0, 0x59 );
p_out.initialize( 0, 0x59 );
{
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, end_of_track, _countof( end_of_track ) ) );
p_out.add_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, end_of_track, _countof( end_of_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 };
if ( (size_t)offset >= p_file.size() || (size_t)(offset + length) > p_file.size() )
return false;
if ( (size_t)offset >= p_file.size() || (size_t)(offset + length) > p_file.size() )
return false;
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset, end = p_file.begin() + offset + length;
uint8_t buffer[ 4 ];
while ( it != end )
{
{
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;
if ( channel == 0x0F ) channel = 9;
else if ( channel >= 9 ) ++channel;
unsigned channel = buffer[ 0 ] & 0x0F;
if ( channel == 0x0F ) channel = 9;
else if ( channel >= 9 ) ++channel;
switch ( buffer[ 0 ] & 0x70 )
{
case 0x00:
type = midi_event::note_on;
if ( it == end ) return false;
switch ( buffer[ 0 ] & 0x70 )
{
case 0x00:
type = midi_event::note_on;
if ( it == end ) return false;
buffer[ 1 ] = *it++;
buffer[ 2 ] = 0;
bytes_to_write = 2;
break;
buffer[ 2 ] = 0;
bytes_to_write = 2;
break;
case 0x10:
type = midi_event::note_on;
if ( it == end ) return false;
case 0x10:
type = midi_event::note_on;
if ( it == end ) return false;
buffer[ 1 ] = *it++;
if ( buffer[ 1 ] & 0x80 )
{
if ( it == end ) return false;
if ( buffer[ 1 ] & 0x80 )
{
if ( it == end ) return false;
buffer[ 2 ] = *it++;
velocity_levels[ channel ] = buffer[ 2 ];
buffer[ 1 ] &= 0x7F;
}
else
{
buffer[ 2 ] = velocity_levels[ channel ];
}
bytes_to_write = 2;
break;
velocity_levels[ channel ] = buffer[ 2 ];
buffer[ 1 ] &= 0x7F;
}
else
{
buffer[ 2 ] = velocity_levels[ channel ];
}
bytes_to_write = 2;
break;
case 0x20:
type = midi_event::pitch_wheel;
if ( it == end ) return false;
case 0x20:
type = midi_event::pitch_wheel;
if ( it == end ) return false;
buffer[ 1 ] = *it++;
buffer[ 2 ] = buffer[ 1 ] >> 1;
buffer[ 1 ] <<= 7;
bytes_to_write = 2;
break;
buffer[ 2 ] = buffer[ 1 ] >> 1;
buffer[ 1 ] <<= 7;
bytes_to_write = 2;
break;
case 0x30:
type = midi_event::control_change;
if ( it == end ) return false;
case 0x30:
type = midi_event::control_change;
if ( it == end ) return false;
buffer[ 1 ] = *it++;
if ( buffer[ 1 ] >= 10 && buffer[ 1 ] <= 14 )
{
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
buffer[ 2 ] = 1;
bytes_to_write = 2;
}
if ( buffer[ 1 ] >= 10 && buffer[ 1 ] <= 14 )
{
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
buffer[ 2 ] = 1;
bytes_to_write = 2;
}
else return false; /*throw exception_io_data( "Unhandled MUS system event" );*/
break;
break;
case 0x40:
if ( it == end ) return false;
case 0x40:
if ( it == end ) return false;
buffer[ 1 ] = *it++;
if ( buffer[ 1 ] )
{
if ( buffer[ 1 ] < 10 )
{
type = midi_event::control_change;
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
if ( it == end ) return false;
if ( buffer[ 1 ] )
{
if ( buffer[ 1 ] < 10 )
{
type = midi_event::control_change;
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
if ( it == end ) return false;
buffer[ 2 ] = *it++;
bytes_to_write = 2;
}
bytes_to_write = 2;
}
else return false; /*throw exception_io_data( "Invalid MUS controller change event" );*/
}
else
{
type = midi_event::program_change;
if ( it == end ) return false;
}
else
{
type = midi_event::program_change;
if ( it == end ) return false;
buffer[ 1 ] = *it++;
bytes_to_write = 1;
}
break;
bytes_to_write = 1;
}
break;
default:
default:
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 );
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;
}

View File

@ -2,15 +2,42 @@
#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 )
{
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 );
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' ||
p_file[ 12 ] != 'd' || p_file[ 13 ] != 'a' || p_file[ 14 ] != 't' || p_file[ 15 ] != 'a' ) return false;
uint32_t data_size = p_file[ 16 ] | ( p_file[ 17 ] << 8 ) | ( p_file[ 18 ] << 16 ) | ( p_file[ 19 ] << 24 );
if ( memcmp( &p_file[ 8 ], "RMID", 4 ) != 0 ||
memcmp( &p_file[ 12 ], "data", 4 ) != 0 ) return false;
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;
std::vector<uint8_t> test;
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 )
{
track_count = 0;
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 body_end = p_file.begin() + 8 + file_size;
std::vector<uint8_t> extra_buffer;
while ( it != body_end )
{
if ( body_end - it < 8 ) return false;
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 ( it[ 0 ] == 'd' && it[ 1 ] == 'a' && it[ 2 ] == 't' && it[ 3 ] == 'a' )
if ( it_equal(it, "data", 4) )
{
std::vector<uint8_t> midi_file;
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;
}
}
return false;
}
static const char * riff_tag_mappings[][2] =
static const char * riff_tag_mappings[][2] =
{
{ "IALB", "album" },
{ "IARL", "archival_location" },
{ "IART", "artist" },
{ "ITRK", "tracknumber" },
{ "ICMS", "commissioned" },
{ "ICMP", "composer" },
{ "ICMT", "comment" },
{ "ICOP", "copyright" },
{ "ICRD", "creation_date" },
{ "IENG", "engineer" },
{ "IGNR", "genre" },
{ "IKEY", "keywords" },
{ "IMED", "medium" },
{ "INAM", "title" },
{ "IPRD", "product" },
{ "ISBJ", "subject" },
{ "ISFT", "software" },
{ "ISRC", "source" },
{ "ISRF", "source_form" },
{ "ITCH", "technician" }
{ "IART", "artist" },
{ "ITRK", "tracknumber" },
{ "ICMS", "commissioned" },
{ "ICMP", "composer" },
{ "ICMT", "comment" },
{ "ICOP", "copyright" },
{ "ICRD", "creation_date" },
{ "IENG", "engineer" },
{ "IGNR", "genre" },
{ "IKEY", "keywords" },
{ "IMED", "medium" },
{ "INAM", "title" },
{ "IPRD", "product" },
{ "ISBJ", "subject" },
{ "ISFT", "software" },
{ "ISRC", "source" },
{ "ISRF", "source_form" },
{ "ITCH", "technician" }
};
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;
bool found_data = false;
bool found_info = false;
bool found_data = false;
bool found_info = false;
midi_meta_data meta_data;
midi_meta_data meta_data;
std::vector<uint8_t> extra_buffer;
while ( it != body_end )
{
if ( body_end - it < 8 ) return false;
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 ( it[ 0 ] == 'd' && it[ 1 ] == 'a' && it[ 2 ] == 't' && it[ 3 ] == 'a' )
{
if ( !found_data )
{
{
if ( body_end - it < 8 ) return false;
uint32_t chunk_size = toInt32LE( it + 4 );
if ( (unsigned long)(body_end - it) < chunk_size ) return false;
if ( it_equal( it, "data", 4 ) )
{
if ( !found_data )
{
std::vector<uint8_t> midi_file;
midi_file.assign( it + 8, it + 8 + chunk_size );
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" );*/
it += 8 + chunk_size;
if ( chunk_size & 1 && it != body_end ) ++it;
}
else if ( it[ 0 ] == 'D' && it[ 1 ] == 'I' && it[ 2 ] == 'S' && it[ 3 ] == 'P' )
{
uint32_t type = it[ 8 ] | ( it[ 9 ] << 8 ) | ( it[ 10 ] << 16 ) | ( it[ 11 ] << 24 );
if ( type == 1 )
{
extra_buffer.resize( chunk_size - 4 );
}
else if ( it_equal( it, "DISP", 4 ) )
{
uint32_t type = toInt32LE( it + 8 );
if ( type == 1 )
{
extra_buffer.resize( chunk_size - 4 + 1 );
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] ) );
}
}
it += 8 + chunk_size;
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;
if ( it[ 8 ] == 'I' && it[ 9 ] == 'N' && it[ 10 ] == 'F' && it[ 11 ] == 'O' )
{
if ( !found_info )
{
if ( chunk_end - it < 12 ) return false;
if ( it_equal( it + 8, "INFO", 4 ) )
{
if ( !found_info )
{
if ( chunk_end - it < 12 ) return false;
it += 12;
while ( it != chunk_end )
{
if ( chunk_end - it < 4 ) return false;
uint32_t field_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
if ( (unsigned long)(chunk_end - it) < 8 + field_size ) return false;
{
if ( chunk_end - it < 4 ) return false;
uint32_t field_size = toInt32LE( it + 4 );
if ( (unsigned long)(chunk_end - it) < 8 + field_size ) return false;
std::string field;
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 ) )
{
field = riff_tag_mappings[ i ][ 1 ];
break;
}
}
extra_buffer.resize( field_size );
{
field = riff_tag_mappings[ i ][ 1 ];
break;
}
}
extra_buffer.resize( field_size + 1 );
std::copy( it + 8, it + 8 + field_size, extra_buffer.begin() );
extra_buffer[ field_size ] = '\0';
it += 8 + field_size;
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;
}
found_info = true;
}
}
found_info = true;
}
else return false; /*throw exception_io_data( "Multiple RIFF LIST INFO chunks found" );*/
}
}
else return false; /* unknown LIST chunk */
it = chunk_end;
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 ( 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;
}

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[ 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[ 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;
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 )
{
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[ 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;
uint16_t form = ( it[0] << 8 ) | it[1];
if ( form > 2 ) return false;
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
if ( form == 2 ) track_count = track_count_16;
else track_count = 1;
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 )
{
midi_track track;
unsigned current_timestamp = 0;
unsigned char last_event_code = 0xFF;
midi_track track;
unsigned current_timestamp = 0;
unsigned char last_event_code = 0xFF;
unsigned last_sysex_length = 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 )
{
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_event_code = event_code;
if ( !needs_end_marker && ( event_code & 0xF0 ) == 0xE0 ) continue;
if ( data_bytes_read < 1 )
{
if ( it == end ) return false;
if ( it == end ) return false;
buffer[ 0 ] = *it++;
++data_bytes_read;
}
@ -85,7 +87,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
case 0xD0:
break;
default:
if ( it == end ) return false;
if ( it == end ) return false;
buffer[ data_bytes_read ] = *it++;
++data_bytes_read;
}
@ -95,13 +97,13 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
{
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;
}
int data_count = decode_delta( it, end );
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[ 0 ] = 0xF0;
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 );
if ( data_count < 0 ) 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 );
it += 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 )
{
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;
}
@ -132,7 +134,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
unsigned char meta_type = *it++;
int data_count = decode_delta( it, end );
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[ 0 ] = 0xFF;
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 )
{
/* Sequencer specific events, single byte */
buffer[ 0 ] = event_code;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 1 ) );
/* Sequencer specific events, single byte */
buffer[ 0 ] = event_code;
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");*/
}
if ( !needs_end_marker )
{
buffer[ 0 ] = 0xFF;
buffer[ 1 ] = 0x2F;
{
buffer[ 0 ] = 0xFF;
buffer[ 1 ] = 0x2F;
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;
}
@ -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");*/
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];
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 dtx = ( it[4] << 8 ) | it[5];
if ( !track_count_16 || !dtx )
return false;
it += 6;
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 )
{
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;
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;
@ -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;
track_data_offset += track_size;
track_data_offset += track_size;
if ( it - p_file.begin() != track_data_offset )
{
{
it = p_file.begin() + track_data_offset;
}
}
}
}
return true;
}

View File

@ -1,35 +1,35 @@
#include "midi_processor.h"
bool midi_processor::is_syx( std::vector<uint8_t> const& p_file )
{
if ( p_file.size() < 2 ) return false;
if ( p_file[ 0 ] != 0xF0 || p_file[ p_file.size() - 1 ] != 0xF7 ) return false;
return true;
}
bool midi_processor::process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out )
{
const size_t size = p_file.size();
size_t ptr = 0;
p_out.initialize( 0, 1 );
midi_track track;
while ( ptr < size )
{
size_t msg_length = 1;
if ( p_file[ptr] != 0xF0 ) return false;
while ( p_file[ptr + msg_length++] != 0xF7 );
track.add_event( midi_event( 0, midi_event::extended, 0, &p_file[ptr], msg_length ) );
ptr += msg_length;
}
p_out.add_track( track );
return true;
}
#include "midi_processor.h"
bool midi_processor::is_syx( std::vector<uint8_t> const& p_file )
{
if ( p_file.size() < 2 ) return false;
if ( p_file[ 0 ] != 0xF0 || p_file[ p_file.size() - 1 ] != 0xF7 ) return false;
return true;
}
bool midi_processor::process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out )
{
const size_t size = p_file.size();
size_t ptr = 0;
p_out.initialize( 0, 1 );
midi_track track;
while ( ptr < size )
{
size_t msg_length = 1;
if ( p_file[ptr] != 0xF0 ) return false;
while ( p_file[ptr + msg_length++] != 0xF7 );
track.add_event( midi_event( 0, midi_event::extended, 0, &p_file[ptr], msg_length ) );
ptr += msg_length;
}
p_out.add_track( track );
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 delta = 0;
if ( it == end ) return 0;
unsigned delta = 0;
if ( it == end ) return 0;
uint8_t byte = *it++;
if ( !( byte & 0x80 ) )
{
do
{
delta += byte;
if ( it == end ) break;
if ( !( byte & 0x80 ) )
{
do
{
delta += byte;
if ( it == end ) break;
byte = *it++;
}
}
while ( !( byte & 0x80 ) && it != end );
}
}
--it;
return delta;
return delta;
}
struct iff_chunk
@ -39,46 +39,46 @@ struct iff_chunk
std::vector<uint8_t> m_data;
std::vector<iff_chunk> m_sub_chunks;
iff_chunk()
{
memset( m_id, 0, sizeof( m_id ) );
memset( m_type, 0, sizeof( m_type ) );
}
iff_chunk()
{
memset( m_id, 0, sizeof( m_id ) );
memset( m_type, 0, sizeof( m_type ) );
}
iff_chunk( const iff_chunk & p_in )
{
memcpy( m_id, p_in.m_id, sizeof( m_id ) );
memcpy( m_type, p_in.m_type, sizeof( m_type ) );
m_data = p_in.m_data;
m_sub_chunks = p_in.m_sub_chunks;
}
iff_chunk( const iff_chunk & p_in )
{
memcpy( m_id, p_in.m_id, sizeof( m_id ) );
memcpy( m_type, p_in.m_type, sizeof( m_type ) );
m_data = p_in.m_data;
m_sub_chunks = p_in.m_sub_chunks;
}
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;
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 ) )
{
++chunk_count;
}
}
return chunk_count;
}
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 )
{
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
{
++chunk_count;
}
}
return chunk_count;
}
};
struct iff_stream
@ -87,69 +87,69 @@ struct iff_stream
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 )
{
if ( !memcmp( p_id, m_chunks[ i ].m_id, 4 ) )
{
return m_chunks[ i ];
}
}
{
if ( !memcmp( p_id, m_chunks[ i ].m_id, 4 ) )
{
return m_chunks[ i ];
}
}
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
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 )
{
if ( end - it < 8 ) return false;
if ( end - it < 8 ) return false;
std::copy( it, it + 4, p_out.m_id );
it += 4;
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;
bool is_cat_chunk = !memcmp( p_out.m_id, "CAT ", 4 );
bool is_form_chunk = !memcmp( p_out.m_id, "FORM", 4 );
bool is_cat_chunk = !memcmp( p_out.m_id, "CAT ", 4 );
bool is_form_chunk = !memcmp( p_out.m_id, "FORM", 4 );
std::size_t chunk_size_limit = end - it;
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 ( end - it < 4 ) return false;
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 ( end - it < 4 ) return false;
std::vector<uint8_t>::const_iterator chunk_end = it + chunk_size;
std::copy( it, it + 4, p_out.m_type );
it += 4;
while ( it < chunk_end )
{
iff_chunk chunk;
{
iff_chunk chunk;
if ( !read_iff_chunk( it, chunk_end, chunk, is_cat_chunk ) ) return false;
p_out.m_sub_chunks.push_back( chunk );
}
}
it = chunk_end;
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 );
it += chunk_size;
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" );
else throw exception_io_data( "Found multiple FORM chunks" );*/
return false;
}
}
return true;
}
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();
bool first_chunk = true;
bool first_chunk = true;
while ( it != end )
{
iff_chunk chunk;
{
iff_chunk chunk;
if ( read_iff_chunk( it, end, chunk, first_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;
else
break;
}
}
return true;
}
bool midi_processor::process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
{
track_count = 0;
iff_stream xmi_file;
if ( !read_iff_stream( p_file, xmi_file ) ) return false;
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" );*/
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" );*/
unsigned _track_count = cat_chunk.get_chunk_count( "FORM" );
track_count = _track_count;
return true;
}
@ -188,32 +188,32 @@ bool midi_processor::process_xmi( std::vector<uint8_t> const& p_file, midi_conta
iff_stream xmi_file;
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" );*/
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" );*/
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 )
{
const iff_chunk & xmid_form_chunk = cat_chunk.find_sub_chunk( "FORM", i );
for ( unsigned i = 0; i < track_count; ++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" );*/
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 */
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;
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();
while ( it != end )
{
{
unsigned delta = decode_xmi_delta( it, end );
current_timestamp += delta;
current_timestamp += delta;
if ( current_timestamp > last_event_timestamp )
{
last_event_timestamp = current_timestamp;
}
if ( current_timestamp > last_event_timestamp )
{
last_event_timestamp = current_timestamp;
}
if ( it == end ) return false;
if ( it == end ) return false;
buffer[ 0 ] = *it++;
if ( buffer[ 0 ] == 0xFF )
{
if ( it == end ) return false;
if ( buffer[ 0 ] == 0xFF )
{
if ( it == end ) return false;
buffer[ 1 ] = *it++;
long meta_count;
if ( buffer[ 1 ] == 0x2F )
{
meta_count = 0;
}
else
{
long meta_count;
if ( buffer[ 1 ] == 0x2F )
{
meta_count = 0;
}
else
{
meta_count = decode_delta( it, end );
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 );
std::copy( it, it + meta_count, buffer.begin() + 2 );
it += meta_count;
}
if ( buffer[ 1 ] == 0x2F && current_timestamp < last_event_timestamp )
{
current_timestamp = last_event_timestamp;
}
if ( buffer[ 1 ] == 0x51 && meta_count == 3 )
{
unsigned tempo = buffer[ 2 ] * 0x10000 + buffer[ 3 ] * 0x100 + buffer[ 4 ];
unsigned ppqn = ( tempo * 3 ) / 25000;
tempo = tempo * 60 / ppqn;
buffer[ 2 ] = tempo / 0x10000;
buffer[ 3 ] = tempo / 0x100;
buffer[ 4 ] = tempo;
if ( current_timestamp == 0 ) initial_tempo = true;
}
}
if ( buffer[ 1 ] == 0x2F && current_timestamp < last_event_timestamp )
{
current_timestamp = last_event_timestamp;
}
if ( buffer[ 1 ] == 0x51 && meta_count == 3 )
{
unsigned tempo = buffer[ 2 ] * 0x10000 + buffer[ 3 ] * 0x100 + buffer[ 4 ];
unsigned ppqn = ( tempo * 3 ) / 25000;
tempo = tempo * 60 / ppqn;
buffer[ 2 ] = tempo / 0x10000;
buffer[ 3 ] = tempo / 0x100;
buffer[ 4 ] = tempo;
if ( current_timestamp == 0 ) initial_tempo = true;
}
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
if ( buffer[ 1 ] == 0x2F ) break;
}
else if ( buffer[ 0 ] == 0xF0 )
{
if ( buffer[ 1 ] == 0x2F ) break;
}
else if ( buffer[ 0 ] == 0xF0 )
{
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 ( end - it < system_exclusive_count ) return false;
if ( end - it < system_exclusive_count ) return false;
buffer.resize( system_exclusive_count + 1 );
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
it += system_exclusive_count;
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 )
{
unsigned bytes_read = 1;
if ( it == end ) return false;
}
else if ( buffer[ 0 ] >= 0x80 && buffer[ 0 ] <= 0xEF )
{
unsigned bytes_read = 1;
if ( it == end ) return false;
buffer[ 1 ] = *it++;
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
unsigned channel = buffer[ 0 ] & 0x0F;
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
{
if ( it == end ) return false;
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
unsigned channel = buffer[ 0 ] & 0x0F;
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
{
if ( it == end ) return false;
buffer[ 2 ] = *it++;
bytes_read = 2;
}
bytes_read = 2;
}
track.add_event( midi_event( current_timestamp, type, channel, &buffer[1], bytes_read ) );
if ( type == midi_event::note_on )
{
buffer[ 2 ] = 0x00;
if ( type == midi_event::note_on )
{
buffer[ 2 ] = 0x00;
int note_length = decode_delta( it, end );
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid XMI note message" );*/
unsigned note_end_timestamp = current_timestamp + note_length;
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
unsigned note_end_timestamp = current_timestamp + note_length;
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 ) );
}
}
}
}
else return false; /*throw exception_io_data( "Unexpected XMI status code" );*/
}
}
if ( !initial_tempo )
track.add_event( midi_event( 0, midi_event::extended, 0, xmi_default_tempo, _countof( xmi_default_tempo ) ) );
if ( !initial_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;
}

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.
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 );