cog/Frameworks/midi_processing/midi_processing/midi_processor_hmi.cpp

229 lines
10 KiB
C++

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