Update midi_processing to latest version, fixing some severe MIDI file handling issues, including Standard MIDI file SysEx and SysEx continuation handling
parent
798cc4ce43
commit
f5c7c4d49a
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 New Issue