cog/Frameworks/midi_processing/midi_processing/midi_processor_standard_mid...

249 lines
9.2 KiB
C++

#include "midi_processor.h"
bool midi_processor::is_standard_midi( std::vector<uint8_t> const& p_file )
{
if ( p_file.size() < 18 ) return false;
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd') return false;
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false;
if ( p_file[ 10 ] == 0 && p_file[ 11 ] == 0 ) return false; // no tracks
if ( p_file[ 12 ] == 0 && p_file[ 13 ] == 0 ) return false; // dtx == 0, will cause division by zero on tempo calculations
return true;
}
bool midi_processor::process_standard_midi_count( std::vector<uint8_t> const& p_file, size_t & track_count )
{
track_count = 0;
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd' ) return false;
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
uint16_t form = ( it[0] << 8 ) | it[1];
if ( form > 2 ) return false;
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
if ( form == 2 ) track_count = track_count_16;
else track_count = 1;
return true;
}
void 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 is_gmf )
{
bool needs_end_marker = false;
midi_track track;
unsigned current_timestamp = 0;
unsigned last_event_timestamp = 0;
unsigned char last_event_code = 0xFF;
unsigned last_sysex_length = 0;
unsigned last_sysex_timestamp = 0;
std::vector<uint8_t> buffer;
buffer.resize( 3 );
for (;;)
{
if ( !needs_end_marker && it == end ) break;
int delta = decode_delta( it, end );
if ( !needs_end_marker && it == end ) break;
if ( delta < 0 )
{
/*"MIDI processor encountered negative delta: " << delta << "; flipping sign.";*/
delta = -delta;
}
if ( delta > 1000000 )
break;
current_timestamp += delta;
if ( it == end ) break;
unsigned char event_code = *it++;
unsigned data_bytes_read = 0;
if ( event_code < 0x80 )
{
if ( last_event_code == 0xFF ) break; /*throw exception_io_data("First MIDI track event short encoded");*/
buffer[ data_bytes_read++ ] = event_code;
event_code = last_event_code;
}
if ( event_code < 0xF0 )
{
if ( last_sysex_length )
{
track.add_event( midi_event( last_event_timestamp = last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
last_sysex_length = 0;
}
last_event_code = event_code;
if ( is_gmf && ( event_code & 0xF0 ) == 0xE0 ) continue;
if ( data_bytes_read < 1 )
{
if ( it == end ) break;
buffer[ 0 ] = *it++;
++data_bytes_read;
}
bool command_valid = true;
switch ( event_code & 0xF0 )
{
case 0xC0:
case 0xD0:
break;
default:
if ( it == end )
{
command_valid = false;
break;
}
buffer[ data_bytes_read ] = *it++;
++data_bytes_read;
}
if ( !command_valid ) break;
track.add_event( midi_event( last_event_timestamp = current_timestamp, (midi_event::event_type)(( event_code >> 4 ) - 8), event_code & 0x0F, &buffer[0], data_bytes_read ) );
}
else if ( event_code == 0xF0 )
{
if ( last_sysex_length )
{
track.add_event( midi_event( last_event_timestamp = last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
last_sysex_length = 0;
}
if ( it == end ) break;
int data_count = decode_delta( it, end );
if ( data_count < 0 ) break; /*throw exception_io_data( "Invalid System Exclusive message" );*/
if ( end - it < data_count ) break;
buffer.resize( data_count + 1 );
buffer[ 0 ] = 0xF0;
std::copy( it, it + data_count, buffer.begin() + 1 );
it += data_count;
last_sysex_length = data_count + 1;
last_sysex_timestamp = current_timestamp;
}
else if ( event_code == 0xF7 ) // SysEx continuation
{
if ( !last_sysex_length ) break;
if ( it == end ) break;
int data_count = decode_delta( it, end );
if ( data_count < 0 ) break;
if ( end - it < data_count ) break;
buffer.resize( last_sysex_length + data_count );
std::copy( it, it + data_count, buffer.begin() + last_sysex_length );
it += data_count;
last_sysex_length += data_count;
}
else if ( event_code == 0xFF )
{
if ( last_sysex_length )
{
track.add_event( midi_event( last_event_timestamp = last_sysex_timestamp, midi_event::extended, 0, &buffer[0], last_sysex_length ) );
last_sysex_length = 0;
}
if ( it == end ) break;
unsigned char meta_type = *it++;
if ( it == end ) break;
int data_count = decode_delta( it, end );
if ( data_count < 0 ) break; /*throw exception_io_data( "Invalid meta message" );*/
if ( end - it < data_count ) break;
buffer.resize( data_count + 2 );
buffer[ 0 ] = 0xFF;
buffer[ 1 ] = meta_type;
std::copy( it, it + data_count, buffer.begin() + 2 );
it += data_count;
if ( meta_type == 0x2F )
current_timestamp = last_event_timestamp;
else
last_event_timestamp = current_timestamp;
track.add_event( midi_event( current_timestamp, midi_event::extended, 0, &buffer[0], data_count + 2 ) );
if ( meta_type == 0x2F )
{
needs_end_marker = true;
break;
}
}
else if ( event_code >= 0xF8 && event_code <= 0xFE )
{
/* Sequencer specific events, single byte */
buffer[ 0 ] = event_code;
track.add_event( midi_event( last_event_timestamp = current_timestamp, midi_event::extended, 0, &buffer[0], 1 ) );
}
else break; /*throw exception_io_data("Unhandled MIDI status code");*/
}
if ( !needs_end_marker )
{
buffer[ 0 ] = 0xFF;
buffer[ 1 ] = 0x2F;
track.add_event( midi_event( last_event_timestamp, midi_event::extended, 0, &buffer[0], 2 ) );
}
p_out.add_track( track );
}
bool midi_processor::process_standard_midi( std::vector<uint8_t> const& p_file, midi_container & p_out )
{
if ( p_file[ 0 ] != 'M' || p_file[ 1 ] != 'T' || p_file[ 2 ] != 'h' || p_file[ 3 ] != 'd' ) return false;
if ( p_file[ 4 ] != 0 || p_file[ 5 ] != 0 || p_file[ 6 ] != 0 || p_file[ 7 ] != 6 ) return false; /*throw exception_io_data("Bad MIDI header size");*/
std::vector<uint8_t>::const_iterator it = p_file.begin() + 8;
std::vector<uint8_t>::const_iterator end = p_file.end();
uint16_t form = ( it[0] << 8 ) | it[1];
if ( form > 2 ) return false;
uint16_t track_count_16 = ( it[2] << 8 ) | it[3];
uint16_t dtx = ( it[4] << 8 ) | it[5];
if ( !track_count_16 || !dtx )
return false;
it += 6;
std::size_t track_count = track_count_16;
p_out.initialize( form, dtx );
for ( std::size_t i = 0; i < track_count; ++i )
{
if ( end - it < 8 ) break;
while ( it[0] != 'M' || it[1] != 'T' || it[2] != 'r' || it[3] != 'k' ) {
uint32_t chunk_size = ( it[4] << 24 ) | ( it[5] << 16 ) | ( it[6] << 8 ) | it[7];
if ( (unsigned long)(end - it) < 8 + chunk_size )
break;
it += 8 + chunk_size;
if ( end - it < 8 ) break;
}
if ( end - it < 8 ) break;
uint32_t track_size = ( it[4] << 24 ) | ( it[5] << 16 ) | ( it[6] << 8 ) | it[7];
it += 8;
intptr_t track_data_offset = it - p_file.begin();
if ( (unsigned long)(end - it) < track_size )
track_size = (uint32_t)(end - it);
process_standard_midi_track( it, it + track_size, p_out, false );
track_data_offset += track_size;
size_t messup_offset = it - p_file.begin();
if ( messup_offset != track_data_offset )
{
/* Assume messed up track, attempt to skip it. */
if ( end - p_file.begin() >= track_data_offset )
it = p_file.begin() + track_data_offset;
else
break;
}
}
return !!p_out.get_track_count();
}