2014-06-01 17:55:23 +00:00
/*
2018-09-02 00:57:29 +00:00
* This file is part of the DXX - Rebirth project < https : //www.dxx-rebirth.com/>.
2014-06-01 17:55:23 +00:00
* It is copyright by its individual contributors , as recorded in the
* project ' s Git history . See COPYING . txt at the top level for license
* terms and a link to the Git history .
*/
2010-07-18 20:28:15 +00:00
/*
* This code handles HMP files . It can :
* - Open / read / close HMP files
* - Play HMP via Windows MIDI
* - Convert HMP to MIDI for further use
* Based on work of Arne de Bruijn and the JFFEE project
*/
2014-08-26 03:19:59 +00:00
# include <stdexcept>
2010-07-18 20:28:15 +00:00
# include <string.h>
2006-03-20 16:43:15 +00:00
# include <stdlib.h>
2010-07-18 20:28:15 +00:00
# include <stdio.h>
2010-09-17 10:44:21 +00:00
2010-07-18 20:28:15 +00:00
# include "hmp.h"
# include "u_mem.h"
2011-07-15 08:43:01 +00:00
# include "console.h"
2012-04-12 17:45:44 +00:00
# include "timer.h"
2014-09-20 23:08:28 +00:00
# include "serial.h"
2010-07-18 20:28:15 +00:00
2014-08-16 04:15:16 +00:00
# include "dxxsconf.h"
2016-03-19 19:08:10 +00:00
# include "dsx-ns.h"
2014-08-16 04:15:16 +00:00
# include "compiler-make_unique.h"
2015-04-02 02:36:52 +00:00
# include "compiler-range_for.h"
2019-05-17 02:13:22 +00:00
# include "d_range.h"
2015-04-02 02:36:52 +00:00
# include "partial_range.h"
2014-08-16 04:15:16 +00:00
2015-12-13 18:00:49 +00:00
namespace dcx {
2015-12-05 22:57:24 +00:00
2015-06-13 22:42:21 +00:00
# define MIDIINT(x) (words_bigendian ? (x) : (SWAPINT(x)))
# define MIDISHORT(x) (words_bigendian ? (x) : (SWAPSHORT(x)))
2010-07-19 17:05:41 +00:00
# ifdef _WIN32
2015-04-02 02:36:52 +00:00
static int midi_volume ;
static int channel_volume [ 16 ] ;
static void hmp_stop ( hmp_file * hmp ) ;
2010-07-19 17:05:41 +00:00
# endif
2010-07-18 20:28:15 +00:00
// READ/OPEN/CLOSE HMP
2010-07-19 17:05:41 +00:00
2014-08-05 02:29:43 +00:00
hmp_file : : ~ hmp_file ( )
2010-07-18 20:28:15 +00:00
{
# ifdef _WIN32
2014-08-05 02:29:43 +00:00
hmp_stop ( this ) ;
2010-07-18 20:28:15 +00:00
# endif
}
2006-03-20 16:43:15 +00:00
2014-08-05 02:29:43 +00:00
std : : unique_ptr < hmp_file > hmp_open ( const char * filename ) {
2016-10-29 23:16:17 +00:00
int data , tempo ;
2015-01-17 18:31:42 +00:00
auto fp = PHYSFSX_openReadBuffered ( filename ) ;
2006-03-20 16:43:15 +00:00
2015-01-17 18:31:42 +00:00
if ( ! fp )
2006-03-20 16:43:15 +00:00
return NULL ;
2014-08-05 02:29:43 +00:00
std : : unique_ptr < hmp_file > hmp ( new hmp_file { } ) ;
2015-04-19 04:18:49 +00:00
char buf [ 8 ] ;
2011-06-01 07:59:51 +00:00
if ( ( PHYSFS_read ( fp , buf , 1 , 8 ) ! = 8 ) | | ( memcmp ( buf , " HMIMIDIP " , 8 ) ) )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2006-03-20 16:43:15 +00:00
2011-06-01 07:59:51 +00:00
if ( PHYSFSX_fseek ( fp , 0x30 , SEEK_SET ) )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2006-03-20 16:43:15 +00:00
2016-10-29 23:16:17 +00:00
unsigned num_tracks ;
2011-06-01 07:59:51 +00:00
if ( PHYSFS_read ( fp , & num_tracks , 4 , 1 ) ! = 1 )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2006-03-20 16:43:15 +00:00
if ( ( num_tracks < 1 ) | | ( num_tracks > HMP_TRACKS ) )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2006-03-20 16:43:15 +00:00
hmp - > num_trks = num_tracks ;
2013-06-10 14:34:45 +00:00
if ( PHYSFSX_fseek ( fp , 0x38 , SEEK_SET ) )
{
return NULL ;
}
if ( PHYSFS_read ( fp , & tempo , 4 , 1 ) ! = 1 )
{
return NULL ;
}
2013-06-27 10:08:51 +00:00
hmp - > tempo = INTEL_INT ( tempo ) ;
2006-03-20 16:43:15 +00:00
2011-06-01 07:59:51 +00:00
if ( PHYSFSX_fseek ( fp , 0x308 , SEEK_SET ) )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2006-03-20 16:43:15 +00:00
2016-10-29 23:16:17 +00:00
range_for ( auto & i , partial_range ( hmp - > trks , num_tracks ) )
{
2011-06-01 07:59:51 +00:00
if ( ( PHYSFSX_fseek ( fp , 4 , SEEK_CUR ) ) | | ( PHYSFS_read ( fp , & data , 4 , 1 ) ! = 1 ) )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2006-03-20 16:43:15 +00:00
data - = 12 ;
2016-10-29 23:16:17 +00:00
i . len = data ;
i . data = make_unique < uint8_t [ ] > ( data ) ;
2010-07-18 20:28:15 +00:00
/* finally, read track data */
2016-10-29 23:16:17 +00:00
if ( ( PHYSFSX_fseek ( fp , 4 , SEEK_CUR ) ) | | ( PHYSFS_read ( fp , i . data . get ( ) , data , 1 ) ! = 1 ) )
2010-07-18 20:28:15 +00:00
{
return NULL ;
}
2016-10-29 23:16:17 +00:00
i . loop_set = 0 ;
2010-07-18 20:28:15 +00:00
}
2010-11-30 11:19:41 +00:00
hmp - > filesize = PHYSFS_fileLength ( fp ) ;
2010-07-18 20:28:15 +00:00
return hmp ;
2006-03-20 16:43:15 +00:00
}
2010-11-28 15:49:32 +00:00
# ifdef _WIN32
2010-07-18 20:28:15 +00:00
// PLAY HMP AS MIDI
void hmp_stop ( hmp_file * hmp )
{
2006-03-20 16:43:15 +00:00
MIDIHDR * mhdr ;
if ( ! hmp - > stop ) {
hmp - > stop = 1 ;
//PumpMessages();
midiStreamStop ( hmp - > hmidi ) ;
2010-07-18 20:28:15 +00:00
while ( hmp - > bufs_in_mm )
2012-04-12 17:45:44 +00:00
timer_delay ( 1 ) ;
2006-03-20 16:43:15 +00:00
}
while ( ( mhdr = hmp - > evbuf ) ) {
2016-07-08 04:14:59 +00:00
midiOutUnprepareHeader ( reinterpret_cast < HMIDIOUT > ( hmp - > hmidi ) , mhdr , sizeof ( MIDIHDR ) ) ;
2006-03-20 16:43:15 +00:00
hmp - > evbuf = mhdr - > lpNext ;
2008-01-23 17:25:09 +00:00
d_free ( mhdr ) ;
2006-03-20 16:43:15 +00:00
}
if ( hmp - > hmidi ) {
midiStreamClose ( hmp - > hmidi ) ;
hmp - > hmidi = NULL ;
}
}
/*
2010-11-28 15:49:32 +00:00
* read a HMI type variable length number
2006-03-20 16:43:15 +00:00
*/
static int get_var_num_hmi ( unsigned char * data , int datalen , unsigned long * value ) {
unsigned char * p ;
unsigned long v = 0 ;
int shift = 0 ;
p = data ;
while ( ( datalen > 0 ) & & ! ( * p & 0x80 ) ) {
v + = * ( p + + ) < < shift ;
shift + = 7 ;
datalen - - ;
2010-07-18 20:28:15 +00:00
}
2006-03-20 16:43:15 +00:00
if ( ! datalen )
return 0 ;
2010-07-18 20:28:15 +00:00
v + = ( * ( p + + ) & 0x7f ) < < shift ;
2006-03-20 16:43:15 +00:00
if ( value ) * value = v ;
2010-07-18 20:28:15 +00:00
return p - data ;
2006-03-20 16:43:15 +00:00
}
/*
2010-11-28 15:49:32 +00:00
* read a MIDI type variable length number
2006-03-20 16:43:15 +00:00
*/
static int get_var_num ( unsigned char * data , int datalen ,
2016-09-04 19:10:43 +00:00
unsigned * value ) {
2006-03-20 16:43:15 +00:00
unsigned char * orgdata = data ;
unsigned long v = 0 ;
while ( ( datalen > 0 ) & & ( * data & 0x80 ) )
v = ( v < < 7 ) + ( * ( data + + ) & 0x7f ) ;
if ( ! datalen )
return 0 ;
2010-07-18 20:28:15 +00:00
v = ( v < < 7 ) + * ( data + + ) ;
if ( value ) * value = v ;
return data - orgdata ;
2006-03-20 16:43:15 +00:00
}
static int get_event ( hmp_file * hmp , event * ev ) {
2015-04-19 04:18:49 +00:00
static const array < int , 7 > cmdlen { { 3 , 3 , 3 , 3 , 2 , 2 , 3 } } ;
2006-03-20 16:43:15 +00:00
unsigned long got ;
unsigned long mindelta , delta ;
2015-04-02 02:36:52 +00:00
int ev_num ;
2016-06-01 01:52:45 +00:00
hmp_track * fndtrk = nullptr ;
2006-03-20 16:43:15 +00:00
mindelta = INT_MAX ;
2016-02-17 03:34:30 +00:00
range_for ( auto & rtrk , partial_range ( hmp - > trks , static_cast < unsigned > ( hmp - > num_trks ) ) )
2015-04-02 02:36:52 +00:00
{
const auto trk = & rtrk ;
2011-07-15 08:43:01 +00:00
if ( ! trk - > left | | ( hmp - > loop_start & & hmp - > looping & & ! trk - > loop_set ) )
2006-03-20 16:43:15 +00:00
continue ;
if ( ! ( got = get_var_num_hmi ( trk - > cur , trk - > left , & delta ) ) )
return HMP_INVALID_FILE ;
if ( trk - > left > got + 2 & & * ( trk - > cur + got ) = = 0xff
& & * ( trk - > cur + got + 1 ) = = 0x2f ) { /* end of track */
trk - > left = 0 ;
continue ;
}
2010-11-28 15:49:32 +00:00
if ( hmp - > loop_start & & hmp - > looping )
if ( trk - > cur = = trk - > loop )
2011-01-09 16:51:45 +00:00
delta = trk - > loop_start - hmp - > loop_start ;
2010-11-28 15:49:32 +00:00
2006-03-20 16:43:15 +00:00
delta + = trk - > cur_time - hmp - > cur_time ;
if ( delta < mindelta ) {
mindelta = delta ;
fndtrk = trk ;
}
}
2016-06-01 01:52:45 +00:00
const auto trk = fndtrk ;
if ( ! trk )
2006-03-20 16:43:15 +00:00
return HMP_EOF ;
got = get_var_num_hmi ( trk - > cur , trk - > left , & delta ) ;
2010-11-28 15:49:32 +00:00
if ( hmp - > loop_start & & hmp - > looping )
if ( trk - > cur = = trk - > loop )
2011-01-09 16:51:45 +00:00
delta = trk - > loop_start - hmp - > loop_start ;
2010-11-28 15:49:32 +00:00
2006-03-20 16:43:15 +00:00
trk - > cur_time + = delta ;
2010-11-28 15:49:32 +00:00
if ( ! hmp - > loop_start & & * ( trk - > cur + got ) > > 4 = = MIDI_CONTROL_CHANGE & & * ( trk - > cur + got + 1 ) = = HMP_LOOP_START )
2010-11-30 11:19:41 +00:00
{
hmp - > loop_start = trk - > cur_time ;
if ( ( hmp - > filesize = = 86949 ) & & ( trk - > cur_time = = 29 ) ) // special ugly HACK HACK HACK for Descent2-version of descent.hmp where loop at this point causes instruments not being reset properly. No track supporting HMP loop I know of meets the requirements for the condition below and even if so - it only disabled the HMP loop feature.
{
hmp - > loop_start = 0 ;
}
}
2010-11-28 15:49:32 +00:00
if ( ! hmp - > loop_end & & * ( trk - > cur + got ) > > 4 = = MIDI_CONTROL_CHANGE & & * ( trk - > cur + got + 1 ) = = HMP_LOOP_END )
hmp - > loop_end = trk - > cur_time ;
2011-01-09 16:51:45 +00:00
if ( hmp - > loop_start & & ! trk - > loop_set & & trk - > cur_time > = hmp - > loop_start )
2010-11-28 15:49:32 +00:00
{
2011-01-09 16:51:45 +00:00
trk - > loop_start = trk - > cur_time ;
2010-11-28 15:49:32 +00:00
trk - > loop = trk - > cur ;
trk - > len = trk - > left ;
trk - > loop_set = 1 ;
}
if ( hmp - > loop_end & & trk - > cur_time > hmp - > loop_end )
return HMP_EOF ;
2006-03-20 16:43:15 +00:00
ev - > delta = trk - > cur_time - hmp - > cur_time ;
hmp - > cur_time = trk - > cur_time ;
if ( ( trk - > left - = got ) < 3 )
2010-07-18 20:28:15 +00:00
return HMP_INVALID_FILE ;
2006-03-20 16:43:15 +00:00
trk - > cur + = got ;
/*memset(ev, 0, sizeof(*ev));*/ ev - > datalen = 0 ;
ev - > msg [ 0 ] = ev_num = * ( trk - > cur + + ) ;
trk - > left - - ;
if ( ev_num < 0x80 )
2010-07-18 20:28:15 +00:00
return HMP_INVALID_FILE ; /* invalid command */
2006-03-20 16:43:15 +00:00
if ( ev_num < 0xf0 ) {
ev - > msg [ 1 ] = * ( trk - > cur + + ) ;
trk - > left - - ;
if ( cmdlen [ ( ( ev_num ) > > 4 ) - 8 ] = = 3 ) {
ev - > msg [ 2 ] = * ( trk - > cur + + ) ;
trk - > left - - ;
2010-11-28 15:49:32 +00:00
if ( ev - > msg [ 0 ] > > 4 = = MIDI_CONTROL_CHANGE & & ev - > msg [ 1 ] = = MIDI_VOLUME )
{
channel_volume [ ev - > msg [ 0 ] & 0xf ] = ev - > msg [ 2 ] ;
ev - > msg [ 2 ] = ev - > msg [ 2 ] * midi_volume / MIDI_VOLUME_SCALE ;
}
2006-03-20 16:43:15 +00:00
}
} else if ( ev_num = = 0xff ) {
ev - > msg [ 1 ] = * ( trk - > cur + + ) ;
trk - > left - - ;
if ( ! ( got = get_var_num ( ev - > data = trk - > cur ,
2016-09-04 19:10:43 +00:00
trk - > left , & ev - > datalen ) ) )
2006-03-20 16:43:15 +00:00
return HMP_INVALID_FILE ;
2010-07-18 20:28:15 +00:00
trk - > cur + = ev - > datalen ;
2006-03-20 16:43:15 +00:00
if ( trk - > left < = ev - > datalen )
return HMP_INVALID_FILE ;
trk - > left - = ev - > datalen ;
} else /* sysex -> error */
return HMP_INVALID_FILE ;
return 0 ;
}
static int fill_buffer ( hmp_file * hmp ) {
MIDIHDR * mhdr = hmp - > evbuf ;
2016-07-07 03:08:13 +00:00
unsigned int * p = reinterpret_cast < unsigned int * > ( mhdr - > lpData + mhdr - > dwBytesRecorded ) ;
unsigned int * pend = reinterpret_cast < unsigned int * > ( mhdr - > lpData + mhdr - > dwBufferLength ) ;
2006-03-20 16:43:15 +00:00
unsigned int i ;
2014-07-04 03:43:38 +00:00
event ev { } ;
2011-07-15 08:43:01 +00:00
2006-03-20 16:43:15 +00:00
while ( p + 4 < = pend ) {
if ( hmp - > pending_size ) {
i = ( p - pend ) * 4 ;
if ( i > hmp - > pending_size )
i = hmp - > pending_size ;
* ( p + + ) = hmp - > pending_event | i ;
* ( p + + ) = 0 ;
2016-07-03 00:54:14 +00:00
memcpy ( reinterpret_cast < uint8_t * > ( p ) , hmp - > pending , i ) ;
2006-03-20 16:43:15 +00:00
hmp - > pending_size - = i ;
p + = ( i + 3 ) / 4 ;
} else {
if ( ( i = get_event ( hmp , & ev ) ) ) {
2016-07-03 00:54:14 +00:00
mhdr - > dwBytesRecorded = ( reinterpret_cast < uint8_t * > ( p ) ) - ( reinterpret_cast < uint8_t * > ( mhdr - > lpData ) ) ;
2006-03-20 16:43:15 +00:00
return i ;
}
if ( ev . datalen ) {
hmp - > pending_size = ev . datalen ;
hmp - > pending = ev . data ;
hmp - > pending_event = ev . msg [ 0 ] < < 24 ;
} else {
* ( p + + ) = ev . delta ;
* ( p + + ) = 0 ;
2016-09-04 19:10:43 +00:00
* ( p + + ) = ( static_cast < DWORD > ( MEVT_SHORTMSG ) < < 24 ) |
static_cast < DWORD > ( ev . msg [ 0 ] ) |
( static_cast < DWORD > ( ev . msg [ 1 ] ) < < 8 ) |
( static_cast < DWORD > ( ev . msg [ 2 ] ) < < 16 ) ;
2006-03-20 16:43:15 +00:00
}
}
}
2016-07-03 00:54:14 +00:00
mhdr - > dwBytesRecorded = ( reinterpret_cast < uint8_t * > ( p ) ) - ( reinterpret_cast < uint8_t * > ( mhdr - > lpData ) ) ;
2006-03-20 16:43:15 +00:00
return 0 ;
}
static int setup_buffers ( hmp_file * hmp ) {
MIDIHDR * buf , * lastbuf ;
lastbuf = NULL ;
2014-09-26 02:42:14 +00:00
for ( int i = 0 ; i < HMP_BUFFERS ; i + + ) {
2016-09-04 19:10:43 +00:00
if ( ! ( buf = reinterpret_cast < MIDIHDR * > ( d_malloc ( HMP_BUFSIZE + sizeof ( MIDIHDR ) ) ) ) )
2006-03-20 16:43:15 +00:00
return HMP_OUT_OF_MEM ;
memset ( buf , 0 , sizeof ( MIDIHDR ) ) ;
2016-06-05 01:04:26 +00:00
buf - > lpData = reinterpret_cast < char * > ( buf ) + sizeof ( MIDIHDR ) ;
2006-03-20 16:43:15 +00:00
buf - > dwBufferLength = HMP_BUFSIZE ;
2013-10-27 00:55:49 +00:00
buf - > dwUser = reinterpret_cast < uintptr_t > ( hmp ) ;
2006-03-20 16:43:15 +00:00
buf - > lpNext = lastbuf ;
lastbuf = buf ;
}
hmp - > evbuf = lastbuf ;
return 0 ;
}
static void reset_tracks ( struct hmp_file * hmp )
{
2016-10-29 23:16:18 +00:00
if ( hmp - > num_trks > 0 )
range_for ( auto & i , partial_range ( hmp - > trks , static_cast < unsigned > ( hmp - > num_trks ) ) )
{
if ( i . loop_set )
i . cur = i . loop ;
2010-11-28 15:49:32 +00:00
else
2016-10-29 23:16:18 +00:00
i . cur = i . data . get ( ) ;
i . left = i . len ;
i . cur_time = 0 ;
2006-03-20 16:43:15 +00:00
}
2010-11-28 15:49:32 +00:00
hmp - > cur_time = 0 ;
2006-03-20 16:43:15 +00:00
}
2015-04-26 20:15:56 +00:00
static void _stdcall midi_callback ( HMIDISTRM , UINT uMsg , DWORD , DWORD_PTR dw1 , DWORD )
2013-10-27 00:55:49 +00:00
{
2006-03-20 16:43:15 +00:00
hmp_file * hmp ;
int rc ;
if ( uMsg ! = MOM_DONE )
return ;
2016-07-06 01:54:24 +00:00
const auto mhdr = reinterpret_cast < MIDIHDR * > ( dw1 ) ;
2006-03-20 16:43:15 +00:00
mhdr - > dwBytesRecorded = 0 ;
2016-07-03 00:54:15 +00:00
hmp = reinterpret_cast < hmp_file * > ( mhdr - > dwUser ) ;
2006-03-20 16:43:15 +00:00
mhdr - > lpNext = hmp - > evbuf ;
hmp - > evbuf = mhdr ;
hmp - > bufs_in_mm - - ;
if ( ! hmp - > stop ) {
while ( fill_buffer ( hmp ) = = HMP_EOF ) {
2008-05-01 21:40:34 +00:00
if ( hmp - > bLoop )
2006-03-20 16:43:15 +00:00
hmp - > stop = 0 ;
2006-05-05 14:14:55 +00:00
else
hmp - > stop = 1 ;
2010-11-28 15:49:32 +00:00
hmp - > looping = 1 ;
2006-03-20 16:43:15 +00:00
reset_tracks ( hmp ) ;
}
if ( ( rc = midiStreamOut ( hmp - > hmidi , hmp - > evbuf ,
sizeof ( MIDIHDR ) ) ) ! = MMSYSERR_NOERROR ) {
/* ??? */
} else {
hmp - > evbuf = hmp - > evbuf - > lpNext ;
hmp - > bufs_in_mm + + ;
}
}
}
static void setup_tempo ( hmp_file * hmp , unsigned long tempo ) {
MIDIHDR * mhdr = hmp - > evbuf ;
2016-07-07 03:08:13 +00:00
unsigned int * p = reinterpret_cast < unsigned int * > ( mhdr - > lpData + mhdr - > dwBytesRecorded ) ;
2006-03-20 16:43:15 +00:00
* ( p + + ) = 0 ;
* ( p + + ) = 0 ;
2016-07-08 04:14:59 +00:00
* ( p + + ) = ( ( static_cast < DWORD > ( MEVT_TEMPO ) ) < < 24 ) | tempo ;
2006-03-20 16:43:15 +00:00
mhdr - > dwBytesRecorded + = 12 ;
}
2010-11-28 15:49:32 +00:00
void hmp_setvolume ( hmp_file * hmp , int volume )
{
if ( hmp )
2019-05-04 18:27:36 +00:00
range_for ( const int channel , xrange ( 16u ) )
2016-07-08 04:14:59 +00:00
midiOutShortMsg ( reinterpret_cast < HMIDIOUT > ( hmp - > hmidi ) , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_VOLUME < < 8 | ( channel_volume [ channel ] * volume / MIDI_VOLUME_SCALE ) < < 16 ) ) ;
2010-11-28 15:49:32 +00:00
midi_volume = volume ;
}
2006-03-20 16:43:15 +00:00
int hmp_play ( hmp_file * hmp , int bLoop )
{
int rc ;
MIDIPROPTIMEDIV mptd ;
2010-07-18 20:28:15 +00:00
2010-11-28 15:49:32 +00:00
hmp - > bLoop = bLoop ;
hmp - > loop_start = 0 ;
hmp - > loop_end = 0 ;
hmp - > looping = 0 ;
2006-03-20 16:43:15 +00:00
hmp - > devid = MIDI_MAPPER ;
if ( ( rc = setup_buffers ( hmp ) ) )
return rc ;
2016-07-02 02:04:11 +00:00
if ( ( midiStreamOpen ( & hmp - > hmidi , & hmp - > devid , 1 , static_cast < DWORD > ( reinterpret_cast < uintptr_t > ( & midi_callback ) ) , 0 , CALLBACK_FUNCTION ) ) ! = MMSYSERR_NOERROR )
{
2006-03-20 16:43:15 +00:00
hmp - > hmidi = NULL ;
return HMP_MM_ERR ;
}
mptd . cbStruct = sizeof ( mptd ) ;
mptd . dwTimeDiv = hmp - > tempo ;
2016-07-02 02:04:11 +00:00
if ( ( midiStreamProperty ( hmp - > hmidi , reinterpret_cast < BYTE * > ( & mptd ) , MIDIPROP_SET | MIDIPROP_TIMEDIV ) ) ! = MMSYSERR_NOERROR )
{
2006-03-20 16:43:15 +00:00
return HMP_MM_ERR ;
}
reset_tracks ( hmp ) ;
setup_tempo ( hmp , 0x0f4240 ) ;
hmp - > stop = 0 ;
while ( hmp - > evbuf ) {
if ( ( rc = fill_buffer ( hmp ) ) ) {
if ( rc = = HMP_EOF ) {
reset_tracks ( hmp ) ;
continue ;
} else
return rc ;
}
2016-07-08 04:14:59 +00:00
if ( ( rc = midiOutPrepareHeader ( reinterpret_cast < HMIDIOUT > ( hmp - > hmidi ) , hmp - > evbuf ,
2006-03-20 16:43:15 +00:00
sizeof ( MIDIHDR ) ) ) ! = MMSYSERR_NOERROR ) {
return HMP_MM_ERR ;
}
if ( ( rc = midiStreamOut ( hmp - > hmidi , hmp - > evbuf ,
sizeof ( MIDIHDR ) ) ) ! = MMSYSERR_NOERROR ) {
return HMP_MM_ERR ;
}
hmp - > evbuf = hmp - > evbuf - > lpNext ;
hmp - > bufs_in_mm + + ;
}
midiStreamRestart ( hmp - > hmidi ) ;
return 0 ;
}
2010-11-28 15:49:32 +00:00
void hmp_pause ( hmp_file * hmp )
{
2011-04-15 23:15:55 +00:00
if ( hmp )
midiStreamPause ( hmp - > hmidi ) ;
2010-11-28 15:49:32 +00:00
}
void hmp_resume ( hmp_file * hmp )
{
2011-04-15 23:15:55 +00:00
if ( hmp )
midiStreamRestart ( hmp - > hmidi ) ;
2010-11-28 15:49:32 +00:00
}
void hmp_reset ( )
{
HMIDIOUT hmidi ;
MIDIHDR mhdr ;
2011-07-15 08:43:01 +00:00
MMRESULT rval ;
char GS_Reset [ ] = { 0xF0 , 0x41 , 0x10 , 0x42 , 0x12 , 0x40 , 0x00 , 0x7F , 0x00 , 0x41 , 0xF7 } ;
2010-11-28 15:49:32 +00:00
2011-07-15 08:43:01 +00:00
if ( ( rval = midiOutOpen ( & hmidi , MIDI_MAPPER , 0 , 0 , 0 ) ) ! = MMSYSERR_NOERROR )
{
switch ( rval )
{
case MIDIERR_NODEVICE :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutOpen Error: no MIDI port was found. " ) ;
2011-07-15 08:43:01 +00:00
break ;
case MMSYSERR_ALLOCATED :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutOpen Error: specified resource is already allocated. " ) ;
2011-07-15 08:43:01 +00:00
break ;
case MMSYSERR_BADDEVICEID :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutOpen Error: specified device identifier is out of range. " ) ;
2011-07-15 08:43:01 +00:00
break ;
case MMSYSERR_INVALPARAM :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutOpen Error: specified pointer or structure is invalid. " ) ;
2011-07-15 08:43:01 +00:00
break ;
case MMSYSERR_NOMEM :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutOpen Error: unable to allocate or lock memory. " ) ;
2011-07-15 08:43:01 +00:00
break ;
default :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutOpen Error code %i " , rval ) ;
2011-07-15 08:43:01 +00:00
break ;
}
return ;
}
2010-11-28 15:49:32 +00:00
2019-05-04 18:27:36 +00:00
range_for ( const int channel , xrange ( 16u ) )
2010-11-28 15:49:32 +00:00
{
2016-07-08 04:14:59 +00:00
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_ALL_SOUNDS_OFF < < 8 | 0 < < 16 ) ) ;
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_RESET_ALL_CONTROLLERS < < 8 | 0 < < 16 ) ) ;
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_ALL_NOTES_OFF < < 8 | 0 < < 16 ) ) ;
2010-11-28 15:49:32 +00:00
channel_volume [ channel ] = 100 ;
2016-07-08 04:14:59 +00:00
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_PANPOT < < 8 | 64 < < 16 ) ) ;
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_REVERB < < 8 | 40 < < 16 ) ) ;
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_CHORUS < < 8 | 0 < < 16 ) ) ;
2010-11-28 15:49:32 +00:00
2016-07-08 04:14:59 +00:00
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_BANK_SELECT_MSB < < 8 | 0 < < 16 ) ) ;
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_BANK_SELECT_LSB < < 8 | 0 < < 16 ) ) ;
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_PROGRAM_CHANGE < < 4 | 0 < < 8 ) ) ;
2010-11-28 15:49:32 +00:00
}
mhdr . lpData = GS_Reset ;
mhdr . dwBufferLength = sizeof ( GS_Reset ) ;
mhdr . dwFlags = 0 ;
2012-04-12 17:45:44 +00:00
if ( ( rval = midiOutPrepareHeader ( hmidi , & mhdr , sizeof ( MIDIHDR ) ) ) = = MMSYSERR_NOERROR )
{
if ( ( rval = midiOutLongMsg ( hmidi , & mhdr , sizeof ( MIDIHDR ) ) ) = = MMSYSERR_NOERROR )
{
fix64 wait_done = timer_query ( ) ;
while ( ! ( mhdr . dwFlags & MHDR_DONE ) )
{
2015-02-05 03:03:48 +00:00
auto timer = timer_update ( ) ;
if ( timer > = wait_done + F1_0 )
2012-04-12 17:45:44 +00:00
{
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " hmp_reset: Timeout waiting for MHDR_DONE " ) ;
2012-04-12 17:45:44 +00:00
break ;
}
}
}
else
{
switch ( rval )
{
case MIDIERR_NOTREADY :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutLongMsg Error: the hardware is busy with other data. " ) ;
2012-04-12 17:45:44 +00:00
break ;
case MIDIERR_UNPREPARED :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutLongMsg Error: the buffer pointed to by lpMidiOutHdr has not been prepared. " ) ;
2012-04-12 17:45:44 +00:00
break ;
case MMSYSERR_INVALHANDLE :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutLongMsg Error: the specified device handle is invalid. " ) ;
2012-04-12 17:45:44 +00:00
break ;
case MMSYSERR_INVALPARAM :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutLongMsg Error: the specified pointer or structure is invalid. " ) ;
2012-04-12 17:45:44 +00:00
break ;
default :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutLongMsg Error code %i " , rval ) ;
2012-04-12 17:45:44 +00:00
break ;
}
}
midiOutUnprepareHeader ( hmidi , & mhdr , sizeof ( MIDIHDR ) ) ;
2010-11-28 15:49:32 +00:00
2012-04-12 17:45:44 +00:00
timer_delay ( F1_0 / 20 ) ;
}
else
{
switch ( rval )
{
case MMSYSERR_INVALHANDLE :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutPrepareHeader Error: The specified device handle is invalid. " ) ;
2012-04-12 17:45:44 +00:00
break ;
case MMSYSERR_INVALPARAM :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutPrepareHeader Error: The specified address is invalid or the given stream buffer is greater than 64K. " ) ;
2012-04-12 17:45:44 +00:00
break ;
case MMSYSERR_NOMEM :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutPrepareHeader Error: The system is unable to allocate or lock memory. " ) ;
2012-04-12 17:45:44 +00:00
break ;
default :
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " midiOutPrepareHeader Error code %i " , rval ) ;
2012-04-12 17:45:44 +00:00
break ;
}
}
2012-04-15 13:52:12 +00:00
2019-05-04 18:27:36 +00:00
range_for ( const int channel , xrange ( 16u ) )
2016-07-08 04:14:59 +00:00
midiOutShortMsg ( hmidi , static_cast < DWORD > ( channel | MIDI_CONTROL_CHANGE < < 4 | MIDI_VOLUME < < 8 | ( 100 * midi_volume / MIDI_VOLUME_SCALE ) < < 16 ) ) ;
2010-11-28 15:49:32 +00:00
midiOutClose ( hmidi ) ;
}
2010-07-18 20:28:15 +00:00
# endif
// CONVERSION FROM HMP TO MIDI
2014-09-20 23:14:03 +00:00
static void hmptrk2mid ( ubyte * data , int size , std : : vector < uint8_t > & midbuf )
2010-07-18 20:28:15 +00:00
{
2016-07-14 01:59:02 +00:00
uint8_t * dptr = data ;
2010-07-18 20:28:15 +00:00
ubyte lc1 = 0 , last_com = 0 ;
2010-10-29 15:40:21 +00:00
uint d ;
2014-09-26 02:42:14 +00:00
int n1 ;
2010-07-18 20:28:15 +00:00
while ( data < dptr + size )
{
if ( data [ 0 ] & 0x80 ) {
ubyte b = ( data [ 0 ] & 0x7F ) ;
2014-09-20 23:14:03 +00:00
midbuf . emplace_back ( b ) ;
2010-07-18 20:28:15 +00:00
}
else {
d = ( data [ 0 ] & 0x7F ) ;
n1 = 0 ;
while ( ( data [ n1 ] & 0x80 ) = = 0 ) {
n1 + + ;
d + = ( data [ n1 ] & 0x7F ) < < ( n1 * 7 ) ;
}
n1 = 1 ;
while ( ( data [ n1 ] & 0x80 ) = = 0 ) {
n1 + + ;
if ( n1 = = 4 )
2014-08-26 03:19:59 +00:00
throw std : : runtime_error ( " bad HMP " ) ;
2010-07-18 20:28:15 +00:00
}
2014-09-26 02:42:14 +00:00
for ( int n2 = 0 ; n2 < = n1 ; n2 + + ) {
2010-07-18 20:28:15 +00:00
ubyte b = ( data [ n1 - n2 ] & 0x7F ) ;
if ( n2 ! = n1 )
b | = 0x80 ;
2014-09-20 23:14:03 +00:00
midbuf . emplace_back ( b ) ;
2010-07-18 20:28:15 +00:00
}
data + = n1 ;
}
data + + ;
if ( * data = = 0xFF ) { //meta?
2014-09-20 23:14:03 +00:00
midbuf . insert ( midbuf . end ( ) , data , data + 3 + data [ 2 ] ) ;
2010-07-18 20:28:15 +00:00
if ( data [ 1 ] = = 0x2F )
break ;
}
else {
lc1 = data [ 0 ] ;
if ( ( lc1 & 0x80 ) = = 0 )
2014-08-26 03:19:59 +00:00
throw std : : runtime_error ( " bad HMP " ) ;
2010-07-18 20:28:15 +00:00
switch ( lc1 & 0xF0 ) {
case 0x80 :
case 0x90 :
case 0xA0 :
case 0xB0 :
case 0xE0 :
if ( lc1 ! = last_com )
2010-10-29 15:40:21 +00:00
{
2014-09-20 23:14:03 +00:00
midbuf . emplace_back ( lc1 ) ;
2010-10-29 15:40:21 +00:00
}
2014-09-20 23:14:03 +00:00
midbuf . insert ( midbuf . end ( ) , data + 1 , data + 3 ) ;
2010-07-18 20:28:15 +00:00
data + = 3 ;
break ;
case 0xC0 :
case 0xD0 :
if ( lc1 ! = last_com )
2010-10-29 15:40:21 +00:00
{
2014-09-20 23:14:03 +00:00
midbuf . emplace_back ( lc1 ) ;
2010-10-29 15:40:21 +00:00
}
2014-09-20 23:14:03 +00:00
midbuf . emplace_back ( data [ 1 ] ) ;
2010-07-18 20:28:15 +00:00
data + = 2 ;
break ;
default :
2014-08-26 03:19:59 +00:00
throw std : : runtime_error ( " bad HMP " ) ;
2010-07-18 20:28:15 +00:00
}
last_com = lc1 ;
}
}
}
2014-09-20 23:08:28 +00:00
struct be_bytebuffer_t : serial : : writer : : bytebuffer_t
{
be_bytebuffer_t ( pointer u ) : bytebuffer_t ( u ) { }
static uint16_t endian ( ) { return big_endian ; }
} ;
2016-08-19 03:41:40 +00:00
const array < uint8_t , 10 > magic_header { {
' M ' , ' T ' , ' h ' , ' d ' ,
0 , 0 , 0 , 6 ,
0 , 1 ,
} } ;
2014-09-20 23:08:28 +00:00
const array < uint8_t , 19 > tempo { { ' M ' , ' T ' , ' r ' , ' k ' , 0 , 0 , 0 , 11 , 0 , 0xFF , 0x51 , 0x03 , 0x18 , 0x80 , 0x00 , 0 , 0xFF , 0x2F , 0 } } ;
2014-09-20 23:14:03 +00:00
const array < uint8_t , 8 > track_header { { ' M ' , ' T ' , ' r ' , ' k ' , 0 , 0 , 0 , 0 } } ;
2014-09-20 23:08:28 +00:00
struct midhdr
{
int16_t num_trks ;
int16_t time_div ;
midhdr ( hmp_file * hmp ) :
num_trks ( hmp - > num_trks ) , time_div ( hmp - > tempo * 1.6 )
{
}
} ;
2016-08-19 03:41:40 +00:00
DEFINE_SERIAL_CONST_UDT_TO_MESSAGE ( midhdr , m , ( magic_header , m . num_trks , m . time_div , tempo ) ) ;
2010-07-18 20:28:15 +00:00
2014-09-20 23:14:03 +00:00
void hmp2mid ( const char * hmp_name , std : : vector < uint8_t > & midbuf )
2010-07-18 20:28:15 +00:00
{
2014-08-05 02:29:43 +00:00
std : : unique_ptr < hmp_file > hmp = hmp_open ( hmp_name ) ;
if ( ! hmp )
2010-07-18 20:28:15 +00:00
return ;
2010-10-29 15:40:21 +00:00
2014-09-20 23:08:28 +00:00
const midhdr mh ( hmp . get ( ) ) ;
2010-07-18 20:28:15 +00:00
// write MIDI-header
2014-09-20 23:14:03 +00:00
midbuf . resize ( serial : : message_type < decltype ( mh ) > : : maximum_size ) ;
be_bytebuffer_t bb ( & midbuf [ 0 ] ) ;
2014-09-20 23:08:28 +00:00
serial : : process_buffer ( bb , mh ) ;
2010-07-18 20:28:15 +00:00
// tracks
2014-09-26 02:42:14 +00:00
for ( int i = 1 ; i < hmp - > num_trks ; i + + )
2010-07-18 20:28:15 +00:00
{
2014-09-20 23:14:03 +00:00
midbuf . insert ( midbuf . end ( ) , track_header . begin ( ) , track_header . end ( ) ) ;
auto size_before = midbuf . size ( ) ;
auto midtrklenpos = midbuf . size ( ) - 4 ;
hmptrk2mid ( hmp - > trks [ i ] . data . get ( ) , hmp - > trks [ i ] . len , midbuf ) ;
auto size_after = midbuf . size ( ) ;
be_bytebuffer_t bbmi ( & midbuf [ midtrklenpos ] ) ;
serial : : process_buffer ( bbmi , static_cast < int32_t > ( size_after - size_before ) ) ;
2010-07-18 20:28:15 +00:00
}
2010-07-19 17:05:41 +00:00
}
2015-12-05 22:57:24 +00:00
}