Update midi_processing to latest version, fixing some severe MIDI file handling issues, including Standard MIDI file SysEx and SysEx continuation handling
This commit is contained in:
parent
798cc4ce43
commit
f5c7c4d49a
15 changed files with 2393 additions and 2105 deletions
File diff suppressed because it is too large
Load diff
|
@ -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 );
|
||||
};
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
Loading…
Reference in a new issue