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
|
struct midi_event
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
max_static_data_count = 16
|
max_static_data_count = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
enum event_type
|
enum event_type
|
||||||
{
|
{
|
||||||
note_off = 0,
|
note_off = 0,
|
||||||
note_on,
|
note_on,
|
||||||
polyphonic_aftertouch,
|
polyphonic_aftertouch,
|
||||||
control_change,
|
control_change,
|
||||||
program_change,
|
program_change,
|
||||||
channel_aftertouch,
|
channel_aftertouch,
|
||||||
pitch_wheel,
|
pitch_wheel,
|
||||||
extended
|
extended
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned long m_timestamp;
|
unsigned long m_timestamp;
|
||||||
|
|
||||||
event_type m_type;
|
event_type m_type;
|
||||||
unsigned m_channel;
|
unsigned m_channel;
|
||||||
unsigned long m_data_count;
|
unsigned long m_data_count;
|
||||||
uint8_t m_data[max_static_data_count];
|
uint8_t m_data[max_static_data_count];
|
||||||
std::vector<uint8_t> m_ext_data;
|
std::vector<uint8_t> m_ext_data;
|
||||||
|
|
||||||
midi_event() : m_timestamp(0), m_type(note_off), m_channel(0), m_data_count(0) { }
|
midi_event() : m_timestamp(0), m_type(note_off), m_channel(0), m_data_count(0) { }
|
||||||
midi_event( const midi_event & p_in );
|
midi_event( const midi_event & p_in );
|
||||||
midi_event( unsigned long p_timestamp, event_type p_type, unsigned p_channel, const uint8_t * p_data, std::size_t p_data_count );
|
midi_event( unsigned long p_timestamp, event_type p_type, unsigned p_channel, const uint8_t * p_data, std::size_t p_data_count );
|
||||||
|
|
||||||
unsigned long get_data_count() const;
|
unsigned long get_data_count() const;
|
||||||
void copy_data( uint8_t * p_out, unsigned long p_offset, unsigned long p_count ) const;
|
void copy_data( uint8_t * p_out, unsigned long p_offset, unsigned long p_count ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,23 +51,24 @@ class midi_track
|
||||||
std::vector<midi_event> m_events;
|
std::vector<midi_event> m_events;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
midi_track() { }
|
midi_track() { }
|
||||||
midi_track(const midi_track & p_in);
|
midi_track(const midi_track & p_in);
|
||||||
|
|
||||||
void add_event( const midi_event & p_event );
|
void add_event( const midi_event & p_event );
|
||||||
std::size_t get_count() const;
|
std::size_t get_count() const;
|
||||||
const midi_event & operator [] ( std::size_t p_index ) const;
|
const midi_event & operator [] ( std::size_t p_index ) const;
|
||||||
|
midi_event & operator [] ( std::size_t p_index );
|
||||||
|
|
||||||
void remove_event( unsigned long index );
|
void remove_event( unsigned long index );
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tempo_entry
|
struct tempo_entry
|
||||||
{
|
{
|
||||||
unsigned long m_timestamp;
|
unsigned long m_timestamp;
|
||||||
unsigned m_tempo;
|
unsigned m_tempo;
|
||||||
|
|
||||||
tempo_entry() : m_timestamp(0), m_tempo(0) { }
|
tempo_entry() : m_timestamp(0), m_tempo(0) { }
|
||||||
tempo_entry(unsigned long p_timestamp, unsigned p_tempo);
|
tempo_entry(unsigned long p_timestamp, unsigned p_tempo);
|
||||||
};
|
};
|
||||||
|
|
||||||
class tempo_map
|
class tempo_map
|
||||||
|
@ -75,11 +76,12 @@ class tempo_map
|
||||||
std::vector<tempo_entry> m_entries;
|
std::vector<tempo_entry> m_entries;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void add_tempo( unsigned p_tempo, unsigned long p_timestamp );
|
void add_tempo( unsigned p_tempo, unsigned long p_timestamp );
|
||||||
unsigned long timestamp_to_ms( unsigned long p_timestamp, unsigned p_dtx ) const;
|
unsigned long timestamp_to_ms( unsigned long p_timestamp, unsigned p_dtx ) const;
|
||||||
|
|
||||||
std::size_t get_count() const;
|
std::size_t get_count() const;
|
||||||
const tempo_entry & operator [] ( std::size_t p_index ) const;
|
const tempo_entry & operator [] ( std::size_t p_index ) const;
|
||||||
|
tempo_entry & operator [] ( std::size_t p_index );
|
||||||
};
|
};
|
||||||
|
|
||||||
struct system_exclusive_entry
|
struct system_exclusive_entry
|
||||||
|
@ -87,8 +89,8 @@ struct system_exclusive_entry
|
||||||
std::size_t m_port;
|
std::size_t m_port;
|
||||||
std::size_t m_offset;
|
std::size_t m_offset;
|
||||||
std::size_t m_length;
|
std::size_t m_length;
|
||||||
system_exclusive_entry() : m_port(0), m_offset(0), m_length(0) { }
|
system_exclusive_entry() : m_port(0), m_offset(0), m_length(0) { }
|
||||||
system_exclusive_entry(const system_exclusive_entry & p_in);
|
system_exclusive_entry(const system_exclusive_entry & p_in);
|
||||||
system_exclusive_entry(std::size_t p_port, std::size_t p_offset, std::size_t p_length);
|
system_exclusive_entry(std::size_t p_port, std::size_t p_offset, std::size_t p_length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,42 +106,42 @@ public:
|
||||||
|
|
||||||
struct midi_stream_event
|
struct midi_stream_event
|
||||||
{
|
{
|
||||||
unsigned long m_timestamp;
|
unsigned long m_timestamp;
|
||||||
uint32_t m_event;
|
uint32_t m_event;
|
||||||
|
|
||||||
midi_stream_event() : m_timestamp(0), m_event(0) { }
|
midi_stream_event() : m_timestamp(0), m_event(0) { }
|
||||||
midi_stream_event(unsigned long p_timestamp, uint32_t p_event);
|
midi_stream_event(unsigned long p_timestamp, uint32_t p_event);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct midi_meta_data_item
|
struct midi_meta_data_item
|
||||||
{
|
{
|
||||||
unsigned long m_timestamp;
|
unsigned long m_timestamp;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_value;
|
std::string m_value;
|
||||||
|
|
||||||
midi_meta_data_item() : m_timestamp(0) { }
|
midi_meta_data_item() : m_timestamp(0) { }
|
||||||
midi_meta_data_item(const midi_meta_data_item & p_in);
|
midi_meta_data_item(const midi_meta_data_item & p_in);
|
||||||
midi_meta_data_item(unsigned long p_timestamp, const char * p_name, const char * p_value);
|
midi_meta_data_item(unsigned long p_timestamp, const char * p_name, const char * p_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
class midi_meta_data
|
class midi_meta_data
|
||||||
{
|
{
|
||||||
std::vector<midi_meta_data_item> m_data;
|
std::vector<midi_meta_data_item> m_data;
|
||||||
std::vector<uint8_t> m_bitmap;
|
std::vector<uint8_t> m_bitmap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
midi_meta_data() { }
|
midi_meta_data() { }
|
||||||
|
|
||||||
void add_item( const midi_meta_data_item & p_item );
|
void add_item( const midi_meta_data_item & p_item );
|
||||||
|
|
||||||
void append( const midi_meta_data & p_data );
|
void append( const midi_meta_data & p_data );
|
||||||
|
|
||||||
bool get_item( const char * p_name, midi_meta_data_item & p_out ) const;
|
bool get_item( const char * p_name, midi_meta_data_item & p_out ) const;
|
||||||
|
|
||||||
bool get_bitmap( std::vector<uint8_t> & p_out );
|
bool get_bitmap( std::vector<uint8_t> & p_out );
|
||||||
|
|
||||||
void assign_bitmap( std::vector<uint8_t>::const_iterator const& begin, std::vector<uint8_t>::const_iterator const& end );
|
void assign_bitmap( std::vector<uint8_t>::const_iterator const& begin, std::vector<uint8_t>::const_iterator const& end );
|
||||||
|
|
||||||
std::size_t get_count() const;
|
std::size_t get_count() const;
|
||||||
|
|
||||||
const midi_meta_data_item & operator [] ( std::size_t p_index ) const;
|
const midi_meta_data_item & operator [] ( std::size_t p_index ) const;
|
||||||
|
@ -148,16 +150,16 @@ public:
|
||||||
class midi_container
|
class midi_container
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
clean_flag_emidi = 1 << 0,
|
clean_flag_emidi = 1 << 0,
|
||||||
clean_flag_instruments = 1 << 1,
|
clean_flag_instruments = 1 << 1,
|
||||||
clean_flag_banks = 1 << 2,
|
clean_flag_banks = 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned m_form;
|
unsigned m_form;
|
||||||
unsigned m_dtx;
|
unsigned m_dtx;
|
||||||
std::vector<uint64_t> m_channel_mask;
|
std::vector<uint64_t> m_channel_mask;
|
||||||
std::vector<tempo_map> m_tempo_map;
|
std::vector<tempo_map> m_tempo_map;
|
||||||
std::vector<midi_track> m_tracks;
|
std::vector<midi_track> m_tracks;
|
||||||
|
@ -166,7 +168,7 @@ private:
|
||||||
|
|
||||||
std::vector< std::vector< std::string > > m_device_names;
|
std::vector< std::vector< std::string > > m_device_names;
|
||||||
|
|
||||||
midi_meta_data m_extra_meta_data;
|
midi_meta_data m_extra_meta_data;
|
||||||
|
|
||||||
std::vector<unsigned long> m_timestamp_end;
|
std::vector<unsigned long> m_timestamp_end;
|
||||||
|
|
||||||
|
@ -207,9 +209,9 @@ private:
|
||||||
public:
|
public:
|
||||||
midi_container() { m_device_names.resize( 16 ); }
|
midi_container() { m_device_names.resize( 16 ); }
|
||||||
|
|
||||||
void initialize( unsigned p_form, unsigned p_dtx );
|
void initialize( unsigned p_form, unsigned p_dtx );
|
||||||
|
|
||||||
void add_track( const midi_track & p_track );
|
void add_track( const midi_track & p_track );
|
||||||
|
|
||||||
void add_track_event( std::size_t p_track_index, const midi_event & p_event );
|
void add_track_event( std::size_t p_track_index, const midi_event & p_event );
|
||||||
|
|
||||||
|
@ -219,7 +221,7 @@ public:
|
||||||
void merge_tracks( const midi_container & p_source );
|
void merge_tracks( const midi_container & p_source );
|
||||||
void set_track_count( unsigned count );
|
void set_track_count( unsigned count );
|
||||||
void set_extra_meta_data( const midi_meta_data & p_data );
|
void set_extra_meta_data( const midi_meta_data & p_data );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Blah.
|
* Blah.
|
||||||
* Hack 0: Remove channel 16
|
* Hack 0: Remove channel 16
|
||||||
|
@ -233,6 +235,17 @@ public:
|
||||||
|
|
||||||
void promote_to_type1();
|
void promote_to_type1();
|
||||||
|
|
||||||
|
void trim_start();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void trim_range_of_tracks(unsigned long start, unsigned long end);
|
||||||
|
void trim_tempo_map(unsigned long p_index, unsigned long base_timestamp);
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef std::string(*split_callback)(uint8_t bank_msb, uint8_t bank_lsb, uint8_t instrument);
|
||||||
|
|
||||||
|
void split_by_instrument_changes(split_callback cb = NULL);
|
||||||
|
|
||||||
unsigned long get_subsong_count() const;
|
unsigned long get_subsong_count() const;
|
||||||
unsigned long get_subsong( unsigned long p_index ) const;
|
unsigned long get_subsong( unsigned long p_index ) const;
|
||||||
|
|
||||||
|
@ -245,9 +258,9 @@ public:
|
||||||
unsigned long get_timestamp_loop_start(unsigned long subsong, bool ms = false) const;
|
unsigned long get_timestamp_loop_start(unsigned long subsong, bool ms = false) const;
|
||||||
unsigned long get_timestamp_loop_end(unsigned long subsong, bool ms = false) const;
|
unsigned long get_timestamp_loop_end(unsigned long subsong, bool ms = false) const;
|
||||||
|
|
||||||
void get_meta_data( unsigned long subsong, midi_meta_data & p_out );
|
void get_meta_data( unsigned long subsong, midi_meta_data & p_out );
|
||||||
|
|
||||||
void scan_for_loops( bool p_xmi_loops, bool p_marker_loops, bool p_rpgmaker_loops );
|
void scan_for_loops( bool p_xmi_loops, bool p_marker_loops, bool p_rpgmaker_loops, bool p_touhou_loops );
|
||||||
|
|
||||||
static void encode_delta( std::vector<uint8_t> & p_out, unsigned long delta );
|
static void encode_delta( std::vector<uint8_t> & p_out, unsigned long delta );
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,14 +51,14 @@ class midi_processor
|
||||||
static bool process_lds( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
static bool process_lds( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
||||||
static bool process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
static bool process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
||||||
static bool process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
static bool process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
||||||
|
|
||||||
static bool process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
|
static bool process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
|
||||||
static bool process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
|
static bool process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
|
||||||
static bool process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
|
static bool process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count );
|
static bool process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count );
|
||||||
|
|
||||||
static bool process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out );
|
static bool process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out );
|
||||||
|
|
||||||
static bool process_syx_file( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
static bool process_syx_file( std::vector<uint8_t> const& p_file, midi_container & p_out );
|
||||||
|
|
|
@ -4,47 +4,47 @@ bool midi_processor::is_gmf( std::vector<uint8_t> const& p_file )
|
||||||
{
|
{
|
||||||
if ( p_file.size() < 32 ) return false;
|
if ( p_file.size() < 32 ) return false;
|
||||||
if ( p_file[ 0 ] != 'G' || p_file[ 1 ] != 'M' || p_file[ 2 ] != 'F' || p_file[ 3 ] != 1 ) return false;
|
if ( p_file[ 0 ] != 'G' || p_file[ 1 ] != 'M' || p_file[ 2 ] != 'F' || p_file[ 3 ] != 1 ) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi_processor::process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
bool midi_processor::process_gmf( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
||||||
{
|
{
|
||||||
uint8_t buffer[10];
|
uint8_t buffer[10];
|
||||||
|
|
||||||
p_out.initialize( 0, 0xC0 );
|
p_out.initialize( 0, 0xC0 );
|
||||||
|
|
||||||
uint16_t tempo = ( p_file[ 4 ] << 8 ) | p_file[ 5 ];
|
uint16_t tempo = ( p_file[ 4 ] << 8 ) | p_file[ 5 ];
|
||||||
uint32_t tempo_scaled = tempo * 100000;
|
uint32_t tempo_scaled = tempo * 100000;
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
|
|
||||||
buffer[0] = 0xFF;
|
buffer[0] = 0xFF;
|
||||||
buffer[1] = 0x51;
|
buffer[1] = 0x51;
|
||||||
buffer[2] = tempo_scaled >> 16;
|
buffer[2] = tempo_scaled >> 16;
|
||||||
buffer[3] = tempo_scaled >> 8;
|
buffer[3] = tempo_scaled >> 8;
|
||||||
buffer[4] = tempo_scaled;
|
buffer[4] = tempo_scaled;
|
||||||
|
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 5 ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 5 ) );
|
||||||
|
|
||||||
buffer[0] = 0xF0;
|
buffer[0] = 0xF0;
|
||||||
buffer[1] = 0x41;
|
buffer[1] = 0x41;
|
||||||
buffer[2] = 0x10;
|
buffer[2] = 0x10;
|
||||||
buffer[3] = 0x16;
|
buffer[3] = 0x16;
|
||||||
buffer[4] = 0x12;
|
buffer[4] = 0x12;
|
||||||
buffer[5] = 0x7F;
|
buffer[5] = 0x7F;
|
||||||
buffer[6] = 0x00;
|
buffer[6] = 0x00;
|
||||||
buffer[7] = 0x00;
|
buffer[7] = 0x00;
|
||||||
buffer[8] = 0x01;
|
buffer[8] = 0x01;
|
||||||
buffer[9] = 0xF7;
|
buffer[9] = 0xF7;
|
||||||
|
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 10 ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 10 ) );
|
||||||
|
|
||||||
buffer[0] = 0xFF;
|
buffer[0] = 0xFF;
|
||||||
buffer[1] = 0x2F;
|
buffer[1] = 0x2F;
|
||||||
|
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 2 ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, buffer, 2 ) );
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + 7;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + 7;
|
||||||
|
|
||||||
|
|
|
@ -6,54 +6,54 @@ const uint8_t midi_processor::loop_end[9] = {0xFF, 0x06, 'l', 'o', 'o', 'p',
|
||||||
|
|
||||||
int midi_processor::decode_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
|
int midi_processor::decode_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
|
||||||
{
|
{
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
unsigned char byte;
|
unsigned char byte;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if ( it == end ) return 0;
|
if ( it == end ) return 0;
|
||||||
byte = *it++;
|
byte = *it++;
|
||||||
delta = ( delta << 7 ) + ( byte & 0x7F );
|
delta = ( delta << 7 ) + ( byte & 0x7F );
|
||||||
}
|
}
|
||||||
while ( byte & 0x80 );
|
while ( byte & 0x80 );
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi_processor::process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out )
|
bool midi_processor::process_file( std::vector<uint8_t> const& p_file, const char * p_extension, midi_container & p_out )
|
||||||
{
|
{
|
||||||
if ( is_standard_midi( p_file ) )
|
if ( is_standard_midi( p_file ) )
|
||||||
{
|
{
|
||||||
return process_standard_midi( p_file, p_out );
|
return process_standard_midi( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_riff_midi( p_file ) )
|
else if ( is_riff_midi( p_file ) )
|
||||||
{
|
{
|
||||||
return process_riff_midi( p_file, p_out );
|
return process_riff_midi( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_hmp( p_file ) )
|
else if ( is_hmp( p_file ) )
|
||||||
{
|
{
|
||||||
return process_hmp( p_file, p_out );
|
return process_hmp( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_hmi( p_file ) )
|
else if ( is_hmi( p_file ) )
|
||||||
{
|
{
|
||||||
return process_hmi( p_file, p_out );
|
return process_hmi( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_xmi( p_file ) )
|
else if ( is_xmi( p_file ) )
|
||||||
{
|
{
|
||||||
return process_xmi( p_file, p_out );
|
return process_xmi( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_mus( p_file ) )
|
else if ( is_mus( p_file ) )
|
||||||
{
|
{
|
||||||
return process_mus( p_file, p_out );
|
return process_mus( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_mids( p_file ) )
|
else if ( is_mids( p_file ) )
|
||||||
{
|
{
|
||||||
return process_mids( p_file, p_out );
|
return process_mids( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_lds( p_file, p_extension ) )
|
else if ( is_lds( p_file, p_extension ) )
|
||||||
{
|
{
|
||||||
return process_lds( p_file, p_out );
|
return process_lds( p_file, p_out );
|
||||||
}
|
}
|
||||||
else if ( is_gmf( p_file ) )
|
else if ( is_gmf( p_file ) )
|
||||||
{
|
{
|
||||||
return process_gmf( p_file, p_out );
|
return process_gmf( p_file, p_out );
|
||||||
}
|
}
|
||||||
else return false;
|
else return false;
|
||||||
|
@ -71,7 +71,7 @@ bool midi_processor::process_syx_file( std::vector<uint8_t> const& p_file, midi_
|
||||||
bool midi_processor::process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count )
|
bool midi_processor::process_track_count( std::vector<uint8_t> const& p_file, const char * p_extension, size_t & track_count )
|
||||||
{
|
{
|
||||||
track_count = 0;
|
track_count = 0;
|
||||||
|
|
||||||
if ( is_standard_midi( p_file ) )
|
if ( is_standard_midi( p_file ) )
|
||||||
{
|
{
|
||||||
return process_standard_midi_count( p_file, track_count );
|
return process_standard_midi_count( p_file, track_count );
|
||||||
|
|
|
@ -18,43 +18,43 @@ bool midi_processor::process_hmi( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
uint32_t track_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
uint32_t track_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
uint32_t track_table_offset = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
|
uint32_t track_table_offset = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
|
||||||
|
|
||||||
if ( track_table_offset >= p_file.size() || track_table_offset + track_count * 4 > p_file.size() )
|
if ( track_table_offset >= p_file.size() || track_table_offset + track_count * 4 > p_file.size() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
it = p_file.begin() + track_table_offset;
|
it = p_file.begin() + track_table_offset;
|
||||||
|
|
||||||
std::vector<uint32_t> track_offsets;
|
std::vector<uint32_t> track_offsets;
|
||||||
track_offsets.resize( track_count );
|
track_offsets.resize( track_count );
|
||||||
|
|
||||||
for ( unsigned i = 0; i < track_count; ++i )
|
for ( unsigned i = 0; i < track_count; ++i )
|
||||||
{
|
{
|
||||||
track_offsets[ i ] = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
track_offsets[ i ] = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_out.initialize( 1, 0xC0 );
|
p_out.initialize( 1, 0xC0 );
|
||||||
|
|
||||||
{
|
{
|
||||||
midi_track track;
|
midi_track track;
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) );
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( unsigned i = 0; i < track_count; ++i )
|
for ( unsigned i = 0; i < track_count; ++i )
|
||||||
{
|
{
|
||||||
unsigned track_offset = track_offsets[ i ];
|
unsigned track_offset = track_offsets[ i ];
|
||||||
unsigned long track_length;
|
unsigned long track_length;
|
||||||
if ( i + 1 < track_count )
|
if ( i + 1 < track_count )
|
||||||
{
|
{
|
||||||
track_length = track_offsets[ i + 1 ] - track_offset;
|
track_length = track_offsets[ i + 1 ] - track_offset;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
track_length = p_file.size() - track_offset;
|
track_length = p_file.size() - track_offset;
|
||||||
}
|
}
|
||||||
if ( track_offset >= p_file.size() || track_offset + track_length > p_file.size() )
|
if ( track_offset >= p_file.size() || track_offset + track_length > p_file.size() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator track_body = p_file.begin() + track_offset;
|
std::vector<uint8_t>::const_iterator track_body = p_file.begin() + track_offset;
|
||||||
std::vector<uint8_t>::const_iterator track_end = track_body + track_length;
|
std::vector<uint8_t>::const_iterator track_end = track_body + track_length;
|
||||||
|
@ -65,31 +65,31 @@ bool midi_processor::process_hmi( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
track_body[ 8 ] != 'T' || track_body[ 9 ] != 'R' || track_body[ 10 ] != 'A' || track_body[ 11 ] != 'C' ||
|
track_body[ 8 ] != 'T' || track_body[ 9 ] != 'R' || track_body[ 10 ] != 'A' || track_body[ 11 ] != 'C' ||
|
||||||
track_body[ 12 ] != 'K' ) return false;
|
track_body[ 12 ] != 'K' ) return false;
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
unsigned current_timestamp = 0;
|
unsigned current_timestamp = 0;
|
||||||
unsigned char last_event_code = 0xFF;
|
unsigned char last_event_code = 0xFF;
|
||||||
|
|
||||||
unsigned last_event_timestamp = 0;
|
unsigned last_event_timestamp = 0;
|
||||||
|
|
||||||
if ( track_length < 0x4B + 4 ) return false;
|
if ( track_length < 0x4B + 4 ) return false;
|
||||||
|
|
||||||
uint32_t meta_offset = track_body[ 0x4B ] | ( track_body[ 0x4C ] << 8 ) | ( track_body[ 0x4D ] << 16 ) | ( track_body[ 0x4E ] << 24 );
|
uint32_t meta_offset = track_body[ 0x4B ] | ( track_body[ 0x4C ] << 8 ) | ( track_body[ 0x4D ] << 16 ) | ( track_body[ 0x4E ] << 24 );
|
||||||
if ( meta_offset && meta_offset + 1 < track_length )
|
if ( meta_offset && meta_offset + 1 < track_length )
|
||||||
{
|
{
|
||||||
buffer.resize( 2 );
|
buffer.resize( 2 );
|
||||||
std::copy( track_body + meta_offset, track_body + meta_offset + 2, buffer.begin() );
|
std::copy( track_body + meta_offset, track_body + meta_offset + 2, buffer.begin() );
|
||||||
unsigned meta_size = buffer[ 1 ];
|
unsigned meta_size = buffer[ 1 ];
|
||||||
if ( meta_offset + 2 + meta_size > track_length ) return false;
|
if ( meta_offset + 2 + meta_size > track_length ) return false;
|
||||||
buffer.resize( meta_size + 2 );
|
buffer.resize( meta_size + 2 );
|
||||||
std::copy( track_body + meta_offset + 2, track_body + meta_offset + 2 + meta_size, buffer.begin() + 2 );
|
std::copy( track_body + meta_offset + 2, track_body + meta_offset + 2 + meta_size, buffer.begin() + 2 );
|
||||||
while ( meta_size > 0 && buffer[ meta_size + 1 ] == ' ' ) --meta_size;
|
while ( meta_size > 0 && buffer[ meta_size + 1 ] == ' ' ) --meta_size;
|
||||||
if ( meta_size > 0 )
|
if ( meta_size > 0 )
|
||||||
{
|
{
|
||||||
buffer[ 0 ] = 0xFF;
|
buffer[ 0 ] = 0xFF;
|
||||||
buffer[ 1 ] = 0x01;
|
buffer[ 1 ] = 0x01;
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, &buffer[0], meta_size + 2 ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, &buffer[0], meta_size + 2 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( track_length < 0x57 + 4 ) return false;
|
if ( track_length < 0x57 + 4 ) return false;
|
||||||
|
|
||||||
|
@ -100,129 +100,129 @@ bool midi_processor::process_hmi( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
buffer.resize( 3 );
|
buffer.resize( 3 );
|
||||||
|
|
||||||
while ( it != track_end )
|
while ( it != track_end )
|
||||||
{
|
{
|
||||||
int delta = decode_delta( it, track_end );
|
int delta = decode_delta( it, track_end );
|
||||||
if ( delta > 0xFFFF || delta < 0 )
|
if ( delta > 0xFFFF || delta < 0 )
|
||||||
{
|
{
|
||||||
current_timestamp = last_event_timestamp;
|
current_timestamp = last_event_timestamp;
|
||||||
/*console::formatter() << "[foo_midi] Large HMI delta detected, shunting.";*/
|
/*console::formatter() << "[foo_midi] Large HMI delta detected, shunting.";*/
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
current_timestamp += delta;
|
current_timestamp += delta;
|
||||||
if ( current_timestamp > last_event_timestamp )
|
if ( current_timestamp > last_event_timestamp )
|
||||||
{
|
{
|
||||||
last_event_timestamp = current_timestamp;
|
last_event_timestamp = current_timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
buffer[ 0 ] = *it++;
|
buffer[ 0 ] = *it++;
|
||||||
if ( buffer[ 0 ] == 0xFF )
|
if ( buffer[ 0 ] == 0xFF )
|
||||||
{
|
{
|
||||||
last_event_code = 0xFF;
|
last_event_code = 0xFF;
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
int meta_count = decode_delta( it, track_end );
|
int meta_count = decode_delta( it, track_end );
|
||||||
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI meta message" );*/
|
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI meta message" );*/
|
||||||
if ( track_end - it < meta_count ) return false;
|
if ( track_end - it < meta_count ) return false;
|
||||||
buffer.resize( meta_count + 2 );
|
buffer.resize( meta_count + 2 );
|
||||||
std::copy( it, it + meta_count, buffer.begin() + 2 );
|
std::copy( it, it + meta_count, buffer.begin() + 2 );
|
||||||
it += meta_count;
|
it += meta_count;
|
||||||
if ( buffer[ 1 ] == 0x2F && last_event_timestamp > current_timestamp )
|
if ( buffer[ 1 ] == 0x2F && last_event_timestamp > current_timestamp )
|
||||||
{
|
{
|
||||||
current_timestamp = last_event_timestamp;
|
current_timestamp = last_event_timestamp;
|
||||||
}
|
}
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
|
||||||
if ( buffer[ 1 ] == 0x2F ) break;
|
if ( buffer[ 1 ] == 0x2F ) break;
|
||||||
}
|
}
|
||||||
else if ( buffer[ 0 ] == 0xF0 )
|
else if ( buffer[ 0 ] == 0xF0 )
|
||||||
{
|
{
|
||||||
last_event_code = 0xFF;
|
last_event_code = 0xFF;
|
||||||
int system_exclusive_count = decode_delta( it, track_end );
|
int system_exclusive_count = decode_delta( it, track_end );
|
||||||
if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI System Exclusive message" );*/
|
if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid HMI System Exclusive message" );*/
|
||||||
if ( track_end - it < system_exclusive_count ) return false;
|
if ( track_end - it < system_exclusive_count ) return false;
|
||||||
buffer.resize( system_exclusive_count + 1 );
|
buffer.resize( system_exclusive_count + 1 );
|
||||||
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
|
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
|
||||||
it += system_exclusive_count;
|
it += system_exclusive_count;
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
|
||||||
}
|
}
|
||||||
else if ( buffer[ 0 ] == 0xFE )
|
else if ( buffer[ 0 ] == 0xFE )
|
||||||
{
|
{
|
||||||
last_event_code = 0xFF;
|
last_event_code = 0xFF;
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
if ( buffer[ 1 ] == 0x10 )
|
if ( buffer[ 1 ] == 0x10 )
|
||||||
{
|
{
|
||||||
if ( track_end - it < 3 ) return false;
|
if ( track_end - it < 3 ) return false;
|
||||||
it += 2;
|
it += 2;
|
||||||
buffer[ 2 ] = *it++;
|
buffer[ 2 ] = *it++;
|
||||||
if ( track_end - it < buffer[ 2 ] + 4 ) return false;
|
if ( track_end - it < buffer[ 2 ] + 4 ) return false;
|
||||||
it += buffer[ 2 ] + 4;
|
it += buffer[ 2 ] + 4;
|
||||||
}
|
}
|
||||||
else if ( buffer[ 1 ] == 0x12 )
|
else if ( buffer[ 1 ] == 0x12 )
|
||||||
{
|
{
|
||||||
if ( track_end - it < 2 ) return false;
|
if ( track_end - it < 2 ) return false;
|
||||||
it += 2;
|
it += 2;
|
||||||
}
|
}
|
||||||
else if ( buffer[ 1 ] == 0x13 )
|
else if ( buffer[ 1 ] == 0x13 )
|
||||||
{
|
{
|
||||||
if ( track_end - it < 10 ) return false;
|
if ( track_end - it < 10 ) return false;
|
||||||
it += 10;
|
it += 10;
|
||||||
}
|
}
|
||||||
else if ( buffer[ 1 ] == 0x14 )
|
else if ( buffer[ 1 ] == 0x14 )
|
||||||
{
|
{
|
||||||
if ( track_end - it < 2 ) return false;
|
if ( track_end - it < 2 ) return false;
|
||||||
it += 2;
|
it += 2;
|
||||||
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_start, _countof( loop_start ) ) );
|
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_start, _countof( loop_start ) ) );
|
||||||
}
|
}
|
||||||
else if ( buffer[ 1 ] == 0x15 )
|
else if ( buffer[ 1 ] == 0x15 )
|
||||||
{
|
{
|
||||||
if ( track_end - it < 6 ) return false;
|
if ( track_end - it < 6 ) return false;
|
||||||
it += 6;
|
it += 6;
|
||||||
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_end, _countof( loop_end ) ) );
|
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, loop_end, _countof( loop_end ) ) );
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Unexpected HMI meta event" );*/
|
else return false; /*throw exception_io_data( "Unexpected HMI meta event" );*/
|
||||||
}
|
}
|
||||||
else if ( buffer[ 0 ] <= 0xEF )
|
else if ( buffer[ 0 ] <= 0xEF )
|
||||||
{
|
{
|
||||||
unsigned bytes_read = 1;
|
unsigned bytes_read = 1;
|
||||||
if ( buffer[ 0 ] >= 0x80 )
|
if ( buffer[ 0 ] >= 0x80 )
|
||||||
{
|
{
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
last_event_code = buffer[ 0 ];
|
last_event_code = buffer[ 0 ];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ( last_event_code == 0xFF ) return false; /*throw exception_io_data( "HMI used shortened event after Meta or SysEx message" );*/
|
if ( last_event_code == 0xFF ) return false; /*throw exception_io_data( "HMI used shortened event after Meta or SysEx message" );*/
|
||||||
buffer[ 1 ] = buffer[ 0 ];
|
buffer[ 1 ] = buffer[ 0 ];
|
||||||
buffer[ 0 ] = last_event_code;
|
buffer[ 0 ] = last_event_code;
|
||||||
}
|
}
|
||||||
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
|
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
|
||||||
unsigned channel = buffer[ 0 ] & 0x0F;
|
unsigned channel = buffer[ 0 ] & 0x0F;
|
||||||
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
|
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
|
||||||
{
|
{
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
buffer[ 2 ] = *it++;
|
buffer[ 2 ] = *it++;
|
||||||
bytes_read = 2;
|
bytes_read = 2;
|
||||||
}
|
}
|
||||||
track.add_event( midi_event( current_timestamp, type, channel, &buffer[ 1 ], bytes_read ) );
|
track.add_event( midi_event( current_timestamp, type, channel, &buffer[ 1 ], bytes_read ) );
|
||||||
if ( type == midi_event::note_on )
|
if ( type == midi_event::note_on )
|
||||||
{
|
{
|
||||||
buffer[ 2 ] = 0x00;
|
buffer[ 2 ] = 0x00;
|
||||||
int note_length = decode_delta( it, track_end );
|
int note_length = decode_delta( it, track_end );
|
||||||
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid HMI note message" );*/
|
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid HMI note message" );*/
|
||||||
unsigned note_end_timestamp = current_timestamp + note_length;
|
unsigned note_end_timestamp = current_timestamp + note_length;
|
||||||
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
|
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
|
||||||
track.add_event( midi_event( note_end_timestamp, midi_event::note_on, channel, &buffer[1], bytes_read ) );
|
track.add_event( midi_event( note_end_timestamp, midi_event::note_on, channel, &buffer[1], bytes_read ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Unexpected HMI status code" );*/
|
else return false; /*throw exception_io_data( "Unexpected HMI status code" );*/
|
||||||
}
|
}
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,18 @@ bool midi_processor::is_hmp( std::vector<uint8_t> const& p_file )
|
||||||
|
|
||||||
unsigned midi_processor::decode_hmp_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
|
unsigned midi_processor::decode_hmp_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
|
||||||
{
|
{
|
||||||
unsigned delta = 0;
|
unsigned delta = 0;
|
||||||
unsigned shift = 0;
|
unsigned shift = 0;
|
||||||
unsigned char byte;
|
unsigned char byte;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if ( it == end ) return 0;
|
if ( it == end ) return 0;
|
||||||
byte = *it++;
|
byte = *it++;
|
||||||
delta = delta + ( ( byte & 0x7F ) << shift );
|
delta = delta + ( ( byte & 0x7F ) << shift );
|
||||||
shift += 7;
|
shift += 7;
|
||||||
}
|
}
|
||||||
while ( !( byte & 0x80 ) );
|
while ( !( byte & 0x80 ) );
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
||||||
|
@ -34,87 +34,89 @@ bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
uint8_t track_count_8;
|
uint8_t track_count_8;
|
||||||
uint16_t dtx = 0xC0;
|
uint16_t dtx = 0xC0;
|
||||||
|
|
||||||
uint32_t offset = is_funky ? 0x1A : 0x30;
|
uint32_t offset = is_funky ? 0x1A : 0x30;
|
||||||
|
|
||||||
if ( offset >= p_file.size() )
|
if ( offset >= p_file.size() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset;
|
||||||
std::vector<uint8_t>::const_iterator end = p_file.end();
|
std::vector<uint8_t>::const_iterator end = p_file.end();
|
||||||
|
|
||||||
track_count_8 = *it;
|
track_count_8 = *it;
|
||||||
|
|
||||||
if ( is_funky )
|
if ( is_funky )
|
||||||
{
|
{
|
||||||
if ( p_file.size() <= 0x4D )
|
if ( p_file.size() <= 0x4D )
|
||||||
return false;
|
return false;
|
||||||
dtx = ( p_file[ 0x4C ] << 16 ) | p_file[ 0x4D ];
|
dtx = ( p_file[ 0x4C ] << 16 ) | p_file[ 0x4D ];
|
||||||
}
|
if ( !dtx ) // dtx == 0, will cause division by zero on tempo calculations
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
p_out.initialize( 1, dtx );
|
p_out.initialize( 1, dtx );
|
||||||
|
|
||||||
{
|
{
|
||||||
midi_track track;
|
midi_track track;
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, hmp_default_tempo, _countof( hmp_default_tempo ) ) );
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t buffer[ 4 ];
|
uint8_t buffer[ 4 ];
|
||||||
|
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 0 ] = *it++;
|
buffer[ 0 ] = *it++;
|
||||||
|
|
||||||
while ( it != end )
|
while ( it != end )
|
||||||
{
|
{
|
||||||
if ( buffer[ 0 ] != 0xFF )
|
if ( buffer[ 0 ] != 0xFF )
|
||||||
{
|
{
|
||||||
buffer[ 0 ] = *it++;
|
buffer[ 0 ] = *it++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( it == end ) break;
|
if ( it == end ) break;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
if ( buffer[ 1 ] != 0x2F )
|
if ( buffer[ 1 ] != 0x2F )
|
||||||
{
|
{
|
||||||
buffer[ 0 ] = buffer[ 1 ];
|
buffer[ 0 ] = buffer[ 1 ];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = is_funky ? 3 : 5;
|
offset = is_funky ? 3 : 5;
|
||||||
if ( (unsigned long)(end - it) < offset ) return false;
|
if ( (unsigned long)(end - it) < offset ) return false;
|
||||||
it += offset;
|
it += offset;
|
||||||
|
|
||||||
unsigned track_count = track_count_8;
|
unsigned track_count = track_count_8;
|
||||||
|
|
||||||
for ( unsigned i = 1; i < track_count; ++i )
|
for ( unsigned i = 1; i < track_count; ++i )
|
||||||
{
|
{
|
||||||
uint16_t track_size_16;
|
uint16_t track_size_16;
|
||||||
uint32_t track_size_32;
|
uint32_t track_size_32;
|
||||||
|
|
||||||
if ( is_funky )
|
if ( is_funky )
|
||||||
{
|
{
|
||||||
if ( end - it < 4 ) break;
|
if ( end - it < 4 ) break;
|
||||||
track_size_16 = it[ 0 ] | ( it[ 1 ] << 8 );
|
track_size_16 = it[ 0 ] | ( it[ 1 ] << 8 );
|
||||||
it += 2;
|
it += 2;
|
||||||
track_size_32 = track_size_16 - 4;
|
track_size_32 = track_size_16 - 4;
|
||||||
if ( (unsigned long)(end - it) < track_size_32 + 2 ) break;
|
if ( (unsigned long)(end - it) < track_size_32 + 2 ) break;
|
||||||
it += 2;
|
it += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ( end - it < 8 ) break;
|
if ( end - it < 8 ) break;
|
||||||
track_size_32 = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
track_size_32 = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
track_size_32 -= 12;
|
track_size_32 -= 12;
|
||||||
if ( (unsigned long)(end - it) < track_size_32 + 8 ) break;
|
if ( (unsigned long)(end - it) < track_size_32 + 8 ) break;
|
||||||
it += 4;
|
it += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
|
|
||||||
unsigned current_timestamp = 0;
|
unsigned current_timestamp = 0;
|
||||||
|
|
||||||
std::vector<uint8_t> _buffer;
|
std::vector<uint8_t> _buffer;
|
||||||
_buffer.resize( 3 );
|
_buffer.resize( 3 );
|
||||||
|
@ -122,47 +124,47 @@ bool midi_processor::process_hmp( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
std::vector<uint8_t>::const_iterator track_end = it + track_size_32;
|
std::vector<uint8_t>::const_iterator track_end = it + track_size_32;
|
||||||
|
|
||||||
while ( it != track_end )
|
while ( it != track_end )
|
||||||
{
|
{
|
||||||
unsigned delta = decode_hmp_delta( it, track_end );
|
unsigned delta = decode_hmp_delta( it, track_end );
|
||||||
current_timestamp += delta;
|
current_timestamp += delta;
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
_buffer[ 0 ] = *it++;
|
_buffer[ 0 ] = *it++;
|
||||||
if ( _buffer[ 0 ] == 0xFF )
|
if ( _buffer[ 0 ] == 0xFF )
|
||||||
{
|
{
|
||||||
if ( it == track_end ) return false;
|
if ( it == track_end ) return false;
|
||||||
_buffer[ 1 ] = *it++;
|
_buffer[ 1 ] = *it++;
|
||||||
int meta_count = decode_delta( it, track_end );
|
int meta_count = decode_delta( it, track_end );
|
||||||
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMP meta message" );*/
|
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid HMP meta message" );*/
|
||||||
if ( track_end - it < meta_count ) return false;
|
if ( track_end - it < meta_count ) return false;
|
||||||
_buffer.resize( meta_count + 2 );
|
_buffer.resize( meta_count + 2 );
|
||||||
std::copy( it, it + meta_count, _buffer.begin() + 2 );
|
std::copy( it, it + meta_count, _buffer.begin() + 2 );
|
||||||
it += meta_count;
|
it += meta_count;
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &_buffer[0], meta_count + 2 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &_buffer[0], meta_count + 2 ) );
|
||||||
if ( _buffer[ 1 ] == 0x2F ) break;
|
if ( _buffer[ 1 ] == 0x2F ) break;
|
||||||
}
|
}
|
||||||
else if ( _buffer[ 0 ] >= 0x80 && _buffer[ 0 ] <= 0xEF )
|
else if ( _buffer[ 0 ] >= 0x80 && _buffer[ 0 ] <= 0xEF )
|
||||||
{
|
{
|
||||||
unsigned bytes_read = 2;
|
unsigned bytes_read = 2;
|
||||||
switch ( _buffer[ 0 ] & 0xF0 )
|
switch ( _buffer[ 0 ] & 0xF0 )
|
||||||
{
|
{
|
||||||
case 0xC0:
|
case 0xC0:
|
||||||
case 0xD0:
|
case 0xD0:
|
||||||
bytes_read = 1;
|
bytes_read = 1;
|
||||||
}
|
}
|
||||||
if ( (unsigned long)(track_end - it) < bytes_read ) return false;
|
if ( (unsigned long)(track_end - it) < bytes_read ) return false;
|
||||||
std::copy( it, it + bytes_read, _buffer.begin() + 1 );
|
std::copy( it, it + bytes_read, _buffer.begin() + 1 );
|
||||||
it += bytes_read;
|
it += bytes_read;
|
||||||
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( ( _buffer[ 0 ] >> 4 ) - 8 ), _buffer[ 0 ] & 0x0F, &_buffer[1], bytes_read ) );
|
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( ( _buffer[ 0 ] >> 4 ) - 8 ), _buffer[ 0 ] & 0x0F, &_buffer[1], bytes_read ) );
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Unexpected status code in HMP track" );*/
|
else return false; /*throw exception_io_data( "Unexpected status code in HMP track" );*/
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = is_funky ? 0 : 4;
|
offset = is_funky ? 0 : 4;
|
||||||
if ( end - it < (signed long)offset ) return false;
|
if ( end - it < (signed long)offset ) return false;
|
||||||
it = track_end + offset;
|
it = track_end + offset;
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
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 )
|
bool midi_processor::process_mids( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
||||||
{
|
{
|
||||||
if ( p_file.size() < 20 ) return false;
|
if ( p_file.size() < 20 ) return false;
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + 16;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + 16;
|
||||||
std::vector<uint8_t>::const_iterator end = p_file.end();
|
std::vector<uint8_t>::const_iterator end = p_file.end();
|
||||||
|
|
||||||
uint32_t fmt_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
uint32_t fmt_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
if ( (unsigned long)(end - it) < fmt_size ) return false;
|
if ( (unsigned long)(end - it) < fmt_size ) return false;
|
||||||
|
|
||||||
uint32_t time_format = 1;
|
uint32_t time_format = 1;
|
||||||
/*uint32_t max_buffer = 0;*/
|
/*uint32_t max_buffer = 0;*/
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
|
|
||||||
if ( fmt_size >= 4 )
|
if ( fmt_size >= 4 )
|
||||||
{
|
{
|
||||||
time_format = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
time_format = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
fmt_size -= 4;
|
fmt_size -= 4;
|
||||||
}
|
if ( !time_format ) // dtx == 0, will cause division by zero on tempo calculations
|
||||||
if ( fmt_size >= 4 )
|
return false;
|
||||||
{
|
}
|
||||||
|
if ( fmt_size >= 4 )
|
||||||
|
{
|
||||||
/*max_buffer = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );*/
|
/*max_buffer = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );*/
|
||||||
it += 4;
|
it += 4;
|
||||||
fmt_size -= 4;
|
fmt_size -= 4;
|
||||||
}
|
}
|
||||||
if ( fmt_size >= 4 )
|
if ( fmt_size >= 4 )
|
||||||
{
|
{
|
||||||
flags = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
flags = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
fmt_size -= 4;
|
fmt_size -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
it += fmt_size;
|
it += fmt_size;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
if ( fmt_size & 1 ) ++it;
|
if ( fmt_size & 1 ) ++it;
|
||||||
|
|
||||||
p_out.initialize( 0, time_format );
|
p_out.initialize( 0, time_format );
|
||||||
|
|
||||||
if ( end - it < 4 ) return false;
|
if ( end - it < 4 ) return false;
|
||||||
if ( it[ 0 ] != 'd' || it[ 1 ] != 'a' || it[ 2 ] != 't' || it[ 3 ] != 'a' ) return false; /*throw exception_io_data( "MIDS missing RIFF data chunk" );*/
|
if ( it[ 0 ] != 'd' || it[ 1 ] != 'a' || it[ 2 ] != 't' || it[ 3 ] != 'a' ) return false; /*throw exception_io_data( "MIDS missing RIFF data chunk" );*/
|
||||||
|
|
||||||
it += 4;
|
it += 4;
|
||||||
|
|
||||||
{
|
{
|
||||||
midi_track track;
|
midi_track track;
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( end - it < 4 ) return false;
|
if ( end - it < 4 ) return false;
|
||||||
uint32_t data_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
uint32_t data_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator body_end = it + data_size;
|
std::vector<uint8_t>::const_iterator body_end = it + data_size;
|
||||||
|
|
||||||
if ( body_end - it < 4 ) return false;
|
if ( body_end - it < 4 ) return false;
|
||||||
uint32_t segment_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
uint32_t segment_count = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
|
|
||||||
bool is_eight_byte = !!(flags & 1);
|
bool is_eight_byte = !!(flags & 1);
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
|
|
||||||
unsigned current_timestamp = 0;
|
unsigned current_timestamp = 0;
|
||||||
|
|
||||||
for ( unsigned i = 0; i < segment_count; ++i )
|
for ( unsigned i = 0; i < segment_count; ++i )
|
||||||
{
|
{
|
||||||
if ( end - it < 12 ) return false;
|
if ( end - it < 12 ) return false;
|
||||||
it += 4;
|
it += 4;
|
||||||
uint32_t segment_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
uint32_t segment_size = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
std::vector<uint8_t>::const_iterator segment_end = it + segment_size;
|
std::vector<uint8_t>::const_iterator segment_end = it + segment_size;
|
||||||
while ( it != segment_end && it != body_end )
|
while ( it != segment_end && it != body_end )
|
||||||
{
|
{
|
||||||
if ( segment_end - it < 4 ) return false;
|
if ( segment_end - it < 4 ) return false;
|
||||||
uint32_t delta = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
uint32_t delta = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
uint32_t event;
|
uint32_t event;
|
||||||
current_timestamp += delta;
|
current_timestamp += delta;
|
||||||
if ( !is_eight_byte )
|
if ( !is_eight_byte )
|
||||||
{
|
{
|
||||||
if ( segment_end - it < 4 ) return false;
|
if ( segment_end - it < 4 ) return false;
|
||||||
it += 4;
|
it += 4;
|
||||||
}
|
}
|
||||||
if ( segment_end - it < 4 ) return false;
|
if ( segment_end - it < 4 ) return false;
|
||||||
event = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
event = it[ 0 ] | ( it[ 1 ] << 8 ) | ( it[ 2 ] << 16 ) | ( it[ 3 ] << 24 );
|
||||||
it += 4;
|
it += 4;
|
||||||
if ( event >> 24 == 0x01 )
|
if ( event >> 24 == 0x01 )
|
||||||
{
|
{
|
||||||
uint8_t buffer[ 5 ] = { 0xFF, 0x51 };
|
uint8_t buffer[ 5 ] = { 0xFF, 0x51 };
|
||||||
buffer[ 2 ] = (uint8_t)( event >> 16 );
|
buffer[ 2 ] = (uint8_t)( event >> 16 );
|
||||||
buffer[ 3 ] = (uint8_t)( event >> 8 );
|
buffer[ 3 ] = (uint8_t)( event >> 8 );
|
||||||
buffer[ 4 ] = (uint8_t)event;
|
buffer[ 4 ] = (uint8_t)event;
|
||||||
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, buffer, sizeof( buffer ) ) );
|
p_out.add_track_event( 0, midi_event( current_timestamp, midi_event::extended, 0, buffer, sizeof( buffer ) ) );
|
||||||
}
|
}
|
||||||
else if ( !( event >> 24 ) )
|
else if ( !( event >> 24 ) )
|
||||||
{
|
{
|
||||||
unsigned event_code = ( event & 0xF0 ) >> 4;
|
unsigned event_code = ( event & 0xF0 ) >> 4;
|
||||||
if ( event_code >= 0x8 && event_code <= 0xE )
|
if ( event_code >= 0x8 && event_code <= 0xE )
|
||||||
{
|
{
|
||||||
unsigned bytes_to_write = 1;
|
unsigned bytes_to_write = 1;
|
||||||
uint8_t buffer[2];
|
uint8_t buffer[2];
|
||||||
buffer[ 0 ] = (uint8_t)( event >> 8 );
|
buffer[ 0 ] = (uint8_t)( event >> 8 );
|
||||||
if ( event_code != 0xC && event_code != 0xD )
|
if ( event_code != 0xC && event_code != 0xD )
|
||||||
{
|
{
|
||||||
buffer[ 1 ] = (uint8_t)( event >> 16 );
|
buffer[ 1 ] = (uint8_t)( event >> 16 );
|
||||||
bytes_to_write = 2;
|
bytes_to_write = 2;
|
||||||
}
|
}
|
||||||
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( event_code - 8 ), event & 0x0F, buffer, bytes_to_write ) );
|
track.add_event( midi_event( current_timestamp, (midi_event::event_type)( event_code - 8 ), event & 0x0F, buffer, bytes_to_write ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,132 +20,132 @@ bool midi_processor::process_mus( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
uint16_t length = p_file[ 4 ] | ( p_file[ 5 ] << 8 );
|
uint16_t length = p_file[ 4 ] | ( p_file[ 5 ] << 8 );
|
||||||
uint16_t offset = p_file[ 6 ] | ( p_file[ 7 ] << 8 );
|
uint16_t offset = p_file[ 6 ] | ( p_file[ 7 ] << 8 );
|
||||||
|
|
||||||
p_out.initialize( 0, 0x59 );
|
p_out.initialize( 0, 0x59 );
|
||||||
|
|
||||||
{
|
{
|
||||||
midi_track track;
|
midi_track track;
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, mus_default_tempo, _countof( mus_default_tempo ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, mus_default_tempo, _countof( mus_default_tempo ) ) );
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
|
|
||||||
unsigned current_timestamp = 0;
|
unsigned current_timestamp = 0;
|
||||||
|
|
||||||
uint8_t velocity_levels[ 16 ] = { 0 };
|
uint8_t velocity_levels[ 16 ] = { 0 };
|
||||||
|
|
||||||
if ( (size_t)offset >= p_file.size() || (size_t)(offset + length) > p_file.size() )
|
if ( (size_t)offset >= p_file.size() || (size_t)(offset + length) > p_file.size() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset, end = p_file.begin() + offset + length;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + offset, end = p_file.begin() + offset + length;
|
||||||
|
|
||||||
uint8_t buffer[ 4 ];
|
uint8_t buffer[ 4 ];
|
||||||
|
|
||||||
while ( it != end )
|
while ( it != end )
|
||||||
{
|
{
|
||||||
buffer[ 0 ] = *it++;
|
buffer[ 0 ] = *it++;
|
||||||
if ( buffer[ 0 ] == 0x60 ) break;
|
if ( buffer[ 0 ] == 0x60 ) break;
|
||||||
|
|
||||||
midi_event::event_type type;
|
midi_event::event_type type;
|
||||||
|
|
||||||
unsigned bytes_to_write;
|
unsigned bytes_to_write;
|
||||||
|
|
||||||
unsigned channel = buffer[ 0 ] & 0x0F;
|
unsigned channel = buffer[ 0 ] & 0x0F;
|
||||||
if ( channel == 0x0F ) channel = 9;
|
if ( channel == 0x0F ) channel = 9;
|
||||||
else if ( channel >= 9 ) ++channel;
|
else if ( channel >= 9 ) ++channel;
|
||||||
|
|
||||||
switch ( buffer[ 0 ] & 0x70 )
|
switch ( buffer[ 0 ] & 0x70 )
|
||||||
{
|
{
|
||||||
case 0x00:
|
case 0x00:
|
||||||
type = midi_event::note_on;
|
type = midi_event::note_on;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
buffer[ 2 ] = 0;
|
buffer[ 2 ] = 0;
|
||||||
bytes_to_write = 2;
|
bytes_to_write = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x10:
|
case 0x10:
|
||||||
type = midi_event::note_on;
|
type = midi_event::note_on;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
if ( buffer[ 1 ] & 0x80 )
|
if ( buffer[ 1 ] & 0x80 )
|
||||||
{
|
{
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 2 ] = *it++;
|
buffer[ 2 ] = *it++;
|
||||||
velocity_levels[ channel ] = buffer[ 2 ];
|
velocity_levels[ channel ] = buffer[ 2 ];
|
||||||
buffer[ 1 ] &= 0x7F;
|
buffer[ 1 ] &= 0x7F;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer[ 2 ] = velocity_levels[ channel ];
|
buffer[ 2 ] = velocity_levels[ channel ];
|
||||||
}
|
}
|
||||||
bytes_to_write = 2;
|
bytes_to_write = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x20:
|
case 0x20:
|
||||||
type = midi_event::pitch_wheel;
|
type = midi_event::pitch_wheel;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
buffer[ 2 ] = buffer[ 1 ] >> 1;
|
buffer[ 2 ] = buffer[ 1 ] >> 1;
|
||||||
buffer[ 1 ] <<= 7;
|
buffer[ 1 ] <<= 7;
|
||||||
bytes_to_write = 2;
|
bytes_to_write = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x30:
|
case 0x30:
|
||||||
type = midi_event::control_change;
|
type = midi_event::control_change;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
if ( buffer[ 1 ] >= 10 && buffer[ 1 ] <= 14 )
|
if ( buffer[ 1 ] >= 10 && buffer[ 1 ] <= 14 )
|
||||||
{
|
{
|
||||||
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
|
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
|
||||||
buffer[ 2 ] = 1;
|
buffer[ 2 ] = 1;
|
||||||
bytes_to_write = 2;
|
bytes_to_write = 2;
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Unhandled MUS system event" );*/
|
else return false; /*throw exception_io_data( "Unhandled MUS system event" );*/
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x40:
|
case 0x40:
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
if ( buffer[ 1 ] )
|
if ( buffer[ 1 ] )
|
||||||
{
|
{
|
||||||
if ( buffer[ 1 ] < 10 )
|
if ( buffer[ 1 ] < 10 )
|
||||||
{
|
{
|
||||||
type = midi_event::control_change;
|
type = midi_event::control_change;
|
||||||
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
|
buffer[ 1 ] = mus_controllers[ buffer[ 1 ] ];
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 2 ] = *it++;
|
buffer[ 2 ] = *it++;
|
||||||
bytes_to_write = 2;
|
bytes_to_write = 2;
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Invalid MUS controller change event" );*/
|
else return false; /*throw exception_io_data( "Invalid MUS controller change event" );*/
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
type = midi_event::program_change;
|
type = midi_event::program_change;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
bytes_to_write = 1;
|
bytes_to_write = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false; /*throw exception_io_data( "Invalid MUS status code" );*/
|
return false; /*throw exception_io_data( "Invalid MUS status code" );*/
|
||||||
}
|
}
|
||||||
|
|
||||||
track.add_event( midi_event( current_timestamp, type, channel, buffer + 1, bytes_to_write ) );
|
track.add_event( midi_event( current_timestamp, type, channel, buffer + 1, bytes_to_write ) );
|
||||||
|
|
||||||
if ( buffer[ 0 ] & 0x80 )
|
if ( buffer[ 0 ] & 0x80 )
|
||||||
{
|
{
|
||||||
int delta = decode_delta( it, end );
|
int delta = decode_delta( it, end );
|
||||||
if ( delta < 0 ) return false; /*throw exception_io_data( "Invalid MUS delta" );*/
|
if ( delta < 0 ) return false; /*throw exception_io_data( "Invalid MUS delta" );*/
|
||||||
current_timestamp += delta;
|
current_timestamp += delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, end_of_track, _countof( end_of_track ) ) );
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,42 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
static inline bool it_equal( std::vector<uint8_t>::const_iterator it1, const char *it2, size_t length )
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < length; i++ )
|
||||||
|
{
|
||||||
|
if ( it1[ i ] != it2[ i ] )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t toInt32LE( const uint8_t *in )
|
||||||
|
{
|
||||||
|
return static_cast<uint32_t>( in[ 0 ] ) |
|
||||||
|
static_cast<uint32_t>( in[ 1 ] << 8 ) |
|
||||||
|
static_cast<uint32_t>( in[ 2 ] << 16 ) |
|
||||||
|
static_cast<uint32_t>( in[ 3 ] << 24 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t toInt32LE( std::vector<uint8_t>::const_iterator in )
|
||||||
|
{
|
||||||
|
return static_cast<uint32_t>( in[ 0 ] ) |
|
||||||
|
static_cast<uint32_t>( in[ 1 ] << 8 ) |
|
||||||
|
static_cast<uint32_t>( in[ 2 ] << 16 ) |
|
||||||
|
static_cast<uint32_t>( in[ 3 ] << 24 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool midi_processor::is_riff_midi( std::vector<uint8_t> const& p_file )
|
bool midi_processor::is_riff_midi( std::vector<uint8_t> const& p_file )
|
||||||
{
|
{
|
||||||
if ( p_file.size() < 20 ) return false;
|
if ( p_file.size() < 20 ) return false;
|
||||||
if ( p_file[ 0 ] != 'R' || p_file[ 1 ] != 'I' || p_file[ 2 ] != 'F' || p_file[ 3 ] != 'F' ) return false;
|
if ( memcmp( &p_file[ 0 ], "RIFF", 4 ) != 0 ) return false;
|
||||||
uint32_t riff_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 );
|
uint32_t riff_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 );
|
||||||
if ( riff_size < 12 || ( p_file.size() < riff_size + 8 ) ) return false;
|
if ( riff_size < 12 || ( p_file.size() < riff_size + 8 ) ) return false;
|
||||||
if ( p_file[ 8 ] != 'R' || p_file[ 9 ] != 'M' || p_file[ 10 ] != 'I' || p_file[ 11 ] != 'D' ||
|
if ( memcmp( &p_file[ 8 ], "RMID", 4 ) != 0 ||
|
||||||
p_file[ 12 ] != 'd' || p_file[ 13 ] != 'a' || p_file[ 14 ] != 't' || p_file[ 15 ] != 'a' ) return false;
|
memcmp( &p_file[ 12 ], "data", 4 ) != 0 ) return false;
|
||||||
uint32_t data_size = p_file[ 16 ] | ( p_file[ 17 ] << 8 ) | ( p_file[ 18 ] << 16 ) | ( p_file[ 19 ] << 24 );
|
uint32_t data_size = toInt32LE(&p_file[ 16 ]);
|
||||||
if ( data_size < 18 || p_file.size() < data_size + 20 || riff_size < data_size + 12 ) return false;
|
if ( data_size < 18 || p_file.size() < data_size + 20 || riff_size < data_size + 12 ) return false;
|
||||||
std::vector<uint8_t> test;
|
std::vector<uint8_t> test;
|
||||||
test.assign( p_file.begin() + 20, p_file.begin() + 20 + 18 );
|
test.assign( p_file.begin() + 20, p_file.begin() + 20 + 18 );
|
||||||
|
@ -20,21 +47,21 @@ bool midi_processor::is_riff_midi( std::vector<uint8_t> const& p_file )
|
||||||
bool midi_processor::process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
|
bool midi_processor::process_riff_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
|
||||||
{
|
{
|
||||||
track_count = 0;
|
track_count = 0;
|
||||||
|
|
||||||
uint32_t file_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 );
|
uint32_t file_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 );
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + 12;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + 12;
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size;
|
std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size;
|
||||||
|
|
||||||
std::vector<uint8_t> extra_buffer;
|
std::vector<uint8_t> extra_buffer;
|
||||||
|
|
||||||
while ( it != body_end )
|
while ( it != body_end )
|
||||||
{
|
{
|
||||||
if ( body_end - it < 8 ) return false;
|
if ( body_end - it < 8 ) return false;
|
||||||
uint32_t chunk_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
|
uint32_t chunk_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
|
||||||
if ( (unsigned long)(body_end - it) < chunk_size ) return false;
|
if ( (unsigned long)(body_end - it) < chunk_size ) return false;
|
||||||
if ( it[ 0 ] == 'd' && it[ 1 ] == 'a' && it[ 2 ] == 't' && it[ 3 ] == 'a' )
|
if ( it_equal(it, "data", 4) )
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> midi_file;
|
std::vector<uint8_t> midi_file;
|
||||||
midi_file.assign( it + 8, it + 8 + chunk_size );
|
midi_file.assign( it + 8, it + 8 + chunk_size );
|
||||||
|
@ -46,32 +73,32 @@ bool midi_processor::process_riff_midi_count( std::vector<uint8_t> const& p_file
|
||||||
if ( chunk_size & 1 && it != body_end ) ++it;
|
if ( chunk_size & 1 && it != body_end ) ++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * riff_tag_mappings[][2] =
|
static const char * riff_tag_mappings[][2] =
|
||||||
{
|
{
|
||||||
{ "IALB", "album" },
|
{ "IALB", "album" },
|
||||||
{ "IARL", "archival_location" },
|
{ "IARL", "archival_location" },
|
||||||
{ "IART", "artist" },
|
{ "IART", "artist" },
|
||||||
{ "ITRK", "tracknumber" },
|
{ "ITRK", "tracknumber" },
|
||||||
{ "ICMS", "commissioned" },
|
{ "ICMS", "commissioned" },
|
||||||
{ "ICMP", "composer" },
|
{ "ICMP", "composer" },
|
||||||
{ "ICMT", "comment" },
|
{ "ICMT", "comment" },
|
||||||
{ "ICOP", "copyright" },
|
{ "ICOP", "copyright" },
|
||||||
{ "ICRD", "creation_date" },
|
{ "ICRD", "creation_date" },
|
||||||
{ "IENG", "engineer" },
|
{ "IENG", "engineer" },
|
||||||
{ "IGNR", "genre" },
|
{ "IGNR", "genre" },
|
||||||
{ "IKEY", "keywords" },
|
{ "IKEY", "keywords" },
|
||||||
{ "IMED", "medium" },
|
{ "IMED", "medium" },
|
||||||
{ "INAM", "title" },
|
{ "INAM", "title" },
|
||||||
{ "IPRD", "product" },
|
{ "IPRD", "product" },
|
||||||
{ "ISBJ", "subject" },
|
{ "ISBJ", "subject" },
|
||||||
{ "ISFT", "software" },
|
{ "ISFT", "software" },
|
||||||
{ "ISRC", "source" },
|
{ "ISRC", "source" },
|
||||||
{ "ISRF", "source_form" },
|
{ "ISRF", "source_form" },
|
||||||
{ "ITCH", "technician" }
|
{ "ITCH", "technician" }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
||||||
|
@ -82,77 +109,79 @@ bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size;
|
std::vector<uint8_t>::const_iterator body_end = p_file.begin() + 8 + file_size;
|
||||||
|
|
||||||
bool found_data = false;
|
bool found_data = false;
|
||||||
bool found_info = false;
|
bool found_info = false;
|
||||||
|
|
||||||
midi_meta_data meta_data;
|
midi_meta_data meta_data;
|
||||||
|
|
||||||
std::vector<uint8_t> extra_buffer;
|
std::vector<uint8_t> extra_buffer;
|
||||||
|
|
||||||
while ( it != body_end )
|
while ( it != body_end )
|
||||||
{
|
{
|
||||||
if ( body_end - it < 8 ) return false;
|
if ( body_end - it < 8 ) return false;
|
||||||
uint32_t chunk_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
|
uint32_t chunk_size = toInt32LE( it + 4 );
|
||||||
if ( (unsigned long)(body_end - it) < chunk_size ) return false;
|
if ( (unsigned long)(body_end - it) < chunk_size ) return false;
|
||||||
if ( it[ 0 ] == 'd' && it[ 1 ] == 'a' && it[ 2 ] == 't' && it[ 3 ] == 'a' )
|
if ( it_equal( it, "data", 4 ) )
|
||||||
{
|
{
|
||||||
if ( !found_data )
|
if ( !found_data )
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> midi_file;
|
std::vector<uint8_t> midi_file;
|
||||||
midi_file.assign( it + 8, it + 8 + chunk_size );
|
midi_file.assign( it + 8, it + 8 + chunk_size );
|
||||||
if ( !process_standard_midi( midi_file, p_out ) ) return false;
|
if ( !process_standard_midi( midi_file, p_out ) ) return false;
|
||||||
found_data = true;
|
found_data = true;
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Multiple RIFF data chunks found" );*/
|
else return false; /*throw exception_io_data( "Multiple RIFF data chunks found" );*/
|
||||||
it += 8 + chunk_size;
|
it += 8 + chunk_size;
|
||||||
if ( chunk_size & 1 && it != body_end ) ++it;
|
if ( chunk_size & 1 && it != body_end ) ++it;
|
||||||
}
|
}
|
||||||
else if ( it[ 0 ] == 'D' && it[ 1 ] == 'I' && it[ 2 ] == 'S' && it[ 3 ] == 'P' )
|
else if ( it_equal( it, "DISP", 4 ) )
|
||||||
{
|
{
|
||||||
uint32_t type = it[ 8 ] | ( it[ 9 ] << 8 ) | ( it[ 10 ] << 16 ) | ( it[ 11 ] << 24 );
|
uint32_t type = toInt32LE( it + 8 );
|
||||||
if ( type == 1 )
|
if ( type == 1 )
|
||||||
{
|
{
|
||||||
extra_buffer.resize( chunk_size - 4 );
|
extra_buffer.resize( chunk_size - 4 + 1 );
|
||||||
std::copy( it + 12, it + 8 + chunk_size, extra_buffer.begin() );
|
std::copy( it + 12, it + 8 + chunk_size, extra_buffer.begin() );
|
||||||
|
extra_buffer[ chunk_size - 4 ] = '\0';
|
||||||
meta_data.add_item( midi_meta_data_item( 0, "display_name", (const char *) &extra_buffer[0] ) );
|
meta_data.add_item( midi_meta_data_item( 0, "display_name", (const char *) &extra_buffer[0] ) );
|
||||||
}
|
}
|
||||||
it += 8 + chunk_size;
|
it += 8 + chunk_size;
|
||||||
if ( chunk_size & 1 && it != body_end ) ++it;
|
if ( chunk_size & 1 && it != body_end ) ++it;
|
||||||
}
|
}
|
||||||
else if ( it[ 0 ] == 'L' && it[ 1 ] == 'I' && it[ 2 ] == 'S' && it[ 3 ] == 'T' )
|
else if ( it_equal( it, "LIST", 4 ) )
|
||||||
{
|
{
|
||||||
std::vector<uint8_t>::const_iterator chunk_end = it + 8 + chunk_size;
|
std::vector<uint8_t>::const_iterator chunk_end = it + 8 + chunk_size;
|
||||||
if ( it[ 8 ] == 'I' && it[ 9 ] == 'N' && it[ 10 ] == 'F' && it[ 11 ] == 'O' )
|
if ( it_equal( it + 8, "INFO", 4 ) )
|
||||||
{
|
{
|
||||||
if ( !found_info )
|
if ( !found_info )
|
||||||
{
|
{
|
||||||
if ( chunk_end - it < 12 ) return false;
|
if ( chunk_end - it < 12 ) return false;
|
||||||
it += 12;
|
it += 12;
|
||||||
while ( it != chunk_end )
|
while ( it != chunk_end )
|
||||||
{
|
{
|
||||||
if ( chunk_end - it < 4 ) return false;
|
if ( chunk_end - it < 4 ) return false;
|
||||||
uint32_t field_size = it[ 4 ] | ( it[ 5 ] << 8 ) | ( it[ 6 ] << 16 ) | ( it[ 7 ] << 24 );
|
uint32_t field_size = toInt32LE( it + 4 );
|
||||||
if ( (unsigned long)(chunk_end - it) < 8 + field_size ) return false;
|
if ( (unsigned long)(chunk_end - it) < 8 + field_size ) return false;
|
||||||
std::string field;
|
std::string field;
|
||||||
field.assign( it, it + 4 );
|
field.assign( it, it + 4 );
|
||||||
for ( unsigned i = 0; i < _countof(riff_tag_mappings); ++i )
|
for ( unsigned i = 0; i < _countof(riff_tag_mappings); ++i )
|
||||||
{
|
{
|
||||||
if ( !memcmp( &it[0], riff_tag_mappings[ i ][ 0 ], 4 ) )
|
if ( !memcmp( &it[0], riff_tag_mappings[ i ][ 0 ], 4 ) )
|
||||||
{
|
{
|
||||||
field = riff_tag_mappings[ i ][ 1 ];
|
field = riff_tag_mappings[ i ][ 1 ];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extra_buffer.resize( field_size );
|
extra_buffer.resize( field_size + 1 );
|
||||||
std::copy( it + 8, it + 8 + field_size, extra_buffer.begin() );
|
std::copy( it + 8, it + 8 + field_size, extra_buffer.begin() );
|
||||||
|
extra_buffer[ field_size ] = '\0';
|
||||||
it += 8 + field_size;
|
it += 8 + field_size;
|
||||||
meta_data.add_item( midi_meta_data_item( 0, field.c_str(), ( const char * ) &extra_buffer[0] ) );
|
meta_data.add_item( midi_meta_data_item( 0, field.c_str(), ( const char * ) &extra_buffer[0] ) );
|
||||||
if ( field_size & 1 && it != chunk_end ) ++it;
|
if ( field_size & 1 && it != chunk_end ) ++it;
|
||||||
}
|
}
|
||||||
found_info = true;
|
found_info = true;
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Multiple RIFF LIST INFO chunks found" );*/
|
else return false; /*throw exception_io_data( "Multiple RIFF LIST INFO chunks found" );*/
|
||||||
}
|
}
|
||||||
else return false; /* unknown LIST chunk */
|
else return false; /* unknown LIST chunk */
|
||||||
it = chunk_end;
|
it = chunk_end;
|
||||||
if ( chunk_size & 1 && it != body_end ) ++it;
|
if ( chunk_size & 1 && it != body_end ) ++it;
|
||||||
|
@ -163,10 +192,10 @@ bool midi_processor::process_riff_midi( std::vector<uint8_t> const& p_file, midi
|
||||||
if ( chunk_size & 1 && it != body_end ) ++it;
|
if ( chunk_size & 1 && it != body_end ) ++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( found_data && found_info ) break;
|
if ( found_data && found_info ) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_out.set_extra_meta_data( meta_data );
|
p_out.set_extra_meta_data( meta_data );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ bool midi_processor::is_standard_midi( std::vector<uint8_t> const& p_file )
|
||||||
if ( p_file.size() < 18 ) return false;
|
if ( p_file.size() < 18 ) return false;
|
||||||
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd') return false;
|
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd') return false;
|
||||||
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false;
|
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false;
|
||||||
|
if ( p_file[ 10 ] == 0 && p_file[ 11 ] == 0 ) return false; // no tracks
|
||||||
|
if ( p_file[ 12 ] == 0 && p_file[ 13 ] == 0 ) return false; // dtx == 0, will cause division by zero on tempo calculations
|
||||||
if ( p_file[ 14 ] != 'M' || p_file[ 15 ] != 'T' || p_file[ 16 ] != 'r' || p_file[ 17 ] != 'k' ) return false;
|
if ( p_file[ 14 ] != 'M' || p_file[ 15 ] != 'T' || p_file[ 16 ] != 'r' || p_file[ 17 ] != 'k' ) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -12,29 +14,29 @@ bool midi_processor::is_standard_midi( std::vector<uint8_t> const& p_file )
|
||||||
bool midi_processor::process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
|
bool midi_processor::process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
|
||||||
{
|
{
|
||||||
track_count = 0;
|
track_count = 0;
|
||||||
|
|
||||||
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd' ) return false;
|
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd' ) return false;
|
||||||
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
|
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
|
||||||
|
|
||||||
uint16_t form = ( it[0] << 8 ) | it[1];
|
uint16_t form = ( it[0] << 8 ) | it[1];
|
||||||
if ( form > 2 ) return false;
|
if ( form > 2 ) return false;
|
||||||
|
|
||||||
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
|
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
|
||||||
|
|
||||||
if ( form == 2 ) track_count = track_count_16;
|
if ( form == 2 ) track_count = track_count_16;
|
||||||
else track_count = 1;
|
else track_count = 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, midi_container & p_out, bool needs_end_marker )
|
bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, midi_container & p_out, bool needs_end_marker )
|
||||||
{
|
{
|
||||||
midi_track track;
|
midi_track track;
|
||||||
unsigned current_timestamp = 0;
|
unsigned current_timestamp = 0;
|
||||||
unsigned char last_event_code = 0xFF;
|
unsigned char last_event_code = 0xFF;
|
||||||
|
|
||||||
unsigned last_sysex_length = 0;
|
unsigned last_sysex_length = 0;
|
||||||
unsigned last_sysex_timestamp = 0;
|
unsigned last_sysex_timestamp = 0;
|
||||||
|
|
||||||
|
@ -67,15 +69,15 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
{
|
{
|
||||||
if ( last_sysex_length )
|
if ( last_sysex_length )
|
||||||
{
|
{
|
||||||
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length + 1 ) );
|
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
|
||||||
last_sysex_length = 0;
|
last_sysex_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_event_code = event_code;
|
last_event_code = event_code;
|
||||||
if ( !needs_end_marker && ( event_code & 0xF0 ) == 0xE0 ) continue;
|
if ( !needs_end_marker && ( event_code & 0xF0 ) == 0xE0 ) continue;
|
||||||
if ( data_bytes_read < 1 )
|
if ( data_bytes_read < 1 )
|
||||||
{
|
{
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 0 ] = *it++;
|
buffer[ 0 ] = *it++;
|
||||||
++data_bytes_read;
|
++data_bytes_read;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +87,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
case 0xD0:
|
case 0xD0:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ data_bytes_read ] = *it++;
|
buffer[ data_bytes_read ] = *it++;
|
||||||
++data_bytes_read;
|
++data_bytes_read;
|
||||||
}
|
}
|
||||||
|
@ -95,13 +97,13 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
{
|
{
|
||||||
if ( last_sysex_length )
|
if ( last_sysex_length )
|
||||||
{
|
{
|
||||||
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length + 1 ) );
|
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
|
||||||
last_sysex_length = 0;
|
last_sysex_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int data_count = decode_delta( it, end );
|
int data_count = decode_delta( it, end );
|
||||||
if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid System Exclusive message" );*/
|
if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid System Exclusive message" );*/
|
||||||
if ( end - it < data_count ) return false;
|
if ( end - it < data_count ) return false;
|
||||||
buffer.resize( data_count + 1 );
|
buffer.resize( data_count + 1 );
|
||||||
buffer[ 0 ] = 0xF0;
|
buffer[ 0 ] = 0xF0;
|
||||||
std::copy( it, it + data_count, buffer.begin() + 1 );
|
std::copy( it, it + data_count, buffer.begin() + 1 );
|
||||||
|
@ -115,7 +117,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
int data_count = decode_delta( it, end );
|
int data_count = decode_delta( it, end );
|
||||||
if ( data_count < 0 ) return false;
|
if ( data_count < 0 ) return false;
|
||||||
if ( end - it < data_count ) return false;
|
if ( end - it < data_count ) return false;
|
||||||
buffer.resize( last_sysex_length + data_count + 1 );
|
buffer.resize( last_sysex_length + data_count );
|
||||||
std::copy( it, it + data_count, buffer.begin() + last_sysex_length );
|
std::copy( it, it + data_count, buffer.begin() + last_sysex_length );
|
||||||
it += data_count;
|
it += data_count;
|
||||||
last_sysex_length += data_count;
|
last_sysex_length += data_count;
|
||||||
|
@ -124,7 +126,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
{
|
{
|
||||||
if ( last_sysex_length )
|
if ( last_sysex_length )
|
||||||
{
|
{
|
||||||
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length + 1 ) );
|
track.add_event( midi_event( last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
|
||||||
last_sysex_length = 0;
|
last_sysex_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +134,7 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
unsigned char meta_type = *it++;
|
unsigned char meta_type = *it++;
|
||||||
int data_count = decode_delta( it, end );
|
int data_count = decode_delta( it, end );
|
||||||
if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid meta message" );*/
|
if ( data_count < 0 ) return false; /*throw exception_io_data( "Invalid meta message" );*/
|
||||||
if ( end - it < data_count ) return false;
|
if ( end - it < data_count ) return false;
|
||||||
buffer.resize( data_count + 2 );
|
buffer.resize( data_count + 2 );
|
||||||
buffer[ 0 ] = 0xFF;
|
buffer[ 0 ] = 0xFF;
|
||||||
buffer[ 1 ] = meta_type;
|
buffer[ 1 ] = meta_type;
|
||||||
|
@ -148,21 +150,21 @@ bool midi_processor::process_standard_midi_track( std::vector<uint8_t>::const_it
|
||||||
}
|
}
|
||||||
else if ( event_code >= 0xF8 && event_code <= 0xFE )
|
else if ( event_code >= 0xF8 && event_code <= 0xFE )
|
||||||
{
|
{
|
||||||
/* Sequencer specific events, single byte */
|
/* Sequencer specific events, single byte */
|
||||||
buffer[ 0 ] = event_code;
|
buffer[ 0 ] = event_code;
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 1 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 1 ) );
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data("Unhandled MIDI status code");*/
|
else return false; /*throw exception_io_data("Unhandled MIDI status code");*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !needs_end_marker )
|
if ( !needs_end_marker )
|
||||||
{
|
{
|
||||||
buffer[ 0 ] = 0xFF;
|
buffer[ 0 ] = 0xFF;
|
||||||
buffer[ 1 ] = 0x2F;
|
buffer[ 1 ] = 0x2F;
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 2 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], 2 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +175,7 @@ bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file,
|
||||||
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
|
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
|
||||||
|
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
|
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
|
||||||
std::vector<uint8_t>::const_iterator end = p_file.end();
|
std::vector<uint8_t>::const_iterator end = p_file.end();
|
||||||
|
|
||||||
uint16_t form = ( it[0] << 8 ) | it[1];
|
uint16_t form = ( it[0] << 8 ) | it[1];
|
||||||
if ( form > 2 ) return false;
|
if ( form > 2 ) return false;
|
||||||
|
@ -181,19 +183,22 @@ bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file,
|
||||||
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
|
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
|
||||||
uint16_t dtx = ( it[4] << 8 ) | it[5];
|
uint16_t dtx = ( it[4] << 8 ) | it[5];
|
||||||
|
|
||||||
|
if ( !track_count_16 || !dtx )
|
||||||
|
return false;
|
||||||
|
|
||||||
it += 6;
|
it += 6;
|
||||||
|
|
||||||
std::size_t track_count = track_count_16;
|
std::size_t track_count = track_count_16;
|
||||||
|
|
||||||
p_out.initialize( form, dtx );
|
p_out.initialize( form, dtx );
|
||||||
|
|
||||||
for ( std::size_t i = 0; i < track_count; ++i )
|
for ( std::size_t i = 0; i < track_count; ++i )
|
||||||
{
|
{
|
||||||
if ( end - it < 8 ) return false;
|
if ( end - it < 8 ) return false;
|
||||||
if ( it[0] != 'M' || it[1] != 'T' || it[2] != 'r' || it[3] != 'k' ) return false;
|
if ( it[0] != 'M' || it[1] != 'T' || it[2] != 'r' || it[3] != 'k' ) return false;
|
||||||
|
|
||||||
uint32_t track_size = ( it[4] << 24 ) | ( it[5] << 16 ) | ( it[6] << 8 ) | it[7];
|
uint32_t track_size = ( it[4] << 24 ) | ( it[5] << 16 ) | ( it[6] << 8 ) | it[7];
|
||||||
if ( (unsigned long)(end - it) < track_size ) return false;
|
if ( (unsigned long)(end - it) < track_size ) return false;
|
||||||
|
|
||||||
it += 8;
|
it += 8;
|
||||||
|
|
||||||
|
@ -201,12 +206,12 @@ bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file,
|
||||||
|
|
||||||
if ( !process_standard_midi_track( it, it + track_size, p_out, true ) ) return false;
|
if ( !process_standard_midi_track( it, it + track_size, p_out, true ) ) return false;
|
||||||
|
|
||||||
track_data_offset += track_size;
|
track_data_offset += track_size;
|
||||||
if ( it - p_file.begin() != track_data_offset )
|
if ( it - p_file.begin() != track_data_offset )
|
||||||
{
|
{
|
||||||
it = p_file.begin() + track_data_offset;
|
it = p_file.begin() + track_data_offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
#include "midi_processor.h"
|
#include "midi_processor.h"
|
||||||
|
|
||||||
bool midi_processor::is_syx( std::vector<uint8_t> const& p_file )
|
bool midi_processor::is_syx( std::vector<uint8_t> const& p_file )
|
||||||
{
|
{
|
||||||
if ( p_file.size() < 2 ) return false;
|
if ( p_file.size() < 2 ) return false;
|
||||||
if ( p_file[ 0 ] != 0xF0 || p_file[ p_file.size() - 1 ] != 0xF7 ) return false;
|
if ( p_file[ 0 ] != 0xF0 || p_file[ p_file.size() - 1 ] != 0xF7 ) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi_processor::process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
bool midi_processor::process_syx( std::vector<uint8_t> const& p_file, midi_container & p_out )
|
||||||
{
|
{
|
||||||
const size_t size = p_file.size();
|
const size_t size = p_file.size();
|
||||||
size_t ptr = 0;
|
size_t ptr = 0;
|
||||||
|
|
||||||
p_out.initialize( 0, 1 );
|
p_out.initialize( 0, 1 );
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
|
|
||||||
while ( ptr < size )
|
while ( ptr < size )
|
||||||
{
|
{
|
||||||
size_t msg_length = 1;
|
size_t msg_length = 1;
|
||||||
|
|
||||||
if ( p_file[ptr] != 0xF0 ) return false;
|
if ( p_file[ptr] != 0xF0 ) return false;
|
||||||
|
|
||||||
while ( p_file[ptr + msg_length++] != 0xF7 );
|
while ( p_file[ptr + msg_length++] != 0xF7 );
|
||||||
|
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, &p_file[ptr], msg_length ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, &p_file[ptr], msg_length ) );
|
||||||
|
|
||||||
ptr += msg_length;
|
ptr += msg_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,21 @@ const uint8_t midi_processor::xmi_default_tempo[5] = {0xFF, 0x51, 0x07, 0xA1, 0x
|
||||||
|
|
||||||
unsigned midi_processor::decode_xmi_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
|
unsigned midi_processor::decode_xmi_delta( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end )
|
||||||
{
|
{
|
||||||
unsigned delta = 0;
|
unsigned delta = 0;
|
||||||
if ( it == end ) return 0;
|
if ( it == end ) return 0;
|
||||||
uint8_t byte = *it++;
|
uint8_t byte = *it++;
|
||||||
if ( !( byte & 0x80 ) )
|
if ( !( byte & 0x80 ) )
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
delta += byte;
|
delta += byte;
|
||||||
if ( it == end ) break;
|
if ( it == end ) break;
|
||||||
byte = *it++;
|
byte = *it++;
|
||||||
}
|
}
|
||||||
while ( !( byte & 0x80 ) && it != end );
|
while ( !( byte & 0x80 ) && it != end );
|
||||||
}
|
}
|
||||||
--it;
|
--it;
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct iff_chunk
|
struct iff_chunk
|
||||||
|
@ -39,46 +39,46 @@ struct iff_chunk
|
||||||
std::vector<uint8_t> m_data;
|
std::vector<uint8_t> m_data;
|
||||||
std::vector<iff_chunk> m_sub_chunks;
|
std::vector<iff_chunk> m_sub_chunks;
|
||||||
|
|
||||||
iff_chunk()
|
iff_chunk()
|
||||||
{
|
{
|
||||||
memset( m_id, 0, sizeof( m_id ) );
|
memset( m_id, 0, sizeof( m_id ) );
|
||||||
memset( m_type, 0, sizeof( m_type ) );
|
memset( m_type, 0, sizeof( m_type ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
iff_chunk( const iff_chunk & p_in )
|
iff_chunk( const iff_chunk & p_in )
|
||||||
{
|
{
|
||||||
memcpy( m_id, p_in.m_id, sizeof( m_id ) );
|
memcpy( m_id, p_in.m_id, sizeof( m_id ) );
|
||||||
memcpy( m_type, p_in.m_type, sizeof( m_type ) );
|
memcpy( m_type, p_in.m_type, sizeof( m_type ) );
|
||||||
m_data = p_in.m_data;
|
m_data = p_in.m_data;
|
||||||
m_sub_chunks = p_in.m_sub_chunks;
|
m_sub_chunks = p_in.m_sub_chunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iff_chunk & find_sub_chunk( const char * p_id, unsigned index = 0 ) const
|
const iff_chunk & find_sub_chunk( const char * p_id, unsigned index = 0 ) const
|
||||||
{
|
{
|
||||||
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
|
|
||||||
{
|
|
||||||
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
|
|
||||||
{
|
|
||||||
if ( index ) --index;
|
|
||||||
if ( !index ) return m_sub_chunks[ i ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned get_chunk_count( const char * p_id ) const
|
|
||||||
{
|
|
||||||
unsigned chunk_count = 0;
|
|
||||||
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
|
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
|
||||||
{
|
{
|
||||||
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
|
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
|
||||||
{
|
{
|
||||||
++chunk_count;
|
if ( index ) --index;
|
||||||
}
|
if ( !index ) return m_sub_chunks[ i ];
|
||||||
}
|
}
|
||||||
return chunk_count;
|
}
|
||||||
}
|
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned get_chunk_count( const char * p_id ) const
|
||||||
|
{
|
||||||
|
unsigned chunk_count = 0;
|
||||||
|
for ( std::size_t i = 0; i < m_sub_chunks.size(); ++i )
|
||||||
|
{
|
||||||
|
if ( !memcmp( p_id, m_sub_chunks[ i ].m_id, 4 ) )
|
||||||
|
{
|
||||||
|
++chunk_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chunk_count;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct iff_stream
|
struct iff_stream
|
||||||
|
@ -87,69 +87,69 @@ struct iff_stream
|
||||||
|
|
||||||
iff_chunk fail;
|
iff_chunk fail;
|
||||||
|
|
||||||
const iff_chunk & find_chunk( const char * p_id ) const
|
const iff_chunk & find_chunk( const char * p_id ) const
|
||||||
{
|
{
|
||||||
for ( std::size_t i = 0; i < m_chunks.size(); ++i )
|
for ( std::size_t i = 0; i < m_chunks.size(); ++i )
|
||||||
{
|
{
|
||||||
if ( !memcmp( p_id, m_chunks[ i ].m_id, 4 ) )
|
if ( !memcmp( p_id, m_chunks[ i ].m_id, 4 ) )
|
||||||
{
|
{
|
||||||
return m_chunks[ i ];
|
return m_chunks[ i ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
|
/*throw exception_io_data( pfc::string_formatter() << "Missing IFF chunk: " << p_id );*/
|
||||||
return fail;
|
return fail;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool read_iff_chunk( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, iff_chunk & p_out, bool first_chunk )
|
static bool read_iff_chunk( std::vector<uint8_t>::const_iterator & it, std::vector<uint8_t>::const_iterator end, iff_chunk & p_out, bool first_chunk )
|
||||||
{
|
{
|
||||||
if ( end - it < 8 ) return false;
|
if ( end - it < 8 ) return false;
|
||||||
std::copy( it, it + 4, p_out.m_id );
|
std::copy( it, it + 4, p_out.m_id );
|
||||||
it += 4;
|
it += 4;
|
||||||
uint32_t chunk_size = ( it[ 0 ] << 24 ) | ( it[ 1 ] << 16 ) | ( it[ 2 ] << 8 ) | it[ 3 ];
|
uint32_t chunk_size = ( it[ 0 ] << 24 ) | ( it[ 1 ] << 16 ) | ( it[ 2 ] << 8 ) | it[ 3 ];
|
||||||
if ( (unsigned long)(end - it) < chunk_size ) return false;
|
if ( (unsigned long)(end - it) < chunk_size ) return false;
|
||||||
it += 4;
|
it += 4;
|
||||||
bool is_cat_chunk = !memcmp( p_out.m_id, "CAT ", 4 );
|
bool is_cat_chunk = !memcmp( p_out.m_id, "CAT ", 4 );
|
||||||
bool is_form_chunk = !memcmp( p_out.m_id, "FORM", 4 );
|
bool is_form_chunk = !memcmp( p_out.m_id, "FORM", 4 );
|
||||||
std::size_t chunk_size_limit = end - it;
|
std::size_t chunk_size_limit = end - it;
|
||||||
if ( chunk_size > chunk_size_limit ) chunk_size = (uint32_t) chunk_size_limit;
|
if ( chunk_size > chunk_size_limit ) chunk_size = (uint32_t) chunk_size_limit;
|
||||||
if ( ( first_chunk && is_form_chunk ) || ( !first_chunk && is_cat_chunk ) )
|
if ( ( first_chunk && is_form_chunk ) || ( !first_chunk && is_cat_chunk ) )
|
||||||
{
|
{
|
||||||
if ( end - it < 4 ) return false;
|
if ( end - it < 4 ) return false;
|
||||||
std::vector<uint8_t>::const_iterator chunk_end = it + chunk_size;
|
std::vector<uint8_t>::const_iterator chunk_end = it + chunk_size;
|
||||||
std::copy( it, it + 4, p_out.m_type );
|
std::copy( it, it + 4, p_out.m_type );
|
||||||
it += 4;
|
it += 4;
|
||||||
while ( it < chunk_end )
|
while ( it < chunk_end )
|
||||||
{
|
{
|
||||||
iff_chunk chunk;
|
iff_chunk chunk;
|
||||||
if ( !read_iff_chunk( it, chunk_end, chunk, is_cat_chunk ) ) return false;
|
if ( !read_iff_chunk( it, chunk_end, chunk, is_cat_chunk ) ) return false;
|
||||||
p_out.m_sub_chunks.push_back( chunk );
|
p_out.m_sub_chunks.push_back( chunk );
|
||||||
}
|
}
|
||||||
it = chunk_end;
|
it = chunk_end;
|
||||||
if ( chunk_size & 1 && it != end ) ++it;
|
if ( chunk_size & 1 && it != end ) ++it;
|
||||||
}
|
}
|
||||||
else if ( !is_form_chunk && !is_cat_chunk )
|
else if ( !is_form_chunk && !is_cat_chunk )
|
||||||
{
|
{
|
||||||
p_out.m_data.assign( it, it + chunk_size );
|
p_out.m_data.assign( it, it + chunk_size );
|
||||||
it += chunk_size;
|
it += chunk_size;
|
||||||
if ( chunk_size & 1 && it != end ) ++it;
|
if ( chunk_size & 1 && it != end ) ++it;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*if ( first_chunk ) throw exception_io_data( pfc::string_formatter() << "Found " << pfc::string8( (const char *)p_out.m_id, 4 ) << " chunk instead of FORM" );
|
/*if ( first_chunk ) throw exception_io_data( pfc::string_formatter() << "Found " << pfc::string8( (const char *)p_out.m_id, 4 ) << " chunk instead of FORM" );
|
||||||
else throw exception_io_data( "Found multiple FORM chunks" );*/
|
else throw exception_io_data( "Found multiple FORM chunks" );*/
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool read_iff_stream( std::vector<uint8_t> const& p_file, iff_stream & p_out )
|
static bool read_iff_stream( std::vector<uint8_t> const& p_file, iff_stream & p_out )
|
||||||
{
|
{
|
||||||
std::vector<uint8_t>::const_iterator it = p_file.begin(), end = p_file.end();
|
std::vector<uint8_t>::const_iterator it = p_file.begin(), end = p_file.end();
|
||||||
bool first_chunk = true;
|
bool first_chunk = true;
|
||||||
while ( it != end )
|
while ( it != end )
|
||||||
{
|
{
|
||||||
iff_chunk chunk;
|
iff_chunk chunk;
|
||||||
if ( read_iff_chunk( it, end, chunk, first_chunk ) )
|
if ( read_iff_chunk( it, end, chunk, first_chunk ) )
|
||||||
{
|
{
|
||||||
p_out.m_chunks.push_back( chunk );
|
p_out.m_chunks.push_back( chunk );
|
||||||
|
@ -159,27 +159,27 @@ static bool read_iff_stream( std::vector<uint8_t> const& p_file, iff_stream & p_
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool midi_processor::process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
|
bool midi_processor::process_xmi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
|
||||||
{
|
{
|
||||||
track_count = 0;
|
track_count = 0;
|
||||||
|
|
||||||
iff_stream xmi_file;
|
iff_stream xmi_file;
|
||||||
if ( !read_iff_stream( p_file, xmi_file ) ) return false;
|
if ( !read_iff_stream( p_file, xmi_file ) ) return false;
|
||||||
|
|
||||||
const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" );
|
const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" );
|
||||||
if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/
|
if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/
|
||||||
|
|
||||||
const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " );
|
const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " );
|
||||||
if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/
|
if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/
|
||||||
|
|
||||||
unsigned _track_count = cat_chunk.get_chunk_count( "FORM" );
|
unsigned _track_count = cat_chunk.get_chunk_count( "FORM" );
|
||||||
|
|
||||||
track_count = _track_count;
|
track_count = _track_count;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,32 +188,32 @@ bool midi_processor::process_xmi( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
iff_stream xmi_file;
|
iff_stream xmi_file;
|
||||||
if ( !read_iff_stream( p_file, xmi_file ) ) return false;
|
if ( !read_iff_stream( p_file, xmi_file ) ) return false;
|
||||||
|
|
||||||
const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" );
|
const iff_chunk & form_chunk = xmi_file.find_chunk( "FORM" );
|
||||||
if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/
|
if ( memcmp( form_chunk.m_type, "XDIR", 4 ) ) return false; /*throw exception_io_data( "XMI IFF not XDIR type" );*/
|
||||||
|
|
||||||
const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " );
|
const iff_chunk & cat_chunk = xmi_file.find_chunk( "CAT " );
|
||||||
if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/
|
if ( memcmp( cat_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI CAT chunk not XMID type" );*/
|
||||||
|
|
||||||
unsigned track_count = cat_chunk.get_chunk_count( "FORM" );
|
unsigned track_count = cat_chunk.get_chunk_count( "FORM" );
|
||||||
|
|
||||||
p_out.initialize( track_count > 1 ? 2 : 0, 60 );
|
p_out.initialize( track_count > 1 ? 2 : 0, 60 );
|
||||||
|
|
||||||
for ( unsigned i = 0; i < track_count; ++i )
|
for ( unsigned i = 0; i < track_count; ++i )
|
||||||
{
|
{
|
||||||
const iff_chunk & xmid_form_chunk = cat_chunk.find_sub_chunk( "FORM", i );
|
const iff_chunk & xmid_form_chunk = cat_chunk.find_sub_chunk( "FORM", i );
|
||||||
if ( memcmp( xmid_form_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI nested FORM chunk not XMID type" );*/
|
if ( memcmp( xmid_form_chunk.m_type, "XMID", 4 ) ) return false; /*throw exception_io_data( "XMI nested FORM chunk not XMID type" );*/
|
||||||
|
|
||||||
const iff_chunk & event_chunk = xmid_form_chunk.find_sub_chunk( "EVNT" );
|
const iff_chunk & event_chunk = xmid_form_chunk.find_sub_chunk( "EVNT" );
|
||||||
if ( memcmp( event_chunk.m_id, "EVNT", 4 ) ) return false; /* EVNT chunk not found */
|
if ( memcmp( event_chunk.m_id, "EVNT", 4 ) ) return false; /* EVNT chunk not found */
|
||||||
std::vector<uint8_t> const& event_body = event_chunk.m_data;
|
std::vector<uint8_t> const& event_body = event_chunk.m_data;
|
||||||
|
|
||||||
midi_track track;
|
midi_track track;
|
||||||
|
|
||||||
bool initial_tempo = false;
|
bool initial_tempo = false;
|
||||||
|
|
||||||
unsigned current_timestamp = 0;
|
unsigned current_timestamp = 0;
|
||||||
|
|
||||||
unsigned last_event_timestamp = 0;
|
unsigned last_event_timestamp = 0;
|
||||||
|
|
||||||
std::vector<uint8_t> buffer;
|
std::vector<uint8_t> buffer;
|
||||||
buffer.resize( 3 );
|
buffer.resize( 3 );
|
||||||
|
@ -221,94 +221,94 @@ bool midi_processor::process_xmi( std::vector<uint8_t> const& p_file, midi_conta
|
||||||
std::vector<uint8_t>::const_iterator it = event_body.begin(), end = event_body.end();
|
std::vector<uint8_t>::const_iterator it = event_body.begin(), end = event_body.end();
|
||||||
|
|
||||||
while ( it != end )
|
while ( it != end )
|
||||||
{
|
{
|
||||||
unsigned delta = decode_xmi_delta( it, end );
|
unsigned delta = decode_xmi_delta( it, end );
|
||||||
current_timestamp += delta;
|
current_timestamp += delta;
|
||||||
|
|
||||||
if ( current_timestamp > last_event_timestamp )
|
if ( current_timestamp > last_event_timestamp )
|
||||||
{
|
{
|
||||||
last_event_timestamp = current_timestamp;
|
last_event_timestamp = current_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 0 ] = *it++;
|
buffer[ 0 ] = *it++;
|
||||||
if ( buffer[ 0 ] == 0xFF )
|
if ( buffer[ 0 ] == 0xFF )
|
||||||
{
|
{
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
long meta_count;
|
long meta_count;
|
||||||
if ( buffer[ 1 ] == 0x2F )
|
if ( buffer[ 1 ] == 0x2F )
|
||||||
{
|
{
|
||||||
meta_count = 0;
|
meta_count = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
meta_count = decode_delta( it, end );
|
meta_count = decode_delta( it, end );
|
||||||
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI meta message" );*/
|
if ( meta_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI meta message" );*/
|
||||||
if ( end - it < meta_count ) return false;
|
if ( end - it < meta_count ) return false;
|
||||||
buffer.resize( meta_count + 2 );
|
buffer.resize( meta_count + 2 );
|
||||||
std::copy( it, it + meta_count, buffer.begin() + 2 );
|
std::copy( it, it + meta_count, buffer.begin() + 2 );
|
||||||
it += meta_count;
|
it += meta_count;
|
||||||
}
|
}
|
||||||
if ( buffer[ 1 ] == 0x2F && current_timestamp < last_event_timestamp )
|
if ( buffer[ 1 ] == 0x2F && current_timestamp < last_event_timestamp )
|
||||||
{
|
{
|
||||||
current_timestamp = last_event_timestamp;
|
current_timestamp = last_event_timestamp;
|
||||||
}
|
}
|
||||||
if ( buffer[ 1 ] == 0x51 && meta_count == 3 )
|
if ( buffer[ 1 ] == 0x51 && meta_count == 3 )
|
||||||
{
|
{
|
||||||
unsigned tempo = buffer[ 2 ] * 0x10000 + buffer[ 3 ] * 0x100 + buffer[ 4 ];
|
unsigned tempo = buffer[ 2 ] * 0x10000 + buffer[ 3 ] * 0x100 + buffer[ 4 ];
|
||||||
unsigned ppqn = ( tempo * 3 ) / 25000;
|
unsigned ppqn = ( tempo * 3 ) / 25000;
|
||||||
tempo = tempo * 60 / ppqn;
|
tempo = tempo * 60 / ppqn;
|
||||||
buffer[ 2 ] = tempo / 0x10000;
|
buffer[ 2 ] = tempo / 0x10000;
|
||||||
buffer[ 3 ] = tempo / 0x100;
|
buffer[ 3 ] = tempo / 0x100;
|
||||||
buffer[ 4 ] = tempo;
|
buffer[ 4 ] = tempo;
|
||||||
if ( current_timestamp == 0 ) initial_tempo = true;
|
if ( current_timestamp == 0 ) initial_tempo = true;
|
||||||
}
|
}
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], meta_count + 2 ) );
|
||||||
if ( buffer[ 1 ] == 0x2F ) break;
|
if ( buffer[ 1 ] == 0x2F ) break;
|
||||||
}
|
}
|
||||||
else if ( buffer[ 0 ] == 0xF0 )
|
else if ( buffer[ 0 ] == 0xF0 )
|
||||||
{
|
{
|
||||||
long system_exclusive_count = decode_delta( it, end );
|
long system_exclusive_count = decode_delta( it, end );
|
||||||
if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI System Exclusive message" );*/
|
if ( system_exclusive_count < 0 ) return false; /*throw exception_io_data( "Invalid XMI System Exclusive message" );*/
|
||||||
if ( end - it < system_exclusive_count ) return false;
|
if ( end - it < system_exclusive_count ) return false;
|
||||||
buffer.resize( system_exclusive_count + 1 );
|
buffer.resize( system_exclusive_count + 1 );
|
||||||
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
|
std::copy( it, it + system_exclusive_count, buffer.begin() + 1 );
|
||||||
it += system_exclusive_count;
|
it += system_exclusive_count;
|
||||||
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
|
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], system_exclusive_count + 1 ) );
|
||||||
}
|
}
|
||||||
else if ( buffer[ 0 ] >= 0x80 && buffer[ 0 ] <= 0xEF )
|
else if ( buffer[ 0 ] >= 0x80 && buffer[ 0 ] <= 0xEF )
|
||||||
{
|
{
|
||||||
unsigned bytes_read = 1;
|
unsigned bytes_read = 1;
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 1 ] = *it++;
|
buffer[ 1 ] = *it++;
|
||||||
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
|
midi_event::event_type type = (midi_event::event_type)( ( buffer[ 0 ] >> 4 ) - 8 );
|
||||||
unsigned channel = buffer[ 0 ] & 0x0F;
|
unsigned channel = buffer[ 0 ] & 0x0F;
|
||||||
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
|
if ( type != midi_event::program_change && type != midi_event::channel_aftertouch )
|
||||||
{
|
{
|
||||||
if ( it == end ) return false;
|
if ( it == end ) return false;
|
||||||
buffer[ 2 ] = *it++;
|
buffer[ 2 ] = *it++;
|
||||||
bytes_read = 2;
|
bytes_read = 2;
|
||||||
}
|
}
|
||||||
track.add_event( midi_event( current_timestamp, type, channel, &buffer[1], bytes_read ) );
|
track.add_event( midi_event( current_timestamp, type, channel, &buffer[1], bytes_read ) );
|
||||||
if ( type == midi_event::note_on )
|
if ( type == midi_event::note_on )
|
||||||
{
|
{
|
||||||
buffer[ 2 ] = 0x00;
|
buffer[ 2 ] = 0x00;
|
||||||
int note_length = decode_delta( it, end );
|
int note_length = decode_delta( it, end );
|
||||||
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid XMI note message" );*/
|
if ( note_length < 0 ) return false; /*throw exception_io_data( "Invalid XMI note message" );*/
|
||||||
unsigned note_end_timestamp = current_timestamp + note_length;
|
unsigned note_end_timestamp = current_timestamp + note_length;
|
||||||
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
|
if ( note_end_timestamp > last_event_timestamp ) last_event_timestamp = note_end_timestamp;
|
||||||
track.add_event( midi_event( note_end_timestamp, type, channel, &buffer[1], bytes_read ) );
|
track.add_event( midi_event( note_end_timestamp, type, channel, &buffer[1], bytes_read ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else return false; /*throw exception_io_data( "Unexpected XMI status code" );*/
|
else return false; /*throw exception_io_data( "Unexpected XMI status code" );*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !initial_tempo )
|
if ( !initial_tempo )
|
||||||
track.add_event( midi_event( 0, midi_event::extended, 0, xmi_default_tempo, _countof( xmi_default_tempo ) ) );
|
track.add_event( midi_event( 0, midi_event::extended, 0, xmi_default_tempo, _countof( xmi_default_tempo ) ) );
|
||||||
|
|
||||||
p_out.add_track( track );
|
p_out.add_track( track );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ static OSType getOSType(const char * in_)
|
||||||
|
|
||||||
track_num = [[[s url] fragment] intValue]; //What if theres no fragment? Assuming we get 0.
|
track_num = [[[s url] fragment] intValue]; //What if theres no fragment? Assuming we get 0.
|
||||||
|
|
||||||
midi_file.scan_for_loops( true, true, true );
|
midi_file.scan_for_loops( true, true, true, true );
|
||||||
|
|
||||||
framesLength = midi_file.get_timestamp_end( track_num, true );
|
framesLength = midi_file.get_timestamp_end( track_num, true );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue